diff --git a/.gitignore b/.gitignore index 6b4b5309f..54b77b4ef 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ # Executables *.exe +*.lz4 *.out *.app *.i*86 diff --git a/exosphere/src/start.s b/exosphere/src/start.s index 7ac4fe26d..84eb1c2cd 100644 --- a/exosphere/src/start.s +++ b/exosphere/src/start.s @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + /* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */ #define cpuactlr_el1 s3_1_c15_c2_0 #define cpuectlr_el1 s3_1_c15_c2_1 @@ -109,7 +109,7 @@ __start_cold: stp x3, x4, [x0], #0x10 cmp x0, x2 blo 1b - + adr x19, __start_cold adr x20, g_coldboot_crt0_relocation_list sub x20, x20, x19 @@ -125,7 +125,7 @@ _post_cold_crt0_reloc: bl get_coldboot_crt0_stack_address mov sp, x0 mov fp, #0 - + /* Relocate Exosphere image to free DRAM, clearing the image in IRAM. */ ldr x0, =0x80010000 add x20, x20, x0 @@ -147,7 +147,7 @@ _post_cold_crt0_reloc: ldr x1, =0x80010000 /* Set size in coldboot relocation list. */ str x21, [x0, #0x8] - + bl coldboot_init ldr x16, =__jump_to_main_cold diff --git a/exosphere2/Makefile b/exosphere2/Makefile new file mode 100644 index 000000000..9b72ff1ec --- /dev/null +++ b/exosphere2/Makefile @@ -0,0 +1,38 @@ +TARGETS := exosphere.bin program.lz4 +CLEAN_TARGETS := exosphere-clean program-clean boot_code-clean + +SUBFOLDERS := $(MODULES) + +all: exosphere.bin + +clean: $(CLEAN_TARGETS) + @rm -f exosphere.bin + +exosphere.bin: program.lz4 boot_code.lz4 + $(MAKE) -C loader_stub + @cp loader_stub/loader_stub.bin exosphere.bin + @echo "Built exosphere.bin..." + +program.lz4: check_libexo + $(MAKE) -C program + @cp program/program.lz4 program.lz4 + @cp program/boot_code.lz4 boot_code.lz4 + +boot_code.lz4: program.lz4 + +check_libexo: + @$(MAKE) --no-print-directory -C ../libraries/libexosphere + +exosphere-clean: + $(MAKE) -C loader_stub clean + @rm -f exosphere.bin + +program-clean: + $(MAKE) -C program clean + @rm -f program.lz4 + +boot_code-clean: + $(MAKE) -C boot_code clean + @rm -f boot_code.lz4 + +.PHONY: all clean $(CLEAN_TARGETS) diff --git a/exosphere2/loader_stub/Makefile b/exosphere2/loader_stub/Makefile new file mode 100644 index 000000000..a5725e57e --- /dev/null +++ b/exosphere2/loader_stub/Makefile @@ -0,0 +1,130 @@ +#--------------------------------------------------------------------------------- +# Define the atmosphere board and cpu +#--------------------------------------------------------------------------------- +export ATMOSPHERE_BOARD := nx-hac-001 +export ATMOSPHERE_CPU := arm-cortex-a57 + +#--------------------------------------------------------------------------------- +# pull in common atmosphere configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/exosphere.mk + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ + $(TOPDIR)/../program + +CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ + $(notdir $(wildcard $(dir)/*.c)))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) + +CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ + $(notdir $(wildcard $(dir)/*.cpp)))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) + +SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ + $(notdir $(wildcard $(dir)/*.s)))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) + +BINFILES := program.lz4 boot_code.lz4 + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES)))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I. + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) + +.PHONY: $(BUILD) clean all check_program + +#--------------------------------------------------------------------------------- +all: $(BUILD) check_program + +$(BUILD): check_program + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +check_program: + @$(MAKE) -C ../program all + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).bin + +$(OUTPUT).bin : $(OUTPUT).elf + $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@ + @echo built ... $(notdir $@) + +$(OUTPUT).elf : $(OFILES) ../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a + +%.elf: + @echo linking $(notdir $@) + $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + @$(NM) -CSn $@ > $(notdir $*.lst) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h: %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +%.lz4.o %_lz4.h: %.lz4 +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/exosphere2/loader_stub/loader_stub.ld b/exosphere2/loader_stub/loader_stub.ld new file mode 100644 index 000000000..bb8239d81 --- /dev/null +++ b/exosphere2/loader_stub/loader_stub.ld @@ -0,0 +1,182 @@ +OUTPUT_ARCH(aarch64) +ENTRY(_start) + +MEMORY +{ + NULL : ORIGIN = 0, LENGTH = 4K + ldr_stub : ORIGIN = 0x040030000, LENGTH = 128K +} + +SECTIONS +{ + /* =========== CODE section =========== */ + PROVIDE(__start__ = 0x040030000); + . = __start__; + __code_start = . ; + + .crt0 : + { + KEEP (*(.crt0 .crt0.*)) + . = ALIGN(8); + } >ldr_stub + + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + . = ALIGN(8); + } >ldr_stub + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } >ldr_stub + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } >ldr_stub + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } >ldr_stub + + + /* =========== RODATA section =========== */ + . = ALIGN(8); + __rodata_start = . ; + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } >ldr_stub + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >ldr_stub + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ldr_stub + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >ldr_stub + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >ldr_stub + + .hash : { *(.hash) } >ldr_stub + + /* =========== DATA section =========== */ + . = ALIGN(8); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >ldr_stub + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >ldr_stub + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >ldr_stub + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >ldr_stub + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } >ldr_stub + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } >ldr_stub + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } >ldr_stub + + .ctors ALIGN(8) : + { + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >ldr_stub + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >ldr_stub + + __got_start__ = .; + + .got : { *(.got) *(.igot) } >ldr_stub + .got.plt : { *(.got.plt) *(.igot.plt) } >ldr_stub + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >ldr_stub + + __bss_start__ = .; + .bss ALIGN(8) : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + } >ldr_stub + __bss_end__ = .; + + __end__ = ABSOLUTE(.) ; + + /* ================== + ==== Metadata ==== + ================== */ + + /* Discard sections that difficult post-processing */ + /DISCARD/ : { *(.group .comment .note .interp) } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } +} \ No newline at end of file diff --git a/exosphere2/loader_stub/loader_stub.specs b/exosphere2/loader_stub/loader_stub.specs new file mode 100644 index 000000000..03984762f --- /dev/null +++ b/exosphere2/loader_stub/loader_stub.specs @@ -0,0 +1,7 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /loader_stub.ld) --gc-sections --nmagic -nostdlib -nostartfiles + +*startfile: +crti%O%s crtbegin%O%s diff --git a/exosphere2/loader_stub/source/secmon_loader_error.cpp b/exosphere2/loader_stub/source/secmon_loader_error.cpp new file mode 100644 index 000000000..2f67b507f --- /dev/null +++ b/exosphere2/loader_stub/source/secmon_loader_error.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_loader_error.hpp" + +namespace ams::diag { + + NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) { + ams::secmon::loader::ErrorReboot(); + } + + NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) { + ams::secmon::loader::ErrorReboot(); + } + + NORETURN void AbortImpl() { + ams::secmon::loader::ErrorReboot(); + } + +} + +namespace ams::secmon::loader { + + NORETURN void ErrorReboot() { + /* Invalidate the security engine. */ + /* TODO */ + + /* Reboot. */ + while (true) { + wdt::Reboot(); + } + } + +} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_error.hpp b/exosphere2/loader_stub/source/secmon_loader_error.hpp new file mode 100644 index 000000000..611990740 --- /dev/null +++ b/exosphere2/loader_stub/source/secmon_loader_error.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#pragma once + +namespace ams::secmon::loader { + + NORETURN void ErrorReboot(); + +} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_main.cpp b/exosphere2/loader_stub/source/secmon_loader_main.cpp new file mode 100644 index 000000000..70dc8555a --- /dev/null +++ b/exosphere2/loader_stub/source/secmon_loader_main.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_loader_uncompress.hpp" +#include "program_lz4.h" +#include "boot_code_lz4.h" + +namespace ams::secmon::loader { + + NORETURN void UncompressAndExecute() { + /* Uncompress the program image. */ + Uncompress(secmon::MemoryRegionPhysicalTzramFullProgramImage.GetPointer(), secmon::MemoryRegionPhysicalTzramFullProgramImage.GetSize(), program_lz4, program_lz4_size); + + /* Copy the boot image to the end of IRAM */ + u8 *relocated_boot_code = secmon::MemoryRegionPhysicalIramBootCodeImage.GetEndPointer() - boot_code_lz4_size; + std::memcpy(relocated_boot_code, boot_code_lz4, boot_code_lz4_size); + + /* Uncompress the boot image. */ + Uncompress(secmon::MemoryRegionPhysicalIramBootCodeImage.GetPointer(), secmon::MemoryRegionPhysicalIramBootCodeImage.GetSize(), relocated_boot_code, boot_code_lz4_size); + + /* Jump to the boot image. */ + reinterpret_cast(secmon::MemoryRegionPhysicalIramBootCodeImage.GetAddress())(); + + /* We will never reach this point. */ + __builtin_unreachable(); + } + +} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp b/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp new file mode 100644 index 000000000..a58e3c5a1 --- /dev/null +++ b/exosphere2/loader_stub/source/secmon_loader_uncompress.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_loader_uncompress.hpp" + +namespace ams::secmon::loader { + + namespace { + + class Lz4Uncompressor { + private: + const u8 *src; + size_t src_size; + size_t src_offset; + u8 *dst; + size_t dst_size; + size_t dst_offset; + public: + Lz4Uncompressor(void *dst, size_t dst_size, const void *src, size_t src_size) : src(static_cast(src)), src_size(src_size), src_offset(0), dst(static_cast(dst)), dst_size(dst_size), dst_offset(0) { + /* ... */ + } + + void Uncompress() { + while (true) { + /* Read a control byte. */ + u8 control = this->ReadByte(); + + /* Copy what it specifies we should copy. */ + this->Copy(this->GetCopySize(control >> 4)); + + /* If we've exceeded size, we're done. */ + if (this->src_offset >= this->src_size) { + break; + } + + /* Read the wide copy offset. */ + u16 wide_offset = this->ReadByte(); + AMS_ABORT_UNLESS(this->CanRead()); + wide_offset |= (this->ReadByte() << 8); + + /* Determine the copy size. */ + const size_t wide_copy_size = this->GetCopySize(control & 0xF); + + /* Copy bytes. */ + const size_t end_offset = this->dst_offset + wide_copy_size + 4; + for (size_t cur_offset = this->dst_offset; cur_offset < end_offset; this->dst_offset = (++cur_offset)) { + AMS_ABORT_UNLESS(wide_offset <= cur_offset); + + this->dst[cur_offset] = this->dst[cur_offset - wide_offset]; + } + } + } + private: + u8 ReadByte() { + return this->src[this->src_offset++]; + } + + bool CanRead() const { + return this->src_offset < this->src_size; + } + + size_t GetCopySize(u8 control) { + size_t size = control; + + if (control >= 0xF) { + do { + AMS_ABORT_UNLESS(this->CanRead()); + control = this->ReadByte(); + size += control; + } while (control == 0xFF); + } + + return size; + } + + void Copy(size_t size) { + __builtin_memcpy(this->dst + this->dst_offset, this->src + this->src_offset, size); + this->dst_offset += size; + this->src_offset += size; + } + }; + + } + + void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Create an execute a decompressor. */ + Lz4Uncompressor(dst, dst_size, src, src_size).Uncompress(); + } + +} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp b/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp new file mode 100644 index 000000000..b820209d5 --- /dev/null +++ b/exosphere2/loader_stub/source/secmon_loader_uncompress.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#pragma once + +namespace ams::secmon::loader { + + void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size); + +} \ No newline at end of file diff --git a/exosphere2/loader_stub/source/start.s b/exosphere2/loader_stub/source/start.s new file mode 100644 index 000000000..2f432aaa7 --- /dev/null +++ b/exosphere2/loader_stub/source/start.s @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */ +#define cpuactlr_el1 s3_1_c15_c2_0 +#define cpuectlr_el1 s3_1_c15_c2_1 + +.macro RESET_CORE + mov x0, #(1 << 63) + msr cpuactlr_el1, x0 /* disable regional clock gating */ + isb + mov x0, #3 + msr rmr_el3, x0 + isb + dsb sy + /* Nintendo forgot to copy-paste the branch instruction below. */ + 1: + wfi + b 1b +.endm + +.macro ERRATUM_INVALIDATE_BTB_AT_BOOT +/* Nintendo copy-pasted https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/nvidia/tegra/common/aarch64/tegra_helpers.S#L312 */ + /* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + /* The following comments are mine. */ + + /* + Enable invalidates of branch target buffer, then flush + the entire instruction cache at the local level, and + with the reg change, the branch target buffer, then disable + invalidates of the branch target buffer again. + */ + mrs x0, cpuactlr_el1 + orr x0, x0, #1 + msr cpuactlr_el1, x0 + + dsb sy + isb + ic iallu + dsb sy + isb + + mrs x0, cpuactlr_el1 + bic x0, x0, #1 + msr cpuactlr_el1, x0 + +.rept 7 + nop /* wait long enough for the write to cpuactlr_el1 to have completed */ +.endr + + /* if the OS lock is set, disable it and request a warm reset */ + mrs x0, oslsr_el1 + ands x0, x0, #2 + b.eq 2f + mov x0, xzr + msr oslar_el1, x0 + + RESET_CORE + +.rept 65 + nop /* guard against speculative excecution */ +.endr + + 2: + /* set the OS lock */ + mov x0, #1 + msr oslar_el1, x0 +.endm + +.section .crt0.text.start, "ax", %progbits +.align 6 +.global _start +_start: + /* mask all interrupts */ + msr daifset, #0xF + + /* Fixup hardware erratum */ + ERRATUM_INVALIDATE_BTB_AT_BOOT + + /* Set the stack pointer to a temporary location. */ + ldr x20, =0x7C010800 + mov sp, x20 + + /* Call our init array functions. */ + bl __libc_init_array + + /* Uncompress the program and iram boot code images. */ + b _ZN3ams6secmon6loader20UncompressAndExecuteEv diff --git a/exosphere2/program/Makefile b/exosphere2/program/Makefile new file mode 100644 index 000000000..078b88fd9 --- /dev/null +++ b/exosphere2/program/Makefile @@ -0,0 +1,128 @@ +#--------------------------------------------------------------------------------- +# 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)) + +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 arm64 + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).lz4 + +$(OUTPUT).lz4 : $(OUTPUT).bin + @python ../split_program.py $(OUTPUT).bin $(dir $(OUTPUT)) + @echo built ... $(notdir $@) + +$(OUTPUT).bin : $(OUTPUT).elf + $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@ + @echo built ... $(notdir $@) + +$(OUTPUT).elf : $(OFILES) ../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a + +secmon_crt0_cpp.o secmon_make_page_table.o : CFLAGS += -fno-builtin + +%.elf: + @echo linking $(notdir $@) + $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + @$(NM) -CSn $@ > $(notdir $*.lst) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h: %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/exosphere2/program/program.ld b/exosphere2/program/program.ld new file mode 100644 index 000000000..61e8720dc --- /dev/null +++ b/exosphere2/program/program.ld @@ -0,0 +1,277 @@ +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_code : ORIGIN = 0x1F01C0800, LENGTH = 6K + + 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_boot_cache.o(.text*)) + KEEP (secmon_make_page_table.o(.text*)) + *(.crt0.rodata*) + secmon_crt0_cpp.o(.rodata*) + secmon_boot_cache.o(.rodata*) + secmon_make_page_table.o(.rodata*) + *(.crt0.data*) + secmon_crt0_cpp.o(.data*) + secmon_boot_cache.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_code : + { + KEEP(secmon_main.o(.text*)) + KEEP(secmon_boot_functions.o(.text*)) + KEEP(secmon_boot_config.o(.text*)) + KEEP(secmon_boot_setup.o(.text*)) + KEEP(secmon_package2.o(.text*)) + KEEP(secmon_key_data.o(.text*)) + secmon_main.o(.rodata*) + secmon_boot_functions.o(.rodata*) + secmon_boot_config.o(.rodata*) + secmon_boot_setup.o(.rodata*) + secmon_package2.o(.rodata*) + secmon_key_data.o(.rodata*) + secmon_main.o(.data*) + secmon_boot_functions.o(.data*) + secmon_boot_config.o(.data*) + secmon_boot_setup.o(.data*) + secmon_package2.o(.data*) + secmon_key_data.o(.data*) + . = ALIGN(8); + } >tzram_boot_code AT>glob + + .tzram_boot_code.bss : + { + __boot_bss_start__ = ABSOLUTE(.); + secmon_main.o(.bss* COMMON) + secmon_boot_functions.o(.bss* COMMON) + secmon_boot_config.o(.bss* COMMON) + secmon_boot_setup.o(.bss* COMMON) + secmon_package2.o(.bss* COMMON) + secmon_key_data.o(.bss* COMMON) + __boot_bss_end__ = ABSOLUTE(.); + } >tzram_boot_code AT>glob + + .tzram_boot_code.fill : + { + FILL(0x00000000); + . = ORIGIN(tzram_boot_code) + LENGTH(tzram_boot_code) - 1; + BYTE(0x00); + } > tzram_boot_code 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 : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + . = ALIGN(8); + } >main AT>glob + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } >main AT>glob + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } >main AT>glob + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } >main AT>glob + + + /* =========== RODATA section =========== */ + . = ALIGN(8); + __rodata_start = . ; + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } >main AT>glob + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >main AT>glob + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >main AT>glob + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >main AT>glob + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >main AT>glob + + .hash : { *(.hash) } >main AT>glob + + /* =========== DATA section =========== */ + . = ALIGN(8); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >main AT>glob + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >main AT>glob + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >main AT>glob + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >main AT>glob + + __got_start__ = .; + + .got : { *(.got) *(.igot) } >main AT>glob + .got.plt : { *(.got.plt) *(.igot.plt) } >main AT>glob + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >main AT>glob + + .bss ALIGN(8) (NOLOAD) : + { + __bss_start__ = ABSOLUTE(.); + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + __bss_end__ = ABSOLUTE(.); + } >main AT>glob + + __program_end__ = ABSOLUTE(.); + + /* ================== + ==== Metadata ==== + ================== */ + + /* Discard sections that difficult post-processing */ + /DISCARD/ : { *(.group .comment .note .interp) } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } +} \ No newline at end of file diff --git a/exosphere2/program/program.specs b/exosphere2/program/program.specs new file mode 100644 index 000000000..22b789960 --- /dev/null +++ b/exosphere2/program/program.specs @@ -0,0 +1,4 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic -nostdlib -nostartfiles \ No newline at end of file diff --git a/exosphere2/program/sc7fw/Makefile b/exosphere2/program/sc7fw/Makefile new file mode 100644 index 000000000..31439e834 --- /dev/null +++ b/exosphere2/program/sc7fw/Makefile @@ -0,0 +1,122 @@ +#--------------------------------------------------------------------------------- +# Define the atmosphere board and cpu +#--------------------------------------------------------------------------------- +export ATMOSPHERE_BOARD := nx-hac-001 +export ATMOSPHERE_CPU := arm7tdmi + +#--------------------------------------------------------------------------------- +# pull in common atmosphere configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../../libraries/config/templates/exosphere.mk + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ + $(notdir $(wildcard $(dir)/*.c)))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) +CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) + +CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ + $(notdir $(wildcard $(dir)/*.cpp)))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) +CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) + +SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ + $(notdir $(wildcard $(dir)/*.s)))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) +SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES)))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I. + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) check_libexo + +$(BUILD): check_libexo + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +check_libexo: + @$(MAKE) --no-print-directory -C ../../../libraries/libexosphere arm + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).bin + +$(OUTPUT).bin : $(OUTPUT).elf + $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@ + @echo built ... $(notdir $@) + +$(OUTPUT).elf : $(OFILES) ../../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a + +%.elf: + @echo linking $(notdir $@) + $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + @$(NM) -CSn $@ > $(notdir $*.lst) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h: %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/exosphere2/program/sc7fw/sc7fw.ld b/exosphere2/program/sc7fw/sc7fw.ld new file mode 100644 index 000000000..e8a097dbb --- /dev/null +++ b/exosphere2/program/sc7fw/sc7fw.ld @@ -0,0 +1,183 @@ +OUTPUT_ARCH(arm) +ENTRY(_start) + +MEMORY +{ + NULL : ORIGIN = 0, LENGTH = 4K + sc7fw : ORIGIN = 0x40003000, LENGTH = 4K +} + + +SECTIONS +{ + /* =========== CODE section =========== */ + PROVIDE(__start__ = ORIGIN(sc7fw)); + . = __start__; + __code_start = . ; + + .vectors : + { + KEEP (*(.vectors .vectors.*)) + . = ALIGN(8); + } >sc7fw + + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + . = ALIGN(8); + } >sc7fw + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } >sc7fw + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } >sc7fw + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } >sc7fw + + + /* =========== RODATA section =========== */ + . = ALIGN(8); + __rodata_start = . ; + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } >sc7fw + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >sc7fw + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >sc7fw + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >sc7fw + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >sc7fw + + .hash : { *(.hash) } >sc7fw + + /* =========== DATA section =========== */ + . = ALIGN(8); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >sc7fw + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >sc7fw + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >sc7fw + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >sc7fw + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } >sc7fw + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } >sc7fw + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } >sc7fw + + .ctors ALIGN(8) : + { + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >sc7fw + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >sc7fw + + __got_start__ = .; + + .got : { *(.got) *(.igot) } >sc7fw + .got.plt : { *(.got.plt) *(.igot.plt) } >sc7fw + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >sc7fw + + __bss_start__ = .; + .bss ALIGN(8) : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + } >sc7fw + __bss_end__ = .; + + __end__ = ABSOLUTE(.) ; + + /* ================== + ==== Metadata ==== + ================== */ + + /* Discard sections that difficult post-processing */ + /DISCARD/ : { *(.group .comment .note .interp) } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } +} \ No newline at end of file diff --git a/exosphere2/program/sc7fw/sc7fw.specs b/exosphere2/program/sc7fw/sc7fw.specs new file mode 100644 index 000000000..13455dc90 --- /dev/null +++ b/exosphere2/program/sc7fw/sc7fw.specs @@ -0,0 +1,7 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /sc7fw.ld) --gc-sections --nmagic -nostdlib -nostartfiles + +*startfile: +crti%O%s crtbegin%O%s diff --git a/exosphere2/program/sc7fw/source/sc7fw_dram.cpp b/exosphere2/program/sc7fw/source/sc7fw_dram.cpp new file mode 100644 index 000000000..dcdf9c94b --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_dram.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "sc7fw_util.hpp" +#include "sc7fw_dram.hpp" + +namespace ams::sc7fw { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + + void UpdateEmcTiming() { + /* Enable timing update. */ + reg::Write(EMC_ADDRESS(EMC_TIMING_CONTROL), EMC_REG_BITS_ENUM(TIMING_CONTROL_TIMING_UPDATE, ENABLED)); + + /* Wait for the timing update to complete. */ + while (!reg::HasValue(EMC_ADDRESS(EMC_EMC_STATUS), EMC_REG_BITS_ENUM(EMC_STATUS_TIMING_UPDATE_STALLED, DONE))) { + /* ... */ + } + } + + void RequestAllPadsPowerDown(uintptr_t addr, uintptr_t expected) { + constexpr u32 DpdAllRequestValue = reg::Encode(PMC_REG_BITS_ENUM(IO_DPD_REQ_CODE, DPD_ON)) | 0x0FFFFFFF; + const auto RequestAddress = addr; + const auto StatusAddress = addr + 4; + + /* Request all pads enter power down. */ + reg::Write(PMC + RequestAddress, DpdAllRequestValue); + + /* Wait until the status reflects our expectation (and all pads are shut down). */ + while (reg::Read(PMC + StatusAddress) != expected) { /* ... */ } + + /* Wait a little while to allow the power down status to propagate. */ + SpinLoop(0x20); + }; + + } + + void SaveEmcFsp() { + /* We require that the RAM is LPDDR4. */ + AMS_ABORT_UNLESS(reg::HasValue(EMC_ADDRESS(EMC_FBIO_CFG5), EMC_REG_BITS_ENUM(FBIO_CFG5_DRAM_TYPE, LPDDR4))); + + /* Read the frequency set points from MRW3. */ + constexpr u32 FspShift = 6; + constexpr u32 FspBits = 2; + constexpr u32 FspMask = ((1u << FspBits) - 1) << FspShift; + static_assert(FspMask == 0x000000C0); + const u32 fsp = (reg::Read(EMC_ADDRESS(EMC_MRW3)) & FspMask) >> FspShift; + + /* Write the fsp to PMC_SCRATCH18, where it will be restored to MRW3 by brom. */ + reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH18, REG_BITS_VALUE(FspShift, FspBits, fsp)); + + /* Write the fsp twice to PMC_SCRATCH12, where it will be restored to MRW12 by brom. */ + reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH12, REG_BITS_VALUE(FspShift, FspBits, fsp), REG_BITS_VALUE(FspShift + 8, FspBits, fsp)); + + /* Write the fsp twice to PMC_SCRATCH13, where it will be restored to MRW13 by brom. */ + reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH13, REG_BITS_VALUE(FspShift, FspBits, fsp), REG_BITS_VALUE(FspShift + 8, FspBits, fsp)); + } + + void EnableSdramSelfRefresh() { + /* We require that the RAM is dual-channel. */ + AMS_ABORT_UNLESS(reg::HasValue(EMC_ADDRESS(EMC_FBIO_CFG7), EMC_REG_BITS_ENUM(FBIO_CFG7_CH1_ENABLE, ENABLE))); + + /* Disable RAM's ability to dynamically self-refresh, and to opportunistically perform powerdown. */ + reg::Write(EMC_ADDRESS(EMC_CFG), EMC_REG_BITS_ENUM(CFG_DYN_SELF_REF, DISABLED), + EMC_REG_BITS_ENUM(CFG_DRAM_ACPD, NO_POWERDOWN)); + + /* Update the EMC timing. */ + UpdateEmcTiming(); + + /* Wait five microseconds. */ + util::WaitMicroSeconds(5); + + /* Disable ZQ calibration. */ + reg::Write(EMC_ADDRESS(EMC_ZCAL_INTERVAL), 0); + + /* Disable automatic calibration. */ + reg::Write(EMC_ADDRESS(EMC_AUTO_CAL_CONFIG), EMC_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL, ENABLE), + EMC_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL, ENABLE), + EMC_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, DISABLE)); + + /* Get whether digital delay locked loops are enabled. */ + const bool has_dll = reg::HasValue(EMC_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, ENABLED)); + if (has_dll) { + /* If they are, disable them. */ + reg::ReadWrite(EMC_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, DISABLED)); + } + + /* Update the EMC timing. */ + UpdateEmcTiming(); + + /* If dll was enabled, wait until both EMC0 and EMC1 have dll disabled. */ + if (has_dll) { + while (!reg::HasValue(EMC0_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, DISABLED))) { /* ... */ } + while (!reg::HasValue(EMC1_ADDRESS(EMC_CFG_DIG_DLL), EMC_REG_BITS_ENUM(CFG_DIG_DLL_CFG_DLL_EN, DISABLED))) { /* ... */ } + } + + /* Stall all reads and writes. */ + reg::Write(EMC_ADDRESS(EMC_REQ_CTRL), EMC_REG_BITS_VALUE(REQ_CTRL_STALL_ALL_READS, 1), + EMC_REG_BITS_VALUE(REQ_CTRL_STALL_ALL_WRITES, 1)); + + /* Wait until both EMC0 and EMC1 have no outstanding transactions. */ + while (!reg::HasValue(EMC0_ADDRESS(EMC_EMC_STATUS), EMC_REG_BITS_ENUM(EMC_STATUS_NO_OUTSTANDING_TRANSACTIONS, COMPLETED))) { /* ... */ } + while (!reg::HasValue(EMC1_ADDRESS(EMC_EMC_STATUS), EMC_REG_BITS_ENUM(EMC_STATUS_NO_OUTSTANDING_TRANSACTIONS, COMPLETED))) { /* ... */ } + + /* Enable self-refresh. */ + reg::Write(EMC_ADDRESS(EMC_SELF_REF), EMC_REG_BITS_ENUM(SELF_REF_SREF_DEV_SELECTN, BOTH), + EMC_REG_BITS_ENUM(SELF_REF_SELF_REF_CMD, ENABLED)); + + /* Wait until both EMC and EMC1 are in self-refresh. */ + const auto desired = reg::HasValue(EMC_ADDRESS(EMC_ADR_CFG), EMC_REG_BITS_ENUM(ADR_CFG_EMEM_NUMDEV, N2)) ? EMC_REG_BITS_ENUM(EMC_STATUS_DRAM_IN_SELF_REFRESH, BOTH_ENABLED) + : EMC_REG_BITS_ENUM(EMC_STATUS_DRAM_DEV0_IN_SELF_REFRESH, ENABLED); + + /* NOTE: Nintendo's sc7 entry firmware has a bug here. */ + /* Instead of waiting for both EMCs to report self-refresh, they just read the EMC_STATUS for each EMC. */ + /* This is incorrect, per documentation. */ + while (!reg::HasValue(EMC0_ADDRESS(EMC_EMC_STATUS), desired)) { /* ... */ } + while (!reg::HasValue(EMC1_ADDRESS(EMC_EMC_STATUS), desired)) { /* ... */ } + } + + void EnableEmcAllSegmentsRefresh() { + constexpr int MR17_PASR_Segment = 17; + + /* Write zeros to MR17_PASR_Segment to enable refresh for all segments for dev0. */ + reg::Write(EMC_ADDRESS(EMC_MRW), EMC_REG_BITS_ENUM (MRW_DEV_SELECTN, DEV0), + EMC_REG_BITS_ENUM (MRW_CNT, EXT1), + EMC_REG_BITS_VALUE(MRW_MA, MR17_PASR_Segment), + EMC_REG_BITS_VALUE(MRW_OP, 0)); + + /* If dev1 exists, do the same for dev1. */ + if (reg::HasValue(EMC_ADDRESS(EMC_ADR_CFG), EMC_REG_BITS_ENUM(ADR_CFG_EMEM_NUMDEV, N2))) { + reg::Write(EMC_ADDRESS(EMC_MRW), EMC_REG_BITS_ENUM (MRW_DEV_SELECTN, DEV1), + EMC_REG_BITS_ENUM (MRW_CNT, EXT1), + EMC_REG_BITS_VALUE(MRW_MA, MR17_PASR_Segment), + EMC_REG_BITS_VALUE(MRW_OP, 0)); + } + } + + void EnableDdrDeepPowerDown() { + /* Read and decode the parameters Nintendo stores in EMC_PMC_SCRATCH3. */ + const u32 scratch3 = reg::Read(EMC_ADDRESS(EMC_PMC_SCRATCH3)); + const bool weak_bias = (scratch3 & reg::EncodeMask(EMC_REG_BITS_MASK(PMC_SCRATCH3_WEAK_BIAS))) == reg::EncodeValue(EMC_REG_BITS_ENUM(PMC_SCRATCH3_WEAK_BIAS, ENABLED)); + const u32 ddr_cntrl = (scratch3 & reg::EncodeMask(EMC_REG_BITS_MASK(PMC_SCRATCH3_DDR_CNTRL))); + + /* Write the decoded value to PMC_DDR_CNTRL. */ + reg::Write(PMC + APBDEV_PMC_DDR_CNTRL, ddr_cntrl); + + /* If weak bias is enabled, set all VTT_E_WB bits in APBDEV_PMC_WEAK_BIAS. */ + if (weak_bias) { + constexpr u32 WeakBiasVttEWbAll = 0x7FFF0000; + reg::Write(PMC + APBDEV_PMC_WEAK_BIAS, WeakBiasVttEWbAll); + } + + /* Request that DPD3 pads power down. */ + constexpr u32 EristaDpd3Mask = 0x0FFFFFFF; + constexpr u32 MarikoDpd3Mask = 0x0FFF9FFF; + if (true /* TODO: IsErista */) { + RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD3_REQ, EristaDpd3Mask); + } else { + RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD3_REQ, MarikoDpd3Mask); + } + + /* Request that DPD4 pads power down. */ + constexpr u32 Dpd4Mask = 0x0FFF1FFF; + RequestAllPadsPowerDown(APBDEV_PMC_IO_DPD4_REQ, Dpd4Mask); + } + +} diff --git a/exosphere2/program/sc7fw/source/sc7fw_dram.hpp b/exosphere2/program/sc7fw/source/sc7fw_dram.hpp new file mode 100644 index 000000000..dca0a47b0 --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_dram.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::sc7fw { + + void SaveEmcFsp(); + void EnableSdramSelfRefresh(); + void EnableEmcAllSegmentsRefresh(); + void EnableDdrDeepPowerDown(); + +} \ No newline at end of file diff --git a/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s b/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s new file mode 100644 index 000000000..ca8c3b787 --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_exception_vectors.s @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.section .vectors, "ax", %progbits +.align 3 +.global reset +reset: + b _start + b _ZN3ams5sc7fw16ExceptionHandlerEv diff --git a/exosphere2/program/sc7fw/source/sc7fw_main.cpp b/exosphere2/program/sc7fw/source/sc7fw_main.cpp new file mode 100644 index 000000000..0a082586a --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_main.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "sc7fw_util.hpp" +#include "sc7fw_dram.hpp" + +namespace ams::sc7fw { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + + void DisableCrail() { + /* Wait for CRAIL to be off. */ + while (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_ENUM(PWRGATE_STATUS_CRAIL, OFF))) { /* ... */ } + + /* Set CRAIL to be clamped. */ + reg::ReadWrite(PMC + APBDEV_PMC_SET_SW_CLAMP, PMC_REG_BITS_VALUE(SET_SW_CLAMP_CRAIL, 1)); + + /* Wait for CRAIL to be clamped. */ + while (!reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, PMC_REG_BITS_ENUM(CLAMP_STATUS_CRAIL, ENABLE))) { /* ... */ } + + /* Spin loop for a short while, to allow time for the clamp to take effect. */ + sc7fw::SpinLoop(10); + + /* Initialize i2c-5. */ + i2c::Initialize(i2c::Port_5); + + /* Disable the voltage to CPU. */ + pmic::DisableVddCpu(fuse::GetRegulator()); + + /* Wait 700 microseconds to ensure voltage is disabled. */ + util::WaitMicroSeconds(700); + } + + void DisableAllInterrupts() { + /* Disable all interrupts for bpmp in all interrupt controllers. */ + reg::Write(PRI_ICTLR(ICTLR_COP_IER_CLR), ~0u); + reg::Write(SEC_ICTLR(ICTLR_COP_IER_CLR), ~0u); + reg::Write(TRI_ICTLR(ICTLR_COP_IER_CLR), ~0u); + reg::Write(QUAD_ICTLR(ICTLR_COP_IER_CLR), ~0u); + reg::Write(PENTA_ICTLR(ICTLR_COP_IER_CLR), ~0u); + reg::Write(HEXA_ICTLR(ICTLR_COP_IER_CLR), ~0u); + } + + void EnterSc7() { + /* Disable read buffering and write buffering in the BPMP cache. */ + reg::ReadWrite(AVP_CACHE_ADDRESS(AVP_CACHE_CONFIG), AVP_CACHE_REG_BITS_ENUM(DISABLE_WB, TRUE), + AVP_CACHE_REG_BITS_ENUM(DISABLE_RB, TRUE)); + + /* Ensure the CPU Rail is turned off. */ + DisableCrail(); + + /* Disable all interrupts. */ + DisableAllInterrupts(); + + /* Save the EMC FSP */ + SaveEmcFsp(); + + /* Enable self-refresh for DRAM */ + EnableSdramSelfRefresh(); + + /* Enable refresh for all EMC devices. */ + EnableEmcAllSegmentsRefresh(); + + /* Enable deep power-down for ddr. */ + EnableDdrDeepPowerDown(); + + /* Enable pad sampling during deep sleep. */ + reg::ReadWrite(PMC + APBDEV_PMC_DPD_SAMPLE, PMC_REG_BITS_ENUM(DPD_SAMPLE_ON, ENABLE)); + reg::Read(PMC + APBDEV_PMC_DPD_SAMPLE); + + /* Wait a while for pad sampling to be enabled. */ + sc7fw::SpinLoop(0x128); + + /* Enter deep sleep. */ + reg::ReadWrite(PMC + APBDEV_PMC_DPD_ENABLE, PMC_REG_BITS_ENUM(DPD_ENABLE_ON, ENABLE)); + + /* Wait forever until we're asleep. */ + while (true) { /* ... */ } + } + + } + + void Main() { + EnterSc7(); + } + + NORETURN void ExceptionHandler() { + /* Write enable to MAIN_RESET. */ + reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE)); + while (true) { /* ... */ } + } + +} + +namespace ams::diag { + + void AbortImpl() { + sc7fw::ExceptionHandler(); + } + +} diff --git a/exosphere2/program/sc7fw/source/sc7fw_start.s b/exosphere2/program/sc7fw/source/sc7fw_start.s new file mode 100644 index 000000000..02761c112 --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_start.s @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.section .text.start, "ax", %progbits +.align 3 +.global _start +_start: + /* Set CPSR_cf and CPSR_cf. */ + msr cpsr_f, #0xC0 + msr cpsr_cf, #0xD3 + + /* Set the stack pointer. */ + ldr sp, =0x40005000 + + /* Set our link register to the exception handler. */ + ldr lr, =_ZN3ams5sc7fw16ExceptionHandlerEv + + /* Invoke main. */ + bl _ZN3ams5sc7fw4MainEv + + /* Infinite loop. */ + 1: b 1b \ No newline at end of file diff --git a/exosphere2/program/sc7fw/source/sc7fw_util.hpp b/exosphere2/program/sc7fw/source/sc7fw_util.hpp new file mode 100644 index 000000000..2202deb0f --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_util.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::sc7fw { + + void SpinLoop(unsigned int num); + +} \ No newline at end of file diff --git a/exosphere2/program/sc7fw/source/sc7fw_util_asm.s b/exosphere2/program/sc7fw/source/sc7fw_util_asm.s new file mode 100644 index 000000000..249c5b0f9 --- /dev/null +++ b/exosphere2/program/sc7fw/source/sc7fw_util_asm.s @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.section .text._ZN3ams5sc7fw8SpinLoopEj, "ax", %progbits +.global _ZN3ams5sc7fw8SpinLoopEj +.thumb_func +.syntax unified +_ZN3ams5sc7fw8SpinLoopEj: + 1: + /* Subtract one from the count. */ + subs r0, r0, #1 + + /* If we aren't at zero, continue looping. */ + bgt 1b + + /* Return. */ + bx lr \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot.hpp b/exosphere2/program/source/boot/secmon_boot.hpp new file mode 100644 index 000000000..dca6d0091 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon::boot { + + void MakePageTable(); + + void InitializeColdBoot(); + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_cache.cpp b/exosphere2/program/source/boot/secmon_boot_cache.cpp new file mode 100644 index 000000000..ad0649c3e --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_cache.cpp @@ -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 . + */ +#include + +namespace ams::secmon::boot { + + /* TODO */ + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_config.cpp b/exosphere2/program/source/boot/secmon_boot_config.cpp new file mode 100644 index 000000000..ad0649c3e --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_config.cpp @@ -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 . + */ +#include + +namespace ams::secmon::boot { + + /* TODO */ + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp new file mode 100644 index 000000000..3095f2f77 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_functions.cpp @@ -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 . + */ +#include +#include "secmon_boot_functions.hpp" + +namespace ams::secmon::boot { + + void ClearIram() { + /* Clear the boot code image from where it was loaded in IRAM. */ + util::ClearMemory(MemoryRegionPhysicalIramBootCodeImage.GetPointer(), MemoryRegionPhysicalIramBootCodeImage.GetSize()); + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_functions.hpp b/exosphere2/program/source/boot/secmon_boot_functions.hpp new file mode 100644 index 000000000..5ec201add --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_functions.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon::boot { + + void ClearIram(); + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_setup.cpp b/exosphere2/program/source/boot/secmon_boot_setup.cpp new file mode 100644 index 000000000..06b411f87 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_setup.cpp @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_boot.hpp" +#include "../secmon_setup.hpp" +#include "../secmon_key_storage.hpp" + +namespace ams::secmon::boot { + + namespace { + + void ValidateSystemCounters() { + const uintptr_t sysctr0 = MemoryRegionVirtualDeviceSysCtr0.GetAddress(); + + /* Validate the system counter frequency is as expected. */ + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_CNTFID0) == 19'200'000u); + + /* Validate the system counters are as expected. */ + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 0)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 1)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 2)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 3)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 4)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 5)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 6)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 7)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 8)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID( 9)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID(10)) == 0); + AMS_ABORT_UNLESS(reg::Read(sysctr0 + SYSCTR0_COUNTERID(11)) == 0); + } + + void SetupPmcRegisters() { + const auto pmc = MemoryRegionVirtualDevicePmc.GetAddress(); + + /* Set the physical address of the warmboot binary to scratch 1. */ + reg::Write(pmc + APBDEV_PMC_SCRATCH1, static_cast(MemoryRegionPhysicalDramSecureDataStoreWarmbootFirmware.GetAddress())); + + /* Configure logging by setting bits 18-19 of scratch 20. */ + reg::ReadWrite(pmc + APBDEV_PMC_SCRATCH20, REG_BITS_VALUE(18, 2, 0)); + + /* Clear the wdt reset flag. */ + reg::ReadWrite(pmc + APBDEV_PMC_SCRATCH190, REG_BITS_VALUE(0, 1, 0)); + + /* Configure warmboot to set Set FUSE_PRIVATEKEYDISABLE to KEY_INVISIBLE. */ + reg::ReadWrite(pmc + APBDEV_PMC_SECURE_SCRATCH21, REG_BITS_VALUE(4, 1, 1)); + + /* Write the warmboot key. */ + /* TODO */ + } + + /* This function derives the master kek and device keys using the tsec root key. */ + /* NOTE: Exosphere does not use this in practice, and expects the bootloader to set up keys already. */ + /* NOTE: This function is currently not implemented. If implemented, it will only be a reference implementation. */ + [[maybe_unused]] + void DeriveMasterKekAndDeviceKey() { + /* TODO: Decide whether to implement this. */ + } + + void SetupRandomKey(int slot, se::KeySlotLockFlags flags) { + /* Create an aligned buffer to hold the key. */ + constexpr size_t KeySize = se::AesBlockSize; + util::AlignedBuffer key; + + /* Ensure data is consistent before triggering the SE. */ + hw::FlushDataCache(key, KeySize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes into the key. */ + se::GenerateRandomBytes(key, KeySize); + + /* Ensure that the CPU sees consistent data. */ + hw::DataSynchronizationBarrierInnerShareable(); + hw::FlushDataCache(key, KeySize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Use the random bytes as a key source. */ + se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_DeviceMaster, key, KeySize); + + /* Lock the keyslot. */ + se::LockAesKeySlot(slot, flags); + } + + constinit const u8 MasterKeyVectorsDev[pkg1::OldMasterKeyCount + 1][se::AesBlockSize] = { + {0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE}, /* Zeroes encrypted with Master Key 00. */ + {0x39, 0x33, 0xF9, 0x31, 0xBA, 0xE4, 0xA7, 0x21, 0x2C, 0xDD, 0xB7, 0xD8, 0xB4, 0x4E, 0x37, 0x23}, /* Master key 00 encrypted with Master key 01. */ + {0x97, 0x29, 0xB0, 0x32, 0x43, 0x14, 0x8C, 0xA6, 0x85, 0xE9, 0x5A, 0x94, 0x99, 0x39, 0xAC, 0x5D}, /* Master key 01 encrypted with Master key 02. */ + {0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3}, /* Master key 02 encrypted with Master key 03. */ + {0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA}, /* Master key 03 encrypted with Master key 04. */ + {0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC}, /* Master key 04 encrypted with Master key 05. */ + {0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E}, /* Master key 05 encrypted with Master key 06. */ + {0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19}, /* Master key 06 encrypted with Master key 07. */ + {0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04}, /* Master key 07 encrypted with Master key 08. */ + {0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE}, /* Master key 08 encrypted with Master key 09. */ + {0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4}, /* Master key 09 encrypted with Master key 0A. */ + }; + + constinit const u8 MasterKeyVectorsProd[pkg1::OldMasterKeyCount + 1][se::AesBlockSize] = { + {0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D}, /* Zeroes encrypted with Master Key 00. */ + {0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD}, /* Master key 00 encrypted with Master key 01. */ + {0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72}, /* Master key 01 encrypted with Master key 02. */ + {0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07}, /* Master key 02 encrypted with Master key 03. */ + {0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9}, /* Master key 03 encrypted with Master key 04. */ + {0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE}, /* Master key 04 encrypted with Master key 05. */ + {0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */ + {0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F}, /* Master key 06 encrypted with Master key 07. */ + {0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29}, /* Master key 07 encrypted with Master key 08. */ + {0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80}, /* Master key 08 encrypted with Master key 09. */ + {0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A}, /* Master key 09 encrypted with Master key 0A. */ + }; + + bool TestKeyGeneration(int generation, bool is_prod) { + /* Decrypt the vector chain from generation to start. */ + int slot = pkg1::AesKeySlot_Master; + for (int i = generation; i > 0; --i) { + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Temporary, slot, is_prod ? MasterKeyVectorsProd[i] : MasterKeyVectorsDev[i], se::AesBlockSize); + slot = pkg1::AesKeySlot_Temporary; + } + + /* Decrypt the final vector. */ + u8 test_vector[se::AesBlockSize]; + se::DecryptAes128(test_vector, se::AesBlockSize, slot, is_prod ? MasterKeyVectorsProd[0] : MasterKeyVectorsDev[0], se::AesBlockSize); + + constexpr u8 ZeroBlock[se::AesBlockSize] = {}; + return crypto::IsSameBytes(ZeroBlock, test_vector, se::AesBlockSize); + } + + int DetermineKeyGeneration(bool is_prod) { + /* Test each generation in order. */ + for (int generation = 0; generation < pkg1::KeyGeneration_Count; ++generation) { + if (TestKeyGeneration(generation, is_prod)) { + return generation; + } + } + + /* We must have found a correct key generation. */ + AMS_ABORT(); + } + + void DeriveAllMasterKeys(bool is_prod, u8 * const work_block) { + + /* Determine the generation. */ + const int generation = DetermineKeyGeneration(is_prod); + + /* Set the global generation. */ + ::ams::secmon::impl::SetKeyGeneration(generation); + + /* Derive all old keys. */ + int slot = pkg1::AesKeySlot_Master; + for (int i = generation; i > 0; --i) { + /* Decrypt the old master key. */ + se::DecryptAes128(work_block, se::AesBlockSize, slot, is_prod ? MasterKeyVectorsProd[i] : MasterKeyVectorsDev[i], se::AesBlockSize); + + /* Set the old master key. */ + SetMasterKey(generation - 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 = fuse::GetHardwareState() != fuse::HardwareState_Development; + + /* 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(); + } + + } + + 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(); + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_crt0.s b/exosphere2/program/source/boot/secmon_crt0.s new file mode 100644 index 000000000..44d24ebcc --- /dev/null +++ b/exosphere2/program/source/boot/secmon_crt0.s @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.section .crt0.text.start, "ax", %progbits +.align 6 +.global _start +_start: + /* mask all interrupts */ + msr daifset, #0xF + + /* Set the stack pointer to a temporary location. */ + ldr x20, =0x7C010800 + mov sp, x20 + + /* Initialize all memory to expected state. */ + ldr x0, =__bss_start__ + ldr x1, =__bss_end__ + ldr x2, =__boot_bss_start__ + ldr x3, =__boot_bss_end__ + bl _ZN3ams6secmon4boot10InitializeEmmmm + + /* Jump to the first bit of virtual code. */ + ldr x16, =_ZN3ams6secmon5StartEv + br x16 diff --git a/exosphere2/program/source/boot/secmon_crt0_cpp.cpp b/exosphere2/program/source/boot/secmon_crt0_cpp.cpp new file mode 100644 index 000000000..35b993ee5 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_crt0_cpp.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_boot.hpp" +#include "../secmon_setup.hpp" + +extern "C" void __libc_init_array(); + +namespace ams::secmon::boot { + + void Initialize(uintptr_t bss_start, size_t bss_end, uintptr_t boot_bss_start, uintptr_t boot_bss_end) { + /* Set our start time. */ + auto &secmon_params = *MemoryRegionPhysicalDeviceBootloaderParams.GetPointer(); + secmon_params.secmon_start_time = *reinterpret_cast(MemoryRegionPhysicalDeviceTimer.GetAddress() + 0x10); + + /* Setup DMA controllers. */ + SetupSocDmaControllers(); + + /* Make the page table. */ + MakePageTable(); + + /* Setup memory controllers the MMU. */ + SetupCpuMemoryControllersEnableMmu(); + + /* Clear bss. */ + std::memset(reinterpret_cast(bss_start), 0, bss_end - bss_start); + std::memset(reinterpret_cast(boot_bss_start), 0, boot_bss_end - boot_bss_start); + + /* Call init array. */ + __libc_init_array(); + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_key_data.cpp b/exosphere2/program/source/boot/secmon_key_data.cpp new file mode 100644 index 000000000..ad0649c3e --- /dev/null +++ b/exosphere2/program/source/boot/secmon_key_data.cpp @@ -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 . + */ +#include + +namespace ams::secmon::boot { + + /* TODO */ + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp new file mode 100644 index 000000000..738452dab --- /dev/null +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_boot.hpp" +#include "secmon_boot_functions.hpp" +#include "../secmon_setup.hpp" +#include "../secmon_misc.hpp" + +namespace ams::secmon { + + void Main() { + /* Set library register addresses. */ + /* actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); */ + clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress()); + /* flowctrl::SetRegisterAddress(); */ + 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(); */ + pmc::SetRegisterAddress(MemoryRegionVirtualDevicePmc.GetAddress()); + se::SetRegisterAddress(MemoryRegionVirtualDeviceSecurityEngine.GetAddress()); + uart::SetRegisterAddress(MemoryRegionVirtualDeviceUart.GetAddress()); + wdt::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress()); + util::SetRegisterAddress(MemoryRegionVirtualDeviceTimer.GetAddress()); + + /* Get the secure monitor parameters. */ + auto &secmon_params = *reinterpret_cast(MemoryRegionVirtualDeviceBootloaderParams.GetAddress()); + + /* Perform initialization. */ + { + /* Perform initial setup. */ + /* This checks the security engine's validity, and configures common interrupts in the GIC. */ + /* This also initializes the global configuration context. */ + secmon::Setup1(); + + /* Save the boot info. */ + secmon::SaveBootInfo(secmon_params); + + /* Perform cold-boot specific init. */ + secmon::boot::InitializeColdBoot(); + + /* Setup the SoC security measures. */ + secmon::SetupSocSecurity(); + + /* TODO: More init. */ + + /* 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; + } + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_make_page_table.cpp b/exosphere2/program/source/boot/secmon_make_page_table.cpp new file mode 100644 index 000000000..dfde30d16 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_make_page_table.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_setup.hpp" +#include "secmon_boot.hpp" + +namespace ams::secmon::boot { + + namespace { + + using namespace ams::mmu; + + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRoCode = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRoCode, MemoryAttributeIndexNormal); + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRwCode = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwCode, MemoryAttributeIndexNormal); + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRoData = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRoData, MemoryAttributeIndexNormal); + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwData, MemoryAttributeIndexNormal); + constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureRwData = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexNormal); + + constexpr inline PageTableMappingAttribute MappingAttributesEl3SecureDevice = AddMappingAttributeIndex(PageTableMappingAttributes_El3SecureRwData, MemoryAttributeIndexDevice); + constexpr inline PageTableMappingAttribute MappingAttributesEl3NonSecureDevice = AddMappingAttributeIndex(PageTableMappingAttributes_El3NonSecureRwData, MemoryAttributeIndexDevice); + + + constexpr void ClearMemory(volatile u64 *start, size_t size) { + volatile u64 * const end = start + (size / sizeof(u64)); + + for (volatile u64 *cur = start; cur < end; ++cur) { + *cur = 0; + } + } + + constexpr void MakePageTablesImpl(u64 *l1, u64 *l2, u64 *l3) { + /* Setup the L1 table. */ + { + ClearMemory(l1, MemoryRegionPhysicalTzramL1PageTable.GetSize()); + + /* Create an L1 table entry for the physical region. */ + SetL1TableEntry(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + static_assert(GetL1EntryIndex(MemoryRegionPhysical.GetAddress()) == 1); + + /* Create an L1 mapping entry for dram. */ + SetL1BlockEntry(l1, MemoryRegionDram.GetAddress(), MemoryRegionDram.GetAddress(), MemoryRegionDram.GetSize(), MappingAttributesEl3NonSecureRwData); + + /* Create an L1 table entry for the virtual region. */ + SetL1TableEntry(l1, MemoryRegionVirtual.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + } + + /* Setup the L2 table. */ + { + ClearMemory(l2, MemoryRegionPhysicalTzramL2L3PageTable.GetSize()); + + /* Create an L2 table entry for the virtual region. */ + SetL2TableEntry(l2, MemoryRegionVirtualL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + + /* Create an L2 table entry for the physical iram region. */ + SetL2TableEntry(l2, MemoryRegionPhysicalIramL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + + /* Create an L2 table entry for the physical tzram region. */ + SetL2TableEntry(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), PageTableTableAttributes_El3SecureCode); + } + + /* Setup the L3 table. */ + { + /* L2 and L3 share a page table. */ + if (l2 != l3) { + ClearMemory(l3, MemoryRegionPhysicalTzramL2L3PageTable.GetSize()); + } + + /* Identity-map TZRAM as rwx. */ + SetL3BlockEntry(l3, MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize(), MappingAttributesEl3SecureRwCode); + + /* Identity-map IRAM boot code as rwx. */ + SetL3BlockEntry(l3, MemoryRegionPhysicalIramBootCode.GetAddress(), MemoryRegionPhysicalIramBootCode.GetAddress(), MemoryRegionPhysicalIramBootCode.GetSize(), MappingAttributesEl3SecureRwCode); + + /* Map all devices. */ + { + #define MAP_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \ + SetL3BlockEntry(l3, MemoryRegionVirtualDevice##_NAME_.GetAddress(), MemoryRegionPhysicalDevice##_NAME_.GetAddress(), MemoryRegionVirtualDevice##_NAME_.GetSize(), _SECURE_ ? MappingAttributesEl3SecureDevice : MappingAttributesEl3NonSecureDevice); + + AMS_SECMON_FOREACH_DEVICE_REGION(MAP_DEVICE_REGION); + + #undef MAP_DEVICE_REGION + } + + /* Map the IRAM SC7 work region. */ + SetL3BlockEntry(l3, MemoryRegionVirtualIramSc7Work.GetAddress(), MemoryRegionPhysicalIramSc7Work.GetAddress(), MemoryRegionVirtualIramSc7Work.GetSize(), MappingAttributesEl3NonSecureDevice); + + /* Map the IRAM SC7 firmware region. */ + SetL3BlockEntry(l3, MemoryRegionVirtualIramSc7Firmware.GetAddress(), MemoryRegionPhysicalIramSc7Firmware.GetAddress(), MemoryRegionVirtualIramSc7Firmware.GetSize(), MappingAttributesEl3NonSecureDevice); + + /* Map the 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, MappingAttributesEl3SecureDevice); + SetL3BlockEntry(l3, MemoryRegionVirtualTzramL2L3PageTable.GetAddress(), MemoryRegionPhysicalTzramL2L3PageTable.GetAddress(), MemoryRegionVirtualTzramL2L3PageTable.GetSize(), MappingAttributesEl3SecureRwData); + } + } + + constexpr bool ValidateTzramPageTables() { + u64 l1_table[MemoryRegionPhysicalTzramL1PageTable.GetSize() / sizeof(u64)] = {}; + u64 l2_l3_table[MemoryRegionPhysicalTzramL2L3PageTable.GetSize() / sizeof(u64)] = {}; + + MakePageTablesImpl(l1_table, l2_l3_table, l2_l3_table); + + return true; + } + + static_assert(ValidateTzramPageTables()); + + } + + void MakePageTable() { + u64 * const l1 = MemoryRegionPhysicalTzramL1PageTable.GetPointer(); + u64 * const l2_l3 = MemoryRegionPhysicalTzramL2L3PageTable.GetPointer(); + MakePageTablesImpl(l1, l2_l3, l2_l3); + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_package2.cpp b/exosphere2/program/source/boot/secmon_package2.cpp new file mode 100644 index 000000000..ad0649c3e --- /dev/null +++ b/exosphere2/program/source/boot/secmon_package2.cpp @@ -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 . + */ +#include + +namespace ams::secmon::boot { + + /* TODO */ + +} \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_size_data.s b/exosphere2/program/source/boot/secmon_size_data.s new file mode 100644 index 000000000..0943ef943 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_size_data.s @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.section .metadata.sizes, "ax", %progbits +.align 6 +.global __metadata__sizes +__metadata__sizes: + .quad 0xAAAAAAAAAAAAAAAA, 0xBBBBBBBBBBBBBBBB + .quad __glob_start__ + .quad __bootcode_start__ + .quad __bootcode_end__ + .quad __program_start__ + .quad 0xCCCCCCCCCCCCCCCC, 0xDDDDDDDDDDDDDDDD diff --git a/exosphere2/program/source/secmon_cache.inc b/exosphere2/program/source/secmon_cache.inc new file mode 100644 index 000000000..1ee25f79a --- /dev/null +++ b/exosphere2/program/source/secmon_cache.inc @@ -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 . + */ + +void FlushEntireDataCache(); +void InvalidateEntireDataCache(); + +void EnsureMappingConsistency(); +void EnsureMappingConsistency(uintptr_t address); +void EnsureInstructionConsistency(); \ No newline at end of file diff --git a/exosphere2/program/source/secmon_cache_impl.inc b/exosphere2/program/source/secmon_cache_impl.inc new file mode 100644 index 000000000..c486c8b4d --- /dev/null +++ b/exosphere2/program/source/secmon_cache_impl.inc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace { + + ALWAYS_INLINE int FloorLog2(int v) { + return BITSIZEOF(u32) - (hw::CountLeadingZeros(static_cast(v)) + 1); + } + + ALWAYS_INLINE int CeilLog2(int v) { + const int log = FloorLog2(v); + return ((1 << log) == v) ? log : log + 1; + } + + void FlushDataCacheTo(int loc) { + for (int level = 0; level < loc; ++level) { + /* Set the selection register. */ + { + util::BitPack32 csselr = {}; + csselr.Set(0); + csselr.Set(level); + HW_CPU_SET_CSSELR_EL1(csselr); + } + + /* Ensure that reordering doesn't occur around this operation. */ + hw::InstructionSynchronizationBarrier(); + + /* Get ccsidr. */ + util::BitPack32 ccsidr; + HW_CPU_GET_CCSIDR_EL1(ccsidr); + + /* Get cache size id info. */ + const int num_sets = ccsidr.Get() + 1; + const int num_ways = ccsidr.Get() + 1; + const int line_size = ccsidr.Get() + 4; + + const int way_shift = 32 - FloorLog2(num_ways); + const int set_shift = line_size; + + for (int way = 0; way <= num_ways; way++) { + for (int set = 0; set <= num_sets; set++) { + const u64 value = (static_cast(way) << way_shift) | (static_cast(set) << set_shift) | (static_cast(level) << 1); + __asm__ __volatile__("dc cisw, %[value]" :: [value]"r"(value) : "memory"); + } + } + } + } + + void InvalidateDataCacheTo(int loc) { + for (int level = 0; level < loc; ++level) { + /* Set the selection register. */ + { + util::BitPack32 csselr = {}; + csselr.Set(0); + csselr.Set(level); + HW_CPU_SET_CSSELR_EL1(csselr); + } + + /* Ensure that reordering doesn't occur around this operation. */ + hw::InstructionSynchronizationBarrier(); + + /* Get ccsidr. */ + util::BitPack32 ccsidr; + HW_CPU_GET_CCSIDR_EL1(ccsidr); + + /* Get cache size id info. */ + const int num_sets = ccsidr.Get() + 1; + const int num_ways = ccsidr.Get() + 1; + const int line_size = ccsidr.Get() + 4; + + const int way_shift = 32 - FloorLog2(num_ways); + const int set_shift = line_size; + + for (int way = 0; way <= num_ways; way++) { + for (int set = 0; set <= num_sets; set++) { + const u64 value = (static_cast(way) << way_shift) | (static_cast(set) << set_shift) | (static_cast(level) << 1); + __asm__ __volatile__("dc isw, %[value]" :: [value]"r"(value) : "memory"); + } + } + } + } + +} + +void FlushEntireDataCache() { + util::BitPack32 clidr; + HW_CPU_GET_CLIDR_EL1(clidr); + FlushDataCacheTo(clidr.Get()); +} + +void InvalidateEntireDataCache() { + util::BitPack32 clidr; + HW_CPU_GET_CLIDR_EL1(clidr); + InvalidateDataCacheTo(clidr.Get()); +} + +void EnsureMappingConsistency() { + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + ::ams::hw::InvalidateEntireTlb(); + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + + ::ams::hw::InstructionSynchronizationBarrier(); +} + +void EnsureMappingConsistency(uintptr_t address) { + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + ::ams::hw::InvalidateTlb(address); + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + + ::ams::hw::InstructionSynchronizationBarrier(); +} + +void EnsureInstructionConsistency() { + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + ::ams::hw::InvalidateEntireInstructionCache(); + ::ams::hw::DataSynchronizationBarrierInnerShareable(); + + ::ams::hw::InstructionSynchronizationBarrier(); +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_cpu_context.cpp b/exosphere2/program/source/secmon_cpu_context.cpp new file mode 100644 index 000000000..d2185a699 --- /dev/null +++ b/exosphere2/program/source/secmon_cpu_context.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_cpu_context.hpp" +#include "secmon_error.hpp" + +namespace ams::secmon { + + namespace { + + struct DebugRegisters { + u32 osdttrx_el1; + u32 osdtrtx_el1; + u32 mdscr_el1; + u32 oseccr_el1; + u32 mdccint_el1; + u32 dbgclaimclr_el1; + u32 dbgvcr32_el2; + u32 sder32_el3; + u32 mdcr_el2; + u32 mdcr_el3; + u32 spsr_el3; + u64 dbgbvcr_el1[12]; + u64 dbgwvcr_el1[ 8]; + }; + + struct CoreContext { + EntryContext entry_context; + bool is_on; + bool is_reset_expected; + bool is_debug_registers_saved; + DebugRegisters debug_registers; + }; + + void SaveDebugRegisters(DebugRegisters &dr) { + /* Set the OS lock; this will be unlocked by entry code. */ + HW_CPU_SET_OSLAR_EL1(1); + + /* Save general debug registers. */ + HW_CPU_GET_OSDTRRX_EL1 (dr.osdttrx_el1); + HW_CPU_GET_OSDTRTX_EL1 (dr.osdtrtx_el1); + HW_CPU_GET_MDSCR_EL1 (dr.mdscr_el1); + HW_CPU_GET_OSECCR_EL1 (dr.oseccr_el1); + HW_CPU_GET_MDCCINT_EL1 (dr.mdccint_el1); + HW_CPU_GET_DBGCLAIMCLR_EL1(dr.dbgclaimclr_el1); + HW_CPU_GET_DBGVCR32_EL2 (dr.dbgvcr32_el2); + HW_CPU_GET_SDER32_EL3 (dr.sder32_el3); + HW_CPU_GET_MDCR_EL2 (dr.mdcr_el2); + HW_CPU_GET_MDCR_EL3 (dr.mdcr_el3); + HW_CPU_GET_SPSR_EL3 (dr.spsr_el3); + + /* Save debug breakpoints. */ + HW_CPU_GET_DBGBVR0_EL1(dr.dbgbvcr_el1[ 0]); + HW_CPU_GET_DBGBCR0_EL1(dr.dbgbvcr_el1[ 1]); + HW_CPU_GET_DBGBVR1_EL1(dr.dbgbvcr_el1[ 2]); + HW_CPU_GET_DBGBCR1_EL1(dr.dbgbvcr_el1[ 3]); + HW_CPU_GET_DBGBVR2_EL1(dr.dbgbvcr_el1[ 4]); + HW_CPU_GET_DBGBCR2_EL1(dr.dbgbvcr_el1[ 5]); + HW_CPU_GET_DBGBVR3_EL1(dr.dbgbvcr_el1[ 6]); + HW_CPU_GET_DBGBCR3_EL1(dr.dbgbvcr_el1[ 7]); + HW_CPU_GET_DBGBVR4_EL1(dr.dbgbvcr_el1[ 8]); + HW_CPU_GET_DBGBCR4_EL1(dr.dbgbvcr_el1[ 9]); + HW_CPU_GET_DBGBVR5_EL1(dr.dbgbvcr_el1[10]); + HW_CPU_GET_DBGBCR5_EL1(dr.dbgbvcr_el1[11]); + + /* Save debug watchpoints. */ + HW_CPU_GET_DBGWVR0_EL1(dr.dbgwvcr_el1[0]); + HW_CPU_GET_DBGWCR0_EL1(dr.dbgwvcr_el1[1]); + HW_CPU_GET_DBGWVR1_EL1(dr.dbgwvcr_el1[2]); + HW_CPU_GET_DBGWCR1_EL1(dr.dbgwvcr_el1[3]); + HW_CPU_GET_DBGWVR2_EL1(dr.dbgwvcr_el1[4]); + HW_CPU_GET_DBGWCR2_EL1(dr.dbgwvcr_el1[5]); + HW_CPU_GET_DBGWVR3_EL1(dr.dbgwvcr_el1[6]); + HW_CPU_GET_DBGWCR3_EL1(dr.dbgwvcr_el1[7]); + } + + void RestoreDebugRegisters(const DebugRegisters &dr) { + /* Restore general debug registers. */ + HW_CPU_SET_OSDTRRX_EL1 (dr.osdttrx_el1); + HW_CPU_SET_OSDTRTX_EL1 (dr.osdtrtx_el1); + HW_CPU_SET_MDSCR_EL1 (dr.mdscr_el1); + HW_CPU_SET_OSECCR_EL1 (dr.oseccr_el1); + HW_CPU_SET_MDCCINT_EL1 (dr.mdccint_el1); + HW_CPU_SET_DBGCLAIMCLR_EL1(dr.dbgclaimclr_el1); + HW_CPU_SET_DBGVCR32_EL2 (dr.dbgvcr32_el2); + HW_CPU_SET_SDER32_EL3 (dr.sder32_el3); + HW_CPU_SET_MDCR_EL2 (dr.mdcr_el2); + HW_CPU_SET_MDCR_EL3 (dr.mdcr_el3); + HW_CPU_SET_SPSR_EL3 (dr.spsr_el3); + + /* Restore debug breakpoints. */ + HW_CPU_SET_DBGBVR0_EL1(dr.dbgbvcr_el1[ 0]); + HW_CPU_SET_DBGBCR0_EL1(dr.dbgbvcr_el1[ 1]); + HW_CPU_SET_DBGBVR1_EL1(dr.dbgbvcr_el1[ 2]); + HW_CPU_SET_DBGBCR1_EL1(dr.dbgbvcr_el1[ 3]); + HW_CPU_SET_DBGBVR2_EL1(dr.dbgbvcr_el1[ 4]); + HW_CPU_SET_DBGBCR2_EL1(dr.dbgbvcr_el1[ 5]); + HW_CPU_SET_DBGBVR3_EL1(dr.dbgbvcr_el1[ 6]); + HW_CPU_SET_DBGBCR3_EL1(dr.dbgbvcr_el1[ 7]); + HW_CPU_SET_DBGBVR4_EL1(dr.dbgbvcr_el1[ 8]); + HW_CPU_SET_DBGBCR4_EL1(dr.dbgbvcr_el1[ 9]); + HW_CPU_SET_DBGBVR5_EL1(dr.dbgbvcr_el1[10]); + HW_CPU_SET_DBGBCR5_EL1(dr.dbgbvcr_el1[11]); + + /* Restore debug watchpoints. */ + HW_CPU_SET_DBGWVR0_EL1(dr.dbgwvcr_el1[0]); + HW_CPU_SET_DBGWCR0_EL1(dr.dbgwvcr_el1[1]); + HW_CPU_SET_DBGWVR1_EL1(dr.dbgwvcr_el1[2]); + HW_CPU_SET_DBGWCR1_EL1(dr.dbgwvcr_el1[3]); + HW_CPU_SET_DBGWVR2_EL1(dr.dbgwvcr_el1[4]); + HW_CPU_SET_DBGWCR2_EL1(dr.dbgwvcr_el1[5]); + HW_CPU_SET_DBGWVR3_EL1(dr.dbgwvcr_el1[6]); + HW_CPU_SET_DBGWCR3_EL1(dr.dbgwvcr_el1[7]); + } + + constinit CoreContext g_core_contexts[NumCores] = {}; + + } + + bool IsCoreOn(int core) { + return g_core_contexts[core].is_on; + } + + void SetCoreOff() { + g_core_contexts[hw::GetCurrentCoreId()].is_on = false; + } + + bool IsResetExpected() { + return g_core_contexts[hw::GetCurrentCoreId()].is_reset_expected; + } + + void SetResetExpected(int core, bool expected) { + g_core_contexts[core].is_reset_expected = expected; + } + + void SetResetExpected(bool expected) { + SetResetExpected(hw::GetCurrentCoreId(), expected); + } + + void SetEntryContext(int core, uintptr_t address, uintptr_t arg) { + g_core_contexts[core].entry_context.pc = address; + g_core_contexts[core].entry_context.x0 = arg; + } + + void GetEntryContext(EntryContext *out) { + auto &ctx = g_core_contexts[hw::GetCurrentCoreId()]; + + const auto pc = ctx.entry_context.pc; + const auto x0 = ctx.entry_context.x0; + + if (pc == 0 || ctx.is_on) { + SetError(pkg1::ErrorInfo_InvalidCoreContext); + AMS_ABORT("Invalid core context"); + } + + ctx.entry_context = {}; + ctx.is_on = true; + + out->pc = pc; + out->x0 = x0; + } + + void SaveDebugRegisters() { + auto &ctx = g_core_contexts[hw::GetCurrentCoreId()]; + + SaveDebugRegisters(ctx.debug_registers); + ctx.is_debug_registers_saved = true; + } + + void RestoreDebugRegisters() { + auto &ctx = g_core_contexts[hw::GetCurrentCoreId()]; + + if (ctx.is_debug_registers_saved) { + RestoreDebugRegisters(ctx.debug_registers); + ctx.is_debug_registers_saved = false; + } + } + +} diff --git a/exosphere2/program/source/secmon_cpu_context.hpp b/exosphere2/program/source/secmon_cpu_context.hpp new file mode 100644 index 000000000..a19bd8af8 --- /dev/null +++ b/exosphere2/program/source/secmon_cpu_context.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon { + + constexpr inline int NumCores = 4; + + struct EntryContext { + u64 x0; + u64 pc; + }; + + bool IsCoreOn(int core); + void SetCoreOff(); + + bool IsResetExpected(); + void SetResetExpected(int core, bool expected); + void SetResetExpected(bool expected); + + void SetEntryContext(int core, uintptr_t address, uintptr_t arg); + void GetEntryContext(EntryContext *out); + + void SaveDebugRegisters(); + void RestoreDebugRegisters(); + +} diff --git a/exosphere2/program/source/secmon_error.cpp b/exosphere2/program/source/secmon_error.cpp new file mode 100644 index 000000000..68338212d --- /dev/null +++ b/exosphere2/program/source/secmon_error.cpp @@ -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 . + */ +#include +#include "secmon_error.hpp" + +namespace ams::diag { + + void AbortImpl() { + 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() { + /* Lockout the security engine. */ + se::Lockout(); + + /* TODO: Lockout fuses. */ + + /* TODO: Disable SE Crypto Operations. */ + + while (true) { + wdt::Reboot(); + } + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_error.hpp b/exosphere2/program/source/secmon_error.hpp new file mode 100644 index 000000000..abbfad245 --- /dev/null +++ b/exosphere2/program/source/secmon_error.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon { + + void SetError(pkg1::ErrorInfo info); + NORETURN void ErrorReboot(); + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_exception_vectors.s b/exosphere2/program/source/secmon_exception_vectors.s new file mode 100644 index 000000000..ef1ab9f32 --- /dev/null +++ b/exosphere2/program/source/secmon_exception_vectors.s @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Some macros taken from https://github.com/ARM-software/arm-trusted-firmware/blob/master/include/common/aarch64/asm_macros.S */ +/* + * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Declare the exception vector table, enforcing it is aligned on a + * 2KB boundary, as required by the ARMv8 architecture. + * Use zero bytes as the fill value to be stored in the padding bytes + * so that it inserts illegal AArch64 instructions. This increases + * security, robustness and potentially facilitates debugging. + */ +.macro vector_base label, section_name=.vectors +.section \section_name, "ax" +.align 11, 0 +\label: +.endm + +/* + * Create an entry in the exception vector table, enforcing it is + * aligned on a 128-byte boundary, as required by the ARMv8 architecture. + * Use zero bytes as the fill value to be stored in the padding bytes + * so that it inserts illegal AArch64 instructions. This increases + * security, robustness and potentially facilitates debugging. + */ +.macro vector_entry label, section_name=.vectors +.cfi_sections .debug_frame +.section \section_name, "ax" +.align 7, 0 +.type \label, %function +.func \label +.cfi_startproc +\label: +.endm + +/* + * This macro verifies that the given vector doesnt exceed the + * architectural limit of 32 instructions. This is meant to be placed + * immediately after the last instruction in the vector. It takes the + * vector entry as the parameter + */ +.macro check_vector_size since + .endfunc + .cfi_endproc + .if (. - \since) > (32 * 4) + .error "Vector exceeds 32 instructions" + .endif +.endm + +/* Actual Vectors for Secure Monitor. */ +.global _ZN3ams6secmon16ExceptionVectorsEv +vector_base _ZN3ams6secmon16ExceptionVectorsEv + +/* Current EL, SP0 */ +vector_entry synch_sp0 + /* Branch to the exception handler. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + .endfunc + .cfi_endproc +_ZN3ams6secmon26UnexpectedExceptionHandlerEv: + /* Load the ErrorInfo scratch. */ + ldr x0, =0x1F004AC40 + + /* Write ErrorInfo_UnknownAbort to it. */ + ldr w1, =0x07F00010 + str w1, [x0] + + /* Perform an error reboot. */ + b _ZN3ams6secmon11ErrorRebootEv + +vector_entry irq_sp0 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size irq_sp0 + +vector_entry fiq_sp0 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size fiq_sp0 + +vector_entry serror_sp0 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size serror_sp0 + +/* Current EL, SPx */ +vector_entry synch_spx + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size synch_spx + +vector_entry irq_spx + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size irq_spx + +vector_entry fiq_spx + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size fiq_spx + +vector_entry serror_spx + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size serror_spx + +/* Lower EL, A64 */ +vector_entry synch_a64 + /* Check whether the exception is an SMC. If it's not, take the unexpected handler. */ + stp x29, x30, [sp, #-0x10]! + mrs x30, esr_el3 + lsr w29, w30, #26 + cmp w29, #0x17 + ldp x29, x30, [sp], #0x10 + b.ne _ZN3ams6secmon26UnexpectedExceptionHandlerEv + + /* Save x29 and x30. */ + stp x29, x30, [sp, #-0x10]! + + /* Get the current core. */ + mrs x29, mpidr_el1 + and x29, x29, #3 + + /* If we're not on core 3, take the core0-2 handler. */ + cmp x29, #3 + b.ne _ZN3ams6secmon25HandleSmcExceptionCore012Ev + + /* Handle the smc. */ + bl _ZN3ams6secmon18HandleSmcExceptionEv + + /* Return. */ + ldp x29, x30, [sp], #0x10 + eret + check_vector_size synch_a64 + +vector_entry irq_a64 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size irq_a64 + +vector_entry fiq_a64 + /* Save X29, X30. */ + stp x29, x30, [sp, #-0x10]! + + /* Get the current core ID, ensure it's core 3. */ + mrs x29, mpidr_el1 + and x29, x29, #3 + cmp x29, #3 + b.ne _ZN3ams6secmon26UnexpectedExceptionHandlerEv + + /* Save x26-x28, x18. */ + stp x28, x18, [sp, #-0x10]! + stp x26, x27, [sp, #-0x10]! + + /* Set x18 to the global data region. */ + ldr x18, =0x1F01FA000 + + /* Handle the fiq exception. */ + bl _ZN3ams6secmon18HandleFiqExceptionEv + + /* Restore registers. */ + ldp x26, x27, [sp], #0x10 + ldp x28, x18, [sp], #0x10 + ldp x29, x30, [sp], #0x10 + + /* Return. */ + eret + check_vector_size fiq_a64 + +vector_entry serror_a64 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + .endfunc + .cfi_endproc +_ZN3ams6secmon25HandleSmcExceptionCore012Ev: + /* Acquire exclusive access to the common smc stack. */ + stp x4, x5, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x0, x1, [sp, #-0x10]! + bl _ZN3ams6secmon25AcquireCommonSmcStackLockEv + ldp x0, x1, [sp], #0x10 + ldp x2, x3, [sp], #0x10 + ldp x4, x5, [sp], #0x10 + + /* Pivot to use the common smc stack. */ + mov x30, sp + ldr x29, =0x1F01F6E80 + mov sp, x29 + stp x29, x30, [sp, #-0x10]! + + /* Handle the SMC. */ + bl _ZN3ams6secmon18HandleSmcExceptionEv + + /* Restore our core-specific stack. */ + ldp x29, x30, [sp], #0x10 + mov x30, sp + + /* Release our exclusive access to the common smc stack. */ + stp x0, x1, [sp, #-0x10]! + bl _ZN3ams6secmon25ReleaseCommonSmcStackLockEv + ldp x0, x1, [sp], #0x10 + + /* Return. */ + ldp x29, x30, [sp], #0x10 + eret + +/* Lower EL, A32 */ +vector_entry synch_a32 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size synch_a32 + +vector_entry irq_a32 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + .endfunc + .cfi_endproc +_ZN3ams6secmon18HandleSmcExceptionEv: + /* Save registers. */ + stp x29, x30, [sp, #-0x10]! + stp x18, x19, [sp, #-0x10]! + stp x16, x17, [sp, #-0x10]! + stp x14, x15, [sp, #-0x10]! + stp x12, x13, [sp, #-0x10]! + stp x10, x11, [sp, #-0x10]! + stp x8, x9, [sp, #-0x10]! + stp x6, x7, [sp, #-0x10]! + stp x4, x5, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x0, x1, [sp, #-0x10]! + + /* Set x18 to the global data region. */ + ldr x18, =0x1F01FA000 + + /* Get esr. */ + mrs x0, esr_el3 + and x0, x0, #0xFFFF + + /* Get the function arguments. */ + mov x1, sp + + /* Invoke the smc handler. */ + bl _ZN3ams6secmon3smc9HandleSmcEiRNS1_12SmcArgumentsE + + /* Restore registers. */ + ldp x0, x1, [sp], #0x10 + ldp x2, x3, [sp], #0x10 + ldp x4, x5, [sp], #0x10 + ldp x6, x7, [sp], #0x10 + ldp x8, x9, [sp], #0x10 + ldp x10, x11, [sp], #0x10 + ldp x12, x13, [sp], #0x10 + ldp x14, x15, [sp], #0x10 + ldp x16, x17, [sp], #0x10 + ldp x18, x19, [sp], #0x10 + ldp x29, x30, [sp], #0x10 + + ret +vector_entry fiq_a32 + /* Handle fiq from a32 the same as fiq from a64. */ + b fiq_a64 + .endfunc + .cfi_endproc +_ZN3ams6secmon18HandleFiqExceptionEv: + /* Save registers. */ + stp x29, x30, [sp, #-0x10]! + stp x24, x25, [sp, #-0x10]! + stp x22, x23, [sp, #-0x10]! + stp x20, x21, [sp, #-0x10]! + stp x18, x19, [sp, #-0x10]! + stp x16, x17, [sp, #-0x10]! + stp x14, x15, [sp, #-0x10]! + stp x12, x13, [sp, #-0x10]! + stp x10, x11, [sp, #-0x10]! + stp x8, x9, [sp, #-0x10]! + stp x6, x7, [sp, #-0x10]! + stp x4, x5, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x0, x1, [sp, #-0x10]! + + /* Invoke the interrupt handler. */ + bl _ZN3ams6secmon15HandleInterruptEv + + /* Restore registers. */ + ldp x0, x1, [sp], #0x10 + ldp x2, x3, [sp], #0x10 + ldp x4, x5, [sp], #0x10 + ldp x6, x7, [sp], #0x10 + ldp x8, x9, [sp], #0x10 + ldp x10, x11, [sp], #0x10 + ldp x12, x13, [sp], #0x10 + ldp x14, x15, [sp], #0x10 + ldp x16, x17, [sp], #0x10 + ldp x18, x19, [sp], #0x10 + ldp x20, x21, [sp], #0x10 + ldp x22, x23, [sp], #0x10 + ldp x24, x25, [sp], #0x10 + ldp x29, x30, [sp], #0x10 + + ret + +vector_entry serror_a32 + /* An unexpected exception was taken. */ + b _ZN3ams6secmon26UnexpectedExceptionHandlerEv + check_vector_size serror_a32 + + /* Instantiate the literal pool for the exception vectors. */ + .ltorg diff --git a/exosphere2/program/source/secmon_interrupt_handler.cpp b/exosphere2/program/source/secmon_interrupt_handler.cpp new file mode 100644 index 000000000..41f466632 --- /dev/null +++ b/exosphere2/program/source/secmon_interrupt_handler.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_interrupt_handler.hpp" +#include "secmon_error.hpp" + +namespace ams::secmon { + + namespace { + + constexpr inline int InterruptHandlersMax = 4; + + constinit InterruptHandler g_handlers[InterruptHandlersMax] = {}; + constinit int g_interrupt_ids[InterruptHandlersMax] = {}; + + } + + void SetInterruptHandler(int interrupt_id, InterruptHandler handler) { + for (int i = 0; i < InterruptHandlersMax; ++i) { + if (g_interrupt_ids[i] == 0) { + g_interrupt_ids[i] = interrupt_id; + g_handlers[i] = handler; + return; + } + } + + AMS_ABORT("Failed to register interrupt handler"); + } + + void HandleInterrupt() { + /* Get the interrupt id. */ + const int interrupt_id = gic::GetInterruptRequestId(); + if (interrupt_id >= gic::InterruptCount) { + /* Invalid interrupt number, just return. */ + return; + } + + /* Check each handler. */ + for (int i = 0; i < InterruptHandlersMax; ++i) { + if (g_interrupt_ids[i] == interrupt_id) { + /* Invoke the handler. */ + g_handlers[i](); + gic::SetEndOfInterrupt(interrupt_id); + return; + } + } + + AMS_ABORT("Failed to find interrupt handler."); + } + +} diff --git a/exosphere2/program/source/secmon_interrupt_handler.hpp b/exosphere2/program/source/secmon_interrupt_handler.hpp new file mode 100644 index 000000000..136da0264 --- /dev/null +++ b/exosphere2/program/source/secmon_interrupt_handler.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon { + + using InterruptHandler = void (*)(); + + void SetInterruptHandler(int interrupt_id, InterruptHandler handler); + +} diff --git a/exosphere2/program/source/secmon_key_storage.cpp b/exosphere2/program/source/secmon_key_storage.cpp new file mode 100644 index 000000000..8e823deec --- /dev/null +++ b/exosphere2/program/source/secmon_key_storage.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_key_storage.hpp" + +namespace ams::secmon { + + namespace { + + constinit u8 g_rsa_moduli[ImportRsaKey_Count][se::RsaSize] = {}; + constinit bool g_rsa_modulus_committed[ImportRsaKey_Count] = {}; + + ALWAYS_INLINE u8 *GetRsaKeyModulus(ImportRsaKey which) { + return g_rsa_moduli[which]; + } + + ALWAYS_INLINE u8 *GetRsaKeyPrivateExponent(ImportRsaKey which) { + return ::ams::secmon::impl::GetRsaPrivateExponentStorage(static_cast(which)); + } + + ALWAYS_INLINE bool IsRsaKeyProvisional(ImportRsaKey which) { + return g_rsa_modulus_committed[which] == false; + } + + void ClearRsaKeyModulus(ImportRsaKey which) { + g_rsa_modulus_committed[which] = false; + std::memset(g_rsa_moduli[which], 0, se::RsaSize); + } + + ALWAYS_INLINE u8 *GetMasterKeyStorage(int index) { + return ::ams::secmon::impl::GetMasterKeyStorage(index); + } + + ALWAYS_INLINE u8 *GetDeviceMasterKeyStorage(int index) { + return ::ams::secmon::impl::GetDeviceMasterKeyStorage(index); + } + + } + + void ImportRsaKeyExponent(ImportRsaKey which, const void *src, size_t size) { + /* If we import an exponent, the modulus is not committed. */ + ClearRsaKeyModulus(which); + + /* Copy the exponent. */ + std::memcpy(GetRsaKeyPrivateExponent(which), src, size); + } + + void ImportRsaKeyModulusProvisionally(ImportRsaKey which, const void *src, size_t size) { + std::memcpy(GetRsaKeyModulus(which), src, std::min(static_cast(size), se::RsaSize)); + } + + void CommitRsaKeyModulus(ImportRsaKey which) { + g_rsa_modulus_committed[which] = true; + } + + bool LoadRsaKey(int slot, ImportRsaKey which) { + /* If the key is still provisional, we can't load it. */ + if (IsRsaKeyProvisional(which)) { + return false; + } + + se::SetRsaKey(slot, GetRsaKeyModulus(which), se::RsaSize, GetRsaKeyPrivateExponent(which), se::RsaSize); + return true; + } + + void LoadProvisionalRsaKey(int slot, ImportRsaKey which) { + se::SetRsaKey(slot, GetRsaKeyModulus(which), se::RsaSize, GetRsaKeyPrivateExponent(which), se::RsaSize); + } + + void 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::min(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::min(0, generation - pkg1::KeyGeneration_4_0_0); + se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_RandomForKeyStorageWrap, GetDeviceMasterKeyStorage(index), se::AesBlockSize); + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_key_storage.hpp b/exosphere2/program/source/secmon_key_storage.hpp new file mode 100644 index 000000000..39d34c9da --- /dev/null +++ b/exosphere2/program/source/secmon_key_storage.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon { + + enum ImportRsaKey { + ImportRsaKey_EsDrmCert = 0, + ImportRsaKey_Lotus = 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 SetMasterKey(int generation, const void *src, size_t size); + void LoadMasterKey(int slot, int generation); + + void SetDeviceMasterKey(int generation, const void *src, size_t size); + void LoadDeviceMasterKey(int slot, int generation); + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_misc.cpp b/exosphere2/program/source/secmon_misc.cpp new file mode 100644 index 000000000..8f7a1c500 --- /dev/null +++ b/exosphere2/program/source/secmon_misc.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_misc.hpp" + +namespace ams::secmon { + + namespace { + + pkg1::BctParameters g_bct_params; + + } + + void SaveBootInfo(const pkg1::SecureMonitorParameters &secmon_params) { + /* Save the BCT parameters. */ + g_bct_params = secmon_params.bct_params; + } + + bool IsRecoveryBoot() { + return (g_bct_params.bootloader_attributes & pkg1::BootloaderAttribute_RecoveryBoot) != 0; + } + + u32 GetRestrictedSmcMask() { + return (g_bct_params.bootloader_attributes & pkg1::BootloaderAttribute_RestrictedSmcMask) >> pkg1::BootloaderAttribute_RestrictedSmcShift; + } + + bool IsJtagEnabled() { + util::BitPack32 dbg_auth; + HW_CPU_GET_DBGAUTHSTATUS_EL1(dbg_auth); + return dbg_auth.Get() == 3; + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_misc.hpp b/exosphere2/program/source/secmon_misc.hpp new file mode 100644 index 000000000..b26641919 --- /dev/null +++ b/exosphere2/program/source/secmon_misc.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::secmon { + + void SaveBootInfo(const pkg1::SecureMonitorParameters &secmon_params); + + bool IsRecoveryBoot(); + + u32 GetRestrictedSmcMask(); + + bool IsJtagEnabled(); + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp new file mode 100644 index 000000000..a468d6dce --- /dev/null +++ b/exosphere2/program/source/secmon_setup.cpp @@ -0,0 +1,750 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_setup.hpp" +#include "secmon_error.hpp" +#include "secmon_cpu_context.hpp" +#include "secmon_interrupt_handler.hpp" + +namespace ams::secmon { + + namespace { + + constexpr inline const uintptr_t TIMER = secmon::MemoryRegionVirtualDeviceTimer.GetAddress(); + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionVirtualDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionVirtualDeviceFlowController.GetAddress(); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionVirtualDevicePmc.GetAddress(); + constexpr inline const uintptr_t MC = secmon::MemoryRegionVirtualDeviceMemoryController.GetAddress(); + + alignas(8) constinit u8 g_se_aes_key_slot_test_vector[se::AesBlockSize] = {}; + + struct Carveout { + uintptr_t address; + size_t size; + }; + + constinit Carveout g_kernel_carveouts[KernelCarveoutCount] = { + { secmon::MemoryRegionDramDefaultKernelCarveout.GetAddress(), secmon::MemoryRegionDramDefaultKernelCarveout.GetSize(), }, + { 0, 0, }, + }; + + constinit bool g_is_cold_boot = true; + + constinit const se::StickyBits ExpectedSeStickyBits = { + .se_security = (1 << 0), /* SE_HARD_SETTING */ + .tzram_security = 0, + .crypto_security_perkey = 0, + .crypto_keytable_access = { + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 0: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 1: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 2: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 3: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 4: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (1 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 5: User keyslot. KEYUSE, UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 6: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 7: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 8: Temp keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 9: SmcTemp keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 10: Wrap1 keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 11: Wrap2 keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 12: DMaster keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Master keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + (0 << 6) | (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (1 << 1) | (0 << 0), /* 14: Unused keyslot. UIVUPDATE, OIVUPDATE, KEYUPDATE enabled. KEYUSE, UIVREAD, OIVREAD, KEYREAD disabled. */ + (0 << 6) | (0 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | (0 << 0), /* 13: Device keyslot. KEYUSE, UIVUPDATE, UIVREAD, OIVUPDATE, OIVREAD, KEYUPDATE, KEYREAD disabled. */ + }, + .rsa_security_perkey = 0, + .rsa_keytable_access = { + (0 << 2) | (1 << 1) | (0 << 0), /* KEYUSE/KEYREAD disabled, KEYUPDATE enabled. */ + (0 << 2) | (1 << 1) | (0 << 0), /* KEYUSE/KEYREAD disabled, KEYUPDATE enabled. */ + }, + }; + + void InitializeConfigurationContext() { + /* Get the global context. */ + auto &ctx = ::ams::secmon::impl::GetConfigurationContext(); + + /* Clear the context to zero. */ + std::memset(std::addressof(ctx), 0, sizeof(ctx)); + + /* If the storage context is valid, we want to copy it to the global context. */ + if (const auto &storage_ctx = *MemoryRegionPhysicalDramMonitorConfiguration.GetPointer(); storage_ctx.IsValid()) { + ctx.secmon_cfg.CopyFrom(storage_ctx); + ctx.emummc_cfg = storage_ctx.emummc_cfg; + } else { + /* If we don't have a valid storage context, we can just use the default one. */ + ctx.secmon_cfg = DefaultSecureMonitorConfiguration; + } + } + + void GenerateSecurityEngineAesKeySlotTestVector(void *dst, size_t size) { + /* Clear the output. */ + AMS_ABORT_UNLESS(size == se::AesBlockSize); + std::memset(dst, 0, se::AesBlockSize); + + /* Ensure output is seen as cleared by the se. */ + hw::FlushDataCache(dst, se::AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Declare a block. */ + alignas(8) u8 empty_block[se::AesBlockSize]; + + /* Iteratively transform an empty block. */ + #define TRANSFORM_WITH_KEY(key) \ + __builtin_memset(empty_block, 0, sizeof(empty_block)); \ + se::SetEncryptedAesKey256(pkg1::AesKeySlot_Temporary, key, empty_block, sizeof(empty_block)); \ + se::DecryptAes128(dst, se::AesBlockSize, pkg1::AesKeySlot_Temporary, dst, se::AesBlockSize) + + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForUserWrap); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForKeyStorageWrap); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Master); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_DeviceMaster); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Device); + + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForUserWrap); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_RandomForKeyStorageWrap); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Master); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_DeviceMaster); + TRANSFORM_WITH_KEY(pkg1::AesKeySlot_Device); + + /* Ensure output is seen correctly by the cpu. */ + hw::FlushDataCache(dst, se::AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Clear the temporary key slot. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_Temporary); + } + + void VerifySecurityEngineStickyBits() { + if (!se::ValidateStickyBits(ExpectedSeStickyBits)) { + SetError(pkg1::ErrorInfo_InvalidSecurityEngineStickyBits); + AMS_ABORT("Invalid sticky bits"); + } + } + + void VerifySecurityEngineAesKeySlotTestVector() { + alignas(8) u8 test_vector[se::AesBlockSize]; + GenerateSecurityEngineAesKeySlotTestVector(test_vector, sizeof(test_vector)); + + AMS_ABORT_UNLESS(crypto::IsSameBytes(g_se_aes_key_slot_test_vector, test_vector, se::AesBlockSize)); + } + + void ClearAesKeySlots() { + /* Clear all non-secure monitor keys. */ + for (int i = 0; i < pkg1::AesKeySlot_SecmonStart; ++i) { + se::ClearAesKeySlot(i); + } + + /* Clear the secure-monitor temporary keys. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_Temporary); + se::ClearAesKeySlot(pkg1::AesKeySlot_Smc); + } + + void ClearRsaKeySlots() { + /* Clear all rsa keyslots. */ + for (int i = 0; i < se::RsaKeySlotCount; ++i) { + se::ClearRsaKeySlot(i); + } + } + + void SetupKernelCarveouts() { + #define MC_ENABLE_CLIENT_ACCESS(INDEX, WHICH) MC_REG_BITS_ENUM(CLIENT_ACCESS##INDEX##_##WHICH, ENABLE) + + constexpr u32 ClientAccess0 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(0, PTCR), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0A), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0AB), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0B), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0BB), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0C), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAY0CB), + MC_ENABLE_CLIENT_ACCESS(0, AFIR), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAYHC), + MC_ENABLE_CLIENT_ACCESS(0, DISPLAYHCB), + MC_ENABLE_CLIENT_ACCESS(0, HDAR), + MC_ENABLE_CLIENT_ACCESS(0, HOST1XDMAR), + MC_ENABLE_CLIENT_ACCESS(0, HOST1XR), + MC_ENABLE_CLIENT_ACCESS(0, NVENCSRD), + MC_ENABLE_CLIENT_ACCESS(0, PPCSAHBDMAR), + MC_ENABLE_CLIENT_ACCESS(0, PPCSAHBSLVR)); + + constexpr u32 ClientAccess1 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(1, MPCORER), + MC_ENABLE_CLIENT_ACCESS(1, NVENCSWR), + MC_ENABLE_CLIENT_ACCESS(1, AFIW), + MC_ENABLE_CLIENT_ACCESS(1, HDAW), + MC_ENABLE_CLIENT_ACCESS(1, HOST1XW), + MC_ENABLE_CLIENT_ACCESS(1, MPCOREW), + MC_ENABLE_CLIENT_ACCESS(1, PPCSAHBDMAW), + MC_ENABLE_CLIENT_ACCESS(1, PPCSAHBSLVW)); + + constexpr u32 ClientAccess2 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTR), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTW), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVR), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVW)); + + constexpr u32 ClientAccess2_100 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTR), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_HOSTW), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVR), + MC_ENABLE_CLIENT_ACCESS(2, XUSB_DEVW), + MC_ENABLE_CLIENT_ACCESS(2, TSECSRD), + MC_ENABLE_CLIENT_ACCESS(2, TSECSWR)); + + constexpr u32 ClientAccess3 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(3, SDMMCRA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAB), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAB), + MC_ENABLE_CLIENT_ACCESS(3, VICSRD), + MC_ENABLE_CLIENT_ACCESS(3, VICSWR), + MC_ENABLE_CLIENT_ACCESS(3, DISPLAYD), + MC_ENABLE_CLIENT_ACCESS(3, APER), + MC_ENABLE_CLIENT_ACCESS(3, APEW), + MC_ENABLE_CLIENT_ACCESS(3, NVJPGSRD), + MC_ENABLE_CLIENT_ACCESS(3, NVJPGSWR)); + + constexpr u32 ClientAccess3_100 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(3, SDMMCRA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCRAB), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAA), + MC_ENABLE_CLIENT_ACCESS(3, SDMMCWAB), + MC_ENABLE_CLIENT_ACCESS(3, VICSRD), + MC_ENABLE_CLIENT_ACCESS(3, VICSWR), + MC_ENABLE_CLIENT_ACCESS(3, DISPLAYD), + MC_ENABLE_CLIENT_ACCESS(3, NVDECSRD), + MC_ENABLE_CLIENT_ACCESS(3, NVDECSWR), + MC_ENABLE_CLIENT_ACCESS(3, APER), + MC_ENABLE_CLIENT_ACCESS(3, APEW), + MC_ENABLE_CLIENT_ACCESS(3, NVJPGSRD), + MC_ENABLE_CLIENT_ACCESS(3, NVJPGSWR)); + + constexpr u32 ClientAccess4 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(4, SESRD), + MC_ENABLE_CLIENT_ACCESS(4, SESWR)); + + constexpr u32 ClientAccess4_800 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(4, SESRD), + MC_ENABLE_CLIENT_ACCESS(4, SESWR), + MC_ENABLE_CLIENT_ACCESS(4, TSECRDB), + MC_ENABLE_CLIENT_ACCESS(4, TSECWRB)); + + + constexpr u32 ClientAccess4_100 = reg::Encode(MC_ENABLE_CLIENT_ACCESS(4, SESRD), + MC_ENABLE_CLIENT_ACCESS(4, SESWR)); + + #undef MC_ENABLE_CLIENT_ACCESS + + constexpr u32 ForceInternalAccess0 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS0_AVPCARM7R, ENABLE)); + constexpr u32 ForceInternalAccess0_100 = 0; + + constexpr u32 ForceInternalAccess1 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS1_AVPCARM7W, ENABLE)); + constexpr u32 ForceInternalAccess1_100 = 0; + + constexpr u32 CarveoutConfig = reg::Encode(MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, ANY_ADDRESS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, TZ_SECURE)); + + constexpr u32 CarveoutConfig_100 = reg::Encode(MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, ANY_ADDRESS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, TZ_SECURE)); + + const u32 target_fw = GetTargetFirmware(); + u32 client_access_2; + u32 client_access_3; + u32 client_access_4; + u32 carveout_config; + if (target_fw >= TargetFirmware_8_1_0) { + client_access_2 = ClientAccess2; + client_access_3 = ClientAccess3; + client_access_4 = ClientAccess4; + carveout_config = CarveoutConfig; + } else if (target_fw >= TargetFirmware_8_0_0) { + client_access_2 = ClientAccess2; + client_access_3 = ClientAccess3; + client_access_4 = ClientAccess4_800; + carveout_config = CarveoutConfig; + } else { + client_access_2 = ClientAccess2_100; + client_access_3 = ClientAccess3_100; + client_access_4 = ClientAccess4_100; + carveout_config = CarveoutConfig_100; + } + + /* Configure carveout 4. */ + reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM, g_kernel_carveouts[0].address >> 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM_HI, g_kernel_carveouts[0].address >> 32); + reg::Write(MC + MC_SECURITY_CARVEOUT4_SIZE_128KB, g_kernel_carveouts[0].size / 128_KB); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0, ClientAccess0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1, ClientAccess1); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2, client_access_2); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3, client_access_3); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4, client_access_4); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0, (target_fw >= TargetFirmware_4_0_0) ? ForceInternalAccess0 : ForceInternalAccess0_100); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1, (target_fw >= TargetFirmware_4_0_0) ? ForceInternalAccess1 : ForceInternalAccess1_100); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CFG0, carveout_config); + + /* Configure carveout 5. */ + reg::Write(MC + MC_SECURITY_CARVEOUT5_BOM, g_kernel_carveouts[0].address >> 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_BOM_HI, g_kernel_carveouts[0].address >> 32); + reg::Write(MC + MC_SECURITY_CARVEOUT5_SIZE_128KB, g_kernel_carveouts[0].size / 128_KB); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0, ClientAccess0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1, ClientAccess1); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2, client_access_2); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3, client_access_3); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4, client_access_4); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT5_CFG0, carveout_config); + } + + void ConfigureSlaveSecurity() { + u32 reg0, reg1, reg2; + if (GetTargetFirmware() > TargetFirmware_1_0_0) { + reg0 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(0, SATA_AUX, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, DTV, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, QSPI, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, SE, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, SATA, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, LA, ENABLE)); + + reg1 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, SPI1, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI2, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI3, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI5, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI6, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, I2C6, ENABLE)); + + reg2 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, SDMMC3, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(2, DDS, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(2, DP2, ENABLE)); + + const auto hw_type = fuse::GetHardwareType(); + + /* Switch Lite can't use HDMI, so set CEC to secure on hoag. */ + if (hw_type == fuse::HardwareType_Hoag) { + reg0 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(0, CEC, ENABLE)); + } + + /* Icosa, Iowa, and Five all set I2C4 to be secure. */ + if (hw_type == fuse::HardwareType_Icosa && hw_type == fuse::HardwareType_Iowa && hw_type == fuse::HardwareType_Five) { + reg1 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, I2C4, ENABLE)); + + } + + /* Hoag additionally sets UART_B to secure. */ + if (hw_type == fuse::HardwareType_Hoag) { + reg1 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, UART_B, ENABLE)); + } + + /* Copper and Calcio lack a lot of hardware, so set the corresponding registers to secure for them. */ + if (hw_type == fuse::HardwareType_Calcio || hw_type == fuse::HardwareType_Copper) { + reg1 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, UART_B, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, UART_C, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI4, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, I2C2, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, I2C3, ENABLE)); + + /* Copper/Calcio have no gamecard reader, and thus set SDMMC2 as secure. */ + reg2 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, SDMMC2, ENABLE)); + } + + /* Mariko hardware types (not Icosa or Copper) additionally set mariko-only mmio (SE2, PKA1, FEK) as secure. */ + if (hw_type != fuse::HardwareType_Icosa && hw_type != fuse::HardwareType_Copper) { + reg2 |= reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, SE2, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(2, PKA1, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(2, FEK, ENABLE)); + } + } else { + reg0 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(0, SATA_AUX, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, DTV, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, QSPI, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, SATA, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(0, LA, ENABLE)); + + reg1 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(1, SPI1, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI2, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI3, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI5, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, SPI6, ENABLE), + SLAVE_SECURITY_REG_BITS_ENUM(1, I2C6, ENABLE)); + + reg2 = reg::Encode(SLAVE_SECURITY_REG_BITS_ENUM(2, DDS, ENABLE), + REG_BITS_VALUE(5, 1, 1), /* Note: Bit 5 is not documented in TRM. */ + REG_BITS_VALUE(4, 1, 1)); /* Note: Bit 4 is not documented in TRM. */ + } + + reg::Write(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, reg0); + reg::Write(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0, reg1); + reg::Write(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0, reg2); + } + + void SetupSecureRegisters() { + /* Configure timers 5-8 and watchdog timers 0-3 as secure. */ + reg::Write(TIMER + TIMER_SHARED_TIMER_SECURE_CFG, TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR5, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR6, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR7, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_TMR8, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT0, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT1, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT2, ENABLE), + TIMER_REG_BITS_ENUM(SHARED_TIMER_SECURE_CFG_WDT3, ENABLE)); + + /* Lock cluster switching, to prevent usage of the A53 cores. */ + reg::Write(FLOW_CTLR + FLOW_CTLR_BPMP_CLUSTER_CONTROL, FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER_LOCK, ENABLE), + FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_CLUSTER_SWITCH_ENABLE, DISABLE), + FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, FAST)); + + /* Enable flow controller debug qualifier for legacy FIQs. */ + reg::Write(FLOW_CTLR + FLOW_CTLR_FLOW_DBG_QUAL, FLOW_REG_BITS_ENUM(FLOW_DBG_QUAL_FIQ2CCPLEX_ENABLE, ENABLE)); + + /* Configure the PMC to disable deep power-down. */ + reg::Write(PMC + APBDEV_PMC_DPD_ENABLE, PMC_REG_BITS_ENUM(DPD_ENABLE_TSC_MULT_EN, DISABLE), + PMC_REG_BITS_ENUM(DPD_ENABLE_ON, DISABLE)); + + /* Configure the video protect region. */ + reg::Write(MC + MC_VIDEO_PROTECT_GPU_OVERRIDE_0, 1); + reg::Write(MC + MC_VIDEO_PROTECT_GPU_OVERRIDE_1, 0); + reg::Write(MC + MC_VIDEO_PROTECT_BOM, 0); + reg::Write(MC + MC_VIDEO_PROTECT_SIZE_MB, 0); + reg::Write(MC + MC_VIDEO_PROTECT_REG_CTRL, MC_REG_BITS_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_ALLOW_TZ_WRITE, DISABLED), + MC_REG_BITS_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_WRITE_ACCESS, DISABLED)); + + /* Configure the SEC carveout. */ + reg::Write(MC + MC_SEC_CARVEOUT_BOM, 0); + reg::Write(MC + MC_SEC_CARVEOUT_SIZE_MB, 0); + reg::Write(MC + MC_SEC_CARVEOUT_REG_CTRL, MC_REG_BITS_ENUM(SEC_CARVEOUT_REG_CTRL_SEC_CARVEOUT_WRITE_ACCESS, DISABLED)); + + /* Configure the MTS carveout. */ + reg::Write(MC + MC_MTS_CARVEOUT_BOM, 0); + reg::Write(MC + MC_MTS_CARVEOUT_SIZE_MB, 0); + reg::Write(MC + MC_MTS_CARVEOUT_ADR_HI, 0); + reg::Write(MC + MC_MTS_CARVEOUT_REG_CTRL, MC_REG_BITS_ENUM(MTS_CARVEOUT_REG_CTRL_MTS_CARVEOUT_WRITE_ACCESS, DISABLED)); + + /* Configure the security carveout. */ + reg::Write(MC + MC_SECURITY_CFG0, MC_REG_BITS_VALUE(SECURITY_CFG0_SECURITY_BOM, 0)); + reg::Write(MC + MC_SECURITY_CFG1, MC_REG_BITS_VALUE(SECURITY_CFG1_SECURITY_SIZE, 0)); + reg::Write(MC + MC_SECURITY_CFG3, MC_REG_BITS_VALUE(SECURITY_CFG3_SECURITY_BOM_HI, 3)); + + /* Configure security carveout 1. */ + reg::Write(MC + MC_SECURITY_CARVEOUT1_BOM, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_BOM_HI, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_SIZE_128KB, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT1_CFG0, MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, UNTRANSLATED_ONLY), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, LOCKBIT_SECURE)); + + /* Security carveout 2 will be configured later by SetupGpuCarveout, after magic values are written to configure gpu/tsec. */ + + /* Configure carveout 3. */ + reg::Write(MC + MC_SECURITY_CARVEOUT3_BOM, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_BOM_HI, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_SIZE_128KB, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2, MC_REG_BITS_ENUM (CLIENT_ACCESS2_GPUSRD, ENABLE), + MC_REG_BITS_ENUM (CLIENT_ACCESS2_GPUSWR, ENABLE)); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4, MC_REG_BITS_ENUM (CLIENT_ACCESS4_GPUSRD2, ENABLE), + MC_REG_BITS_ENUM (CLIENT_ACCESS4_GPUSWR2, ENABLE)); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT3_CFG0, MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 3), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, UNTRANSLATED_ONLY), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, LOCKBIT_SECURE)); + + /* Configure the two kernel carveouts. */ + SetupKernelCarveouts(); + + /* Configure slave register security. */ + ConfigureSlaveSecurity(); + } + + void SetupSmmu() { + /* Turn on SMMU translation for all devices. */ + reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_0, ~0u); + reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_1, ~0u); + reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_2, ~0u); + reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_3, ~0u); + reg::Write(MC + MC_SMMU_TRANSLATION_ENABLE_4, ~0u); + + /* Configure ASIDs 1-3 as secure, and all others as non-secure. */ + reg::Write(MC + MC_SMMU_ASID_SECURITY, MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_1, SECURE), + MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_2, SECURE), + MC_REG_BITS_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_3, SECURE)); + + reg::Write(MC + MC_SMMU_ASID_SECURITY_1, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_2, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_3, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_4, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_5, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_6, 0); + reg::Write(MC + MC_SMMU_ASID_SECURITY_7, 0); + + /* Initialize the PTB registers to zero .*/ + reg::Write(MC + MC_SMMU_PTB_ASID, 0); + reg::Write(MC + MC_SMMU_PTB_DATA, 0); + + /* Configure the TLB and PTC, then read TLB_CONFIG to ensure configuration takes. */ + reg::Write(MC + MC_SMMU_TLB_CONFIG, MC_REG_BITS_ENUM (SMMU_TLB_CONFIG_TLB_HIT_UNDER_MISS, ENABLE), + MC_REG_BITS_ENUM (SMMU_TLB_CONFIG_TLB_ROUND_ROBIN_ARBITRATION, ENABLE), + MC_REG_BITS_VALUE(SMMU_TLB_CONFIG_TLB_ACTIVE_LINES, 0x30)); + + reg::Write(MC + MC_SMMU_PTC_CONFIG, MC_REG_BITS_ENUM (SMMU_PTC_CONFIG_PTC_CACHE_ENABLE, ENABLE), + MC_REG_BITS_VALUE(SMMU_PTC_CONFIG_PTC_REQ_LIMIT, 8), + MC_REG_BITS_VALUE(SMMU_PTC_CONFIG_PTC_INDEX_MAP, 0x3F)); + + reg::Read (MC + MC_SMMU_TLB_CONFIG); + + /* Flush the entire page table cache, and read TLB_CONFIG to ensure the flush takes. */ + reg::Write(MC + MC_SMMU_PTC_FLUSH, 0); + reg::Read (MC + MC_SMMU_TLB_CONFIG); + + /* Flush the entire translation lookaside buffer, and read TLB_CONFIG to ensure the flush takes. */ + reg::Write(MC + MC_SMMU_PTC_FLUSH, 0); + reg::Read (MC + MC_SMMU_TLB_CONFIG); + + /* Enable the SMMU, and read TLB_CONFIG to ensure the enable takes. */ + reg::Write(MC + MC_SMMU_CONFIG, MC_REG_BITS_ENUM (SMMU_CONFIG_SMMU_ENABLE, ENABLE)); + reg::Read (MC + MC_SMMU_TLB_CONFIG); + } + + } + + void Setup1() { + /* Load the global configuration context. */ + InitializeConfigurationContext(); + + /* Initialize uart for logging. */ + log::Initialize(); + + /* Initialize the security engine. */ + se::Initialize(); + + /* Initialize the gic. */ + gic::InitializeCommon(); + } + + void SaveSecurityEngineAesKeySlotTestVector() { + GenerateSecurityEngineAesKeySlotTestVector(g_se_aes_key_slot_test_vector, sizeof(g_se_aes_key_slot_test_vector)); + } + + void SetupSocSecurity() { + /* Set the fuse visibility. */ + clkrst::SetFuseVisibility(true); + + /* Set fuses as only secure-writable. */ + fuse::SetWriteSecureOnly(); + + /* Lockout the fuses. */ + fuse::Lockout(); + + /* Set the security engine to secure mode. */ + se::SetSecure(true); + + /* Verify the security engine's sticky bits. */ + VerifySecurityEngineStickyBits(); + + /* Verify the security engine's Aes slots contain correct contents. */ + VerifySecurityEngineAesKeySlotTestVector(); + + /* Clear aes keyslots. */ + ClearAesKeySlots(); + + /* Clear rsa keyslots. */ + ClearRsaKeySlots(); + + /* Overwrite keys that we want to be random with random contents. */ + se::InitializeRandom(); + se::SetRandomKey(pkg1::AesKeySlot_Temporary); + se::GenerateSrk(); + se::SetRandomKey(pkg1::AesKeySlot_TzramSave); + + /* Initialize pmc secure scratch. */ + pmc::InitializeRandomScratch(); + + /* Setup secure registers. */ + SetupSecureRegisters(); + + /* Setup the smmu. */ + SetupSmmu(); + + /* Clear the cpu reset vector. */ + reg::Write(secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress() + EVP_CPU_RESET_VECTOR, 0); + + /* Configure the SB registers to our start address. */ + constexpr u32 ResetVectorLow = static_cast((PhysicalTzramProgramResetVector >> 0)); + constexpr u32 ResetVectorHigh = static_cast((PhysicalTzramProgramResetVector >> BITSIZEOF(u32))); + + /* Write our reset vector to the secure boot registers. */ + reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_AA64_RESET_LOW, ResetVectorLow); + reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_AA64_RESET_HIGH, ResetVectorHigh); + + /* Disable non-secure writes to the reset vector. */ + reg::Write(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_CSR, SB_REG_BITS_ENUM(CSR_NS_RST_VEC_WR_DIS, DISABLE)); + + /* Read back SB_CSR to make sure our non-secure write disable takes. */ + reg::Read(secmon::MemoryRegionVirtualDeviceSystem.GetAddress() + SB_CSR); + + /* Write our reset vector to scratch registers used by warmboot, and lock those scratch registers. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH34, ResetVectorLow); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH35, ResetVectorHigh); + pmc::LockSecureRegister(pmc::SecureRegister_ResetVector); + + /* Setup the security engine interrupt. */ + constexpr int SecurityEngineInterruptId = 90; + gic::SetPriority (SecurityEngineInterruptId, gic::HighestPriority); + gic::SetInterruptGroup(SecurityEngineInterruptId, 0); + gic::SetEnable (SecurityEngineInterruptId, true); + gic::SetSpiTargetCpu (SecurityEngineInterruptId, (1 << 3)); + gic::SetSpiMode (SecurityEngineInterruptId, gic::InterruptMode_Level); + + /* Setup the activity monitor interrupt. */ + constexpr int ActivityMonitorInterruptId = 77; + gic::SetPriority (ActivityMonitorInterruptId, gic::HighestPriority); + gic::SetInterruptGroup(ActivityMonitorInterruptId, 0); + gic::SetEnable (ActivityMonitorInterruptId, true); + gic::SetSpiTargetCpu (ActivityMonitorInterruptId, (1 << 3)); + gic::SetSpiMode (ActivityMonitorInterruptId, gic::InterruptMode_Level); + + /* If we're coldboot, perform one-time setup. */ + if (g_is_cold_boot) { + /* Register both interrupt handlers. */ + SetInterruptHandler(SecurityEngineInterruptId, se::HandleInterrupt); + SetInterruptHandler(ActivityMonitorInterruptId, actmon::HandleInterrupt); + + /* We're expecting the other cores to come out of reset. */ + for (int i = 1; i < NumCores; ++i) { + SetResetExpected(i, true); + } + + /* We only coldboot once. */ + g_is_cold_boot = false; + } + } + + void SetupSocProtections() { + + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.hpp b/exosphere2/program/source/secmon_setup.hpp new file mode 100644 index 000000000..4113c7905 --- /dev/null +++ b/exosphere2/program/source/secmon_setup.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon { + + constexpr inline u64 MemoryAttributeIndexNormal = 0; + constexpr inline u64 MemoryAttributeIndexDevice = 1; + + constexpr inline int KernelCarveoutCount = 2; + + void SetupCpuMemoryControllersEnableMmu(); + + void SetupSocDmaControllers(); + void SetupSocSecurity(); + void SetupSocProtections(); + + void Setup1(); + + void SaveSecurityEngineAesKeySlotTestVector(); + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup_warm.cpp b/exosphere2/program/source/secmon_setup_warm.cpp new file mode 100644 index 000000000..1f168d89a --- /dev/null +++ b/exosphere2/program/source/secmon_setup_warm.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_setup.hpp" + +namespace ams::secmon { + + namespace setup { + + #include "secmon_cache_impl.inc" + + } + + namespace { + + using namespace ams::mmu; + + void SetupCpuCommonControllers() { + /* Set cpuactlr_el1. */ + { + util::BitPack64 cpuactlr = {}; + cpuactlr.Set(1); + cpuactlr.Set(1); + HW_CPU_SET_CPUACTLR_EL1(cpuactlr); + } + + /* Set cpuectlr_el1. */ + { + util::BitPack64 cpuectlr = {}; + cpuectlr.Set(1); + cpuectlr.Set(3); + cpuectlr.Set(3); + HW_CPU_SET_CPUECTLR_EL1(cpuectlr); + } + + /* Prevent instruction reordering. */ + hw::InstructionSynchronizationBarrier(); + } + + void SetupCpuEl3Controllers() { + /* Set scr_el3. */ + { + util::BitPack32 scr = {}; + scr.Set(1); /* Set EL0/EL1 as Non-Secure. */ + scr.Set(0); /* IRQs are taken in IRQ mode. */ + scr.Set(1); /* FIQs are taken in Monitor mode. */ + scr.Set(1); /* External aborts are taken in Monitor mode. */ + scr.Set(1); /* CPSR.F is non-secure writable. */ + scr.Set(1); /* CPSR.A is non-secure writable. */ + scr.Set(0); /* This bit is not implemented. */ + scr.Set(0); /* Secure Monitor Call is allowed. */ + scr.Set(0); /* Hypervisor Calls are disabled. */ /* TODO: Enable for thermosphere? */ + scr.Set(1); /* Secure mode cannot fetch instructions from non-secure memory. */ + scr.Set(1); /* Reserved bit. N probably sets it because on Cortex A53, this sets kernel as aarch64. */ + scr.Set(0); /* Reserved bit. On Cortex A53, this sets secure registers to EL3 only. */ + scr.Set(0); /* WFI is not trapped. */ + scr.Set(0); /* WFE is not trapped. */ + + HW_CPU_SET_SCR_EL3(scr); + } + + /* Set ttbr0_el3. */ + { + constexpr u64 ttbr0 = MemoryRegionPhysicalTzramL1PageTable.GetAddress(); + HW_CPU_SET_TTBR0_EL3(ttbr0); + } + + /* Set tcr_el3. */ + { + util::BitPack32 tcr = { hw::TcrEl3::Res1 }; + tcr.Set(31); /* Configure TTBR0 addressed size to be 64 GiB */ + tcr.Set(1); /* Configure PTE walks as inner write-back write-allocate cacheable */ + tcr.Set(1); /* Configure PTE walks as outer write-back write-allocate cacheable */ + tcr.Set(3); /* Configure PTE walks as inner shareable */ + tcr.Set(0); /* Set TTBR0_EL3 granule as 4 KiB */ + tcr.Set(1); /* Set the physical addrss size as 36-bit (64 GiB) */ + tcr.Set(0); /* Top byte is not ignored in addrss calculations */ + + HW_CPU_SET_TCR_EL3(tcr); + } + + /* Clear cptr_el3. */ + { + util::BitPack32 cptr = {}; + + cptr.Set(0); /* FP/SIMD instructions don't trap. */ + cptr.Set(0); /* Reserved bit (no trace functionality present). */ + cptr.Set(0); /* Access to cpacr_El1 does not trap. */ + + HW_CPU_SET_CPTR_EL3(cptr); + } + + /* Set mair_el3. */ + { + u64 mair = (MemoryRegionAttributes_Normal << (MemoryRegionAttributeWidth * MemoryAttributeIndexNormal)) | + (MemoryRegionAttributes_Device << (MemoryRegionAttributeWidth * MemoryAttributeIndexDevice)); + HW_CPU_SET_MAIR_EL3(mair); + } + + /* Set vectors. */ + { + constexpr u64 vectors = MemoryRegionVirtualTzramProgramExceptionVectors.GetAddress(); + HW_CPU_SET_VBAR_EL3(vectors); + } + + /* Prevent instruction re-ordering around this point. */ + hw::InstructionSynchronizationBarrier(); + } + + void EnableMmu() { + /* Create sctlr value. */ + util::BitPack32 sctlr = { hw::SctlrEl3::Res1 }; + sctlr.Set(1); /* Globally enable the MMU. */ + sctlr.Set(0); /* Disable alignment fault checking. */ + sctlr.Set(1); /* Globally enable the data and unified caches. */ + sctlr.Set(0); /* Disable stack alignment checking. */ + sctlr.Set(1); /* Globally enable the instruction cache. */ + sctlr.Set(0); /* Do not force writable pages to be ExecuteNever. */ + sctlr.Set(0); /* Exceptions should be little endian. */ + + /* Ensure all writes are done before turning on the mmu. */ + hw::DataSynchronizationBarrierInnerShareable(); + + /* Invalidate the entire tlb. */ + hw::InvalidateEntireTlb(); + + /* Ensure instruction consistency. */ + hw::DataSynchronizationBarrierInnerShareable(); + hw::InstructionSynchronizationBarrier(); + + /* Set sctlr_el3. */ + HW_CPU_SET_SCTLR_EL3(sctlr); + hw::InstructionSynchronizationBarrier(); + } + + } + + 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)); + } + } + +} \ No newline at end of file diff --git a/exosphere2/program/source/secmon_start_virtual.s b/exosphere2/program/source/secmon_start_virtual.s new file mode 100644 index 000000000..e71744dad --- /dev/null +++ b/exosphere2/program/source/secmon_start_virtual.s @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.section .text._ZN3ams6secmon5StartEv, "ax", %progbits +.align 6 +.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, #1 + ldr x20, =0x1F01C0800 + mov sp, x20 + + /* Setup X18 to point to the global context. */ + ldr x18, =0x1F01FA000 + + /* Invoke main. */ + bl _ZN3ams6secmon4MainEv + + /* Set the stack pointer to the core 3 exception stack address. */ + ldr x20, =0x1F01FB000 + mov sp, x20 + + /* TODO: JumpToLowerExceptionLevel */ + 1: b 1b + +.section .text._ZN3ams6secmon25AcquireCommonSmcStackLockEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon25AcquireCommonSmcStackLockEv +_ZN3ams6secmon25AcquireCommonSmcStackLockEv: + /* Get the address of the lock. */ + ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE + + /* 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._ZN3ams6secmon25ReleaseCommonSmcStackLockEv, "ax", %progbits +.align 4 +.global _ZN3ams6secmon25ReleaseCommonSmcStackLockEv +_ZN3ams6secmon25ReleaseCommonSmcStackLockEv: + /* Get the address of the lock. */ + ldr x0, =_ZN3ams6secmon18CommonSmcStackLockE + + /* Release the spinlock. */ + stlr wzr, [x0] + + /* Return. */ + ret + +.section .data._ZN3ams6secmon18CommonSmcStackLockE, "aw", %progbits +.global _ZN3ams6secmon18CommonSmcStackLockE +_ZN3ams6secmon18CommonSmcStackLockE: + /* Define storage for the global common smc stack spinlock. */ + .word 0 diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp new file mode 100644 index 000000000..9283a278c --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_aes.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGenerateAesKek(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcLoadAesKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcComputeAes(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcGenerateSpecificAesKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcComputeCmac(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcLoadPreparedAesKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp new file mode 100644 index 000000000..b0b3c3d95 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_aes.hpp @@ -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 . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGenerateAesKek(const SmcArguments &args); + SmcResult SmcLoadAesKey(const SmcArguments &args); + SmcResult SmcComputeAes(const SmcArguments &args); + SmcResult SmcGenerateSpecificAesKey(const SmcArguments &args); + SmcResult SmcComputeCmac(const SmcArguments &args); + SmcResult SmcLoadPreparedAesKey(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.cpp b/exosphere2/program/source/smc/secmon_smc_carveout.cpp new file mode 100644 index 000000000..ba037d498 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_carveout.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_carveout.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcSetKernelCarveoutRegion(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_carveout.hpp b/exosphere2/program/source/smc/secmon_smc_carveout.hpp new file mode 100644 index 000000000..f3d346d97 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_carveout.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcSetKernelCarveoutRegion(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_common.hpp b/exosphere2/program/source/smc/secmon_smc_common.hpp new file mode 100644 index 000000000..dba7c4af5 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_common.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon::smc { + + enum class SmcResult : u32 { + Success = 0, + NotImplemented = 1, + InvalidArgument = 2, + InProgress = 3, + NoAsyncOperation = 4, + InvalidAsyncOperation = 5, + NotPermitted = 6, + NotInitialized = 7, + + PsciSuccess = 0, + PsciNotSupported = static_cast(-1), + PsciInvalidParameters = static_cast(-2), + PsciDenied = static_cast(-3), + PsciAlreadyOn = static_cast(-4), + }; + + struct SmcArguments { + u64 r[8]; + }; + + constexpr inline int SecurityEngineUserInterruptId = 44; + constexpr inline u64 InvalidAsyncKey = 0; + +} diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp new file mode 100644 index 000000000..39fb28dcb --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_device_unique_data.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcDecryptDeviceUniqueData(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcReencryptDeviceUniqueData(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + /* Legacy APIs. */ + SmcResult SmcDecryptAndImportEsDeviceKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcDecryptAndImportLotusKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp new file mode 100644 index 000000000..9def74026 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp @@ -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 . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcDecryptDeviceUniqueData(const SmcArguments &args); + SmcResult SmcReencryptDeviceUniqueData(const SmcArguments &args); + + /* Legacy APIs. */ + SmcResult SmcDecryptAndImportEsDeviceKey(const SmcArguments &args); + SmcResult SmcDecryptAndImportLotusKey(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_error.cpp b/exosphere2/program/source/smc/secmon_smc_error.cpp new file mode 100644 index 000000000..309a2e832 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_error.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_error.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcShowError(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_error.hpp b/exosphere2/program/source/smc/secmon_smc_error.hpp new file mode 100644 index 000000000..62eff45da --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_error.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcShowError(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_es.cpp b/exosphere2/program/source/smc/secmon_smc_es.cpp new file mode 100644 index 000000000..a707c35f0 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_es.cpp @@ -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 . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_es.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcPrepareEsDeviceUniqueKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcPrepareEsCommonKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_es.hpp b/exosphere2/program/source/smc/secmon_smc_es.hpp new file mode 100644 index 000000000..1aabbc210 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_es.hpp @@ -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 . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + enum EsKeyType { + EsKeyType_TitleKey = 0, + EsKeyType_ArchiveKey = 1, + + EsKeyType_Count = 2, + }; + + SmcResult SmcPrepareEsDeviceUniqueKey(const SmcArguments &args); + SmcResult SmcPrepareEsCommonKey(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp new file mode 100644 index 000000000..75bfa4357 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "../secmon_misc.hpp" +#include "secmon_smc_common.hpp" +#include "secmon_smc_handler.hpp" +#include "secmon_smc_aes.hpp" +#include "secmon_smc_carveout.hpp" +#include "secmon_smc_device_unique_data.hpp" +#include "secmon_smc_error.hpp" +#include "secmon_smc_es.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, SmcPrepareEsCommonKey } + }; + + constinit HandlerInfo g_kern_handlers[] = { + { 0x00000000, Restriction_SafeModeNotAllowed, nullptr }, + { 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu }, + { 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu }, + { 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu }, + { 0xC3000002, 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 }, + { 0xF0000003, Restriction_None, SmcGetEmummcConfig }, + }; + + 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); + } + + /* Check if we're a user SMC. */ + if (type == HandlerType_User) { + /* Nintendo uses OEM SMCs. */ + /* We will assign Atmosphere extension SMCs the TrustedApplication range. */ + if (util::BitPack64{id}.Get() == SmcCallRange_TrustedApp) { + return g_ams_handler_table; + } + + /* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */ + if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) { + InvalidSmcError(id); + } + } + + return g_handler_tables[type]; + } + + const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) { + /* Get and check the index. */ + const auto index = util::BitPack64{id}.Get(); + if (AMS_UNLIKELY(index >= table.count)) { + InvalidSmcError(id); + } + + /* Get and check the handler info. */ + const auto &handler_info = table.entries[index]; + + /* Check that the handler isn't null. */ + if (AMS_UNLIKELY(handler_info.handler == nullptr)) { + InvalidSmcError(id); + } + + /* Check that the handler's id matches. */ + if (AMS_UNLIKELY(handler_info.function_id != id)) { + InvalidSmcError(id); + } + + return handler_info; + } + + bool IsHandlerRestricted(const HandlerInfo &info) { + return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0; + } + + SmcResult InvokeSmcHandler(const HandlerInfo &info, const SmcArguments &args) { + /* Check if the smc is restricted. */ + if (AMS_UNLIKELY(IsHandlerRestricted(info))) { + return SmcResult::NotPermitted; + } + + /* Invoke the smc. */ + return info.handler(args); + } + + } + + void HandleSmc(int type, SmcArguments &args) { + /* Get the table. */ + const auto &table = GetHandlerTable(static_cast(type), args.r[0]); + + /* Get the handler info. */ + const auto &info = GetHandlerInfo(table, args.r[0]); + + /* Set the invocation result. */ + args.r[0] = static_cast(InvokeSmcHandler(info, args)); + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_handler.hpp b/exosphere2/program/source/smc/secmon_smc_handler.hpp new file mode 100644 index 000000000..9c5d4b317 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_handler.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + using SmcHandler = SmcResult (*)(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_info.cpp b/exosphere2/program/source/smc/secmon_smc_info.cpp new file mode 100644 index 000000000..2be199d28 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_info.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_info.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGetConfigUser(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcGetConfigKern(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcSetConfig(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + /* This is an atmosphere extension smc. */ + SmcResult SmcGetEmummcConfig(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_info.hpp b/exosphere2/program/source/smc/secmon_smc_info.hpp new file mode 100644 index 000000000..1d41950ff --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_info.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + enum class ConfigItem : u32 { + /* Standard config items. */ + DisableProgramVerification = 1, + DramId = 2, + SecurityEngineIrqNumber = 3, + FuseVersion = 4, + HardwareType = 5, + IsRetail = 6, + IsRecoveryBoot = 7, + DeviceId = 8, + BootReason = 9, + MemoryMode = 10, + IsDebugMode = 11, + KernelConfiguration = 12, + IsChargerHiZModeEnabled = 13, + IsQuest = 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(const SmcArguments &args); + SmcResult SmcGetConfigKern(const SmcArguments &args); + SmcResult SmcSetConfig(const SmcArguments &args); + + /* This is an atmosphere extension smc. */ + SmcResult SmcGetEmummcConfig(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp new file mode 100644 index 000000000..47f29fc8d --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_memory_access.hpp" + +namespace ams::secmon::smc { + + /* This is an atmosphere extension smc. */ + SmcResult SmcIramCopy(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcWriteAddress(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.hpp b/exosphere2/program/source/smc/secmon_smc_memory_access.hpp new file mode 100644 index 000000000..2b44fe292 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_memory_access.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + /* This is an atmosphere extension smc. */ + SmcResult SmcIramCopy(const SmcArguments &args); + SmcResult SmcWriteAddress(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp new file mode 100644 index 000000000..7434bf217 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -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 . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_power_management.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcPowerOffCpu(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcPowerOnCpu(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcSuspendCpu(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.hpp b/exosphere2/program/source/smc/secmon_smc_power_management.hpp new file mode 100644 index 000000000..7b67d6e78 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_power_management.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcPowerOffCpu(const SmcArguments &args); + SmcResult SmcPowerOnCpu(const SmcArguments &args); + + SmcResult SmcSuspendCpu(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_random.cpp b/exosphere2/program/source/smc/secmon_smc_random.cpp new file mode 100644 index 000000000..283e8d736 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_random.cpp @@ -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 . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_random.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGenerateRandomBytes(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcGenerateRandomBytesNonBlocking(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_random.hpp b/exosphere2/program/source/smc/secmon_smc_random.hpp new file mode 100644 index 000000000..27b20b241 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_random.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGenerateRandomBytes(const SmcArguments &args); + SmcResult SmcGenerateRandomBytesNonBlocking(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.cpp b/exosphere2/program/source/smc/secmon_smc_register_access.cpp new file mode 100644 index 000000000..a656174f3 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_register_access.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_register_access.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcReadWriteRegister(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.hpp b/exosphere2/program/source/smc/secmon_smc_register_access.hpp new file mode 100644 index 000000000..d364b27f8 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_register_access.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcReadWriteRegister(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_result.cpp b/exosphere2/program/source/smc/secmon_smc_result.cpp new file mode 100644 index 000000000..6b17b8e56 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_result.cpp @@ -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 . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_result.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcGetResult(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcGetResultData(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_result.hpp b/exosphere2/program/source/smc/secmon_smc_result.hpp new file mode 100644 index 000000000..9355f22af --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_result.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + using GetResultHandler = SmcResult (*)(void *dst, size_t dst_size); + + u64 BeginAsyncOperation(GetResultHandler handler); + void CancelAsyncOperation(u64 async_key); + void EndAsyncOperation(); + + SmcResult SmcGetResult(const SmcArguments &args); + SmcResult SmcGetResultData(const SmcArguments &args); + +} diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.cpp b/exosphere2/program/source/smc/secmon_smc_rsa.cpp new file mode 100644 index 000000000..78c98e620 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_rsa.cpp @@ -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 . + */ +#include +#include "../secmon_error.hpp" +#include "secmon_smc_rsa.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcModularExponentiate(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + + SmcResult SmcModularExponentiateByStorageKey(const SmcArguments &args) { + /* TODO */ + return SmcResult::NotImplemented; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.hpp b/exosphere2/program/source/smc/secmon_smc_rsa.hpp new file mode 100644 index 000000000..c2dcd5cbe --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_rsa.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + SmcResult SmcModularExponentiate(const SmcArguments &args); + SmcResult SmcModularExponentiateByStorageKey(const SmcArguments &args); + +} diff --git a/exosphere2/program/split_program.py b/exosphere2/program/split_program.py new file mode 100644 index 000000000..b52406eba --- /dev/null +++ b/exosphere2/program/split_program.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +import sys, lz4 +from struct import unpack as up + +def lz4_compress(data): + return lz4.block.compress(data, 'high_compression', store_size=False) + +def split_binary(data): + A, B, START, BOOT_CODE_START, BOOT_CODE_END, PROGRAM_START, C, D = up('= 0x40 + for (fn, fdata) in split_binary(data): + with open('%s/%s' % (argv[2], fn), 'wb') as f: + f.write(fdata) + return 0 + +if __name__ == '__main__': + sys.exit(main(len(sys.argv), sys.argv)) \ No newline at end of file diff --git a/libraries/config/arch/arm/arch.mk b/libraries/config/arch/arm/arch.mk new file mode 100644 index 000000000..f629bb39f --- /dev/null +++ b/libraries/config/arch/arm/arch.mk @@ -0,0 +1,11 @@ +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +include $(DEVKITPRO)/devkitARM/base_rules + +export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM +export ATMOSPHERE_SETTINGS += +export ATMOSPHERE_CFLAGS += +export ATMOSPHERE_CXXFLAGS += +export ATMOSPHERE_ASFLAGS += diff --git a/libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk b/libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk new file mode 100644 index 000000000..d50dd0607 --- /dev/null +++ b/libraries/config/arch/arm/cpu/arm7tdmi/cpu.mk @@ -0,0 +1,5 @@ +export ATMOSPHERE_DEFINES += -DATMOSPHERE_CPU_ARM7TDMI +export ATMOSPHERE_SETTINGS += -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork +export ATMOSPHERE_CFLAGS += +export ATMOSPHERE_CXXFLAGS += +export ATMOSPHERE_ASFLAGS += \ No newline at end of file diff --git a/libraries/config/board/nintendo/nx_bpmp/board.mk b/libraries/config/board/nintendo/nx_bpmp/board.mk new file mode 100644 index 000000000..e267e24d6 --- /dev/null +++ b/libraries/config/board/nintendo/nx_bpmp/board.mk @@ -0,0 +1,5 @@ +export ATMOSPHERE_DEFINES += -DATMOSPHERE_BOARD_NINTENDO_NX -D__BPMP__ +export ATMOSPHERE_SETTINGS += +export ATMOSPHERE_CFLAGS += +export ATMOSPHERE_CXXFLAGS += +export ATMOSPHERE_ASFLAGS += \ No newline at end of file diff --git a/libraries/config/common.mk b/libraries/config/common.mk index 4d68b232e..e3a12dd7b 100644 --- a/libraries/config/common.mk +++ b/libraries/config/common.mk @@ -8,12 +8,13 @@ export ATMOSPHERE_LIBRARIES_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/.. ifeq ($(strip $(ATMOSPHERE_BOARD)),) export ATMOSPHERE_BOARD := nx-hac-001 -endif ifeq ($(strip $(ATMOSPHERE_CPU)),) export ATMOSPHERE_CPU := arm-cortex-a57 endif +endif + export ATMOSPHERE_DEFINES := -DATMOSPHERE export ATMOSPHERE_SETTINGS := -fPIE -g export ATMOSPHERE_CFLAGS := -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv \ @@ -23,6 +24,8 @@ export ATMOSPHERE_ASFLAGS := ifeq ($(ATMOSPHERE_BOARD),nx-hac-001) + +ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57) export ATMOSPHERE_ARCH_DIR := arch/arm64 export ATMOSPHERE_BOARD_DIR := board/nintendo/nx export ATMOSPHERE_OS_DIR := os/horizon @@ -30,6 +33,16 @@ export ATMOSPHERE_OS_DIR := os/horizon export ATMOSPHERE_ARCH_NAME := arm64 export ATMOSPHERE_BOARD_NAME := nintendo_nx export ATMOSPHERE_OS_NAME := horizon +else ifeq ($(ATMOSPHERE_CPU),arm7tdmi) +export ATMOSPHERE_ARCH_DIR := arch/arm +export ATMOSPHERE_BOARD_DIR := board/nintendo/nx_bpmp +export ATMOSPHERE_OS_DIR := os/horizon + +export ATMOSPHERE_ARCH_NAME := arm +export ATMOSPHERE_BOARD_NAME := nintendo_nx +export ATMOSPHERE_OS_NAME := horizon +endif + endif ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57) @@ -37,12 +50,20 @@ export ATMOSPHERE_CPU_DIR := arch/arm64/cpu/cortex_a57 export ATMOSPHERE_CPU_NAME := arm_cortex_a57 endif +ifeq ($(ATMOSPHERE_CPU),arm7tdmi) +export ATMOSPHERE_CPU_DIR := arch/arm/cpu/arm7tdmi +export ATMOSPHERE_CPU_NAME := arm7tdmi +endif + export ATMOSPHERE_ARCH_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_ARCH_DIR) export ATMOSPHERE_BOARD_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_BOARD_DIR) export ATMOSPHERE_OS_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_OS_DIR) export ATMOSPHERE_CPU_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_CPU_DIR) +export ATMOSPHERE_LIBRARY_DIR := lib_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME) +export ATMOSPHERE_BUILD_DIR := build_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME) + include $(ATMOSPHERE_ARCH_MAKE_DIR)/arch.mk include $(ATMOSPHERE_BOARD_MAKE_DIR)/board.mk include $(ATMOSPHERE_OS_MAKE_DIR)/os.mk diff --git a/libraries/config/templates/exosphere.mk b/libraries/config/templates/exosphere.mk new file mode 100644 index 000000000..163626659 --- /dev/null +++ b/libraries/config/templates/exosphere.mk @@ -0,0 +1,46 @@ +#--------------------------------------------------------------------------------- +# pull in common atmosphere configuration +#--------------------------------------------------------------------------------- +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm64) +DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -O2 -Werror -fno-non-call-exceptions +CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) +CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) +else ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm) +DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE +SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions +CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) +CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) +endif + +export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now + +export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \ + -Wl,--wrap,__cxa_throw \ + -Wl,--wrap,__cxa_rethrow \ + -Wl,--wrap,__cxa_allocate_exception \ + -Wl,--wrap,__cxa_free_exception \ + -Wl,--wrap,__cxa_begin_catch \ + -Wl,--wrap,__cxa_end_catch \ + -Wl,--wrap,__cxa_call_unexpected \ + -Wl,--wrap,__cxa_call_terminate \ + -Wl,--wrap,__gxx_personality_v0 \ + -Wl,--wrap,_Unwind_Resume \ + -Wl,--wrap,_ZSt19__throw_logic_errorPKc \ + -Wl,--wrap,_ZSt20__throw_length_errorPKc \ + -Wl,--wrap,_ZNSt11logic_errorC2EPKc + +export LIBS := -lexosphere + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere diff --git a/libraries/libexosphere/Makefile b/libraries/libexosphere/Makefile new file mode 100644 index 000000000..97b463c6b --- /dev/null +++ b/libraries/libexosphere/Makefile @@ -0,0 +1,19 @@ +.PHONY: all arm64 arm clean arm64-clean arm-clean + +all: arm64 arm + +arm64: + $(MAKE) -f arm64.mk + +arm: + $(MAKE) -f arm.mk + +#--------------------------------------------------------------------------------- + +clean: arm64-clean arm-clean + +arm64-clean: + $(MAKE) -f arm64.mk clean + +arm-clean: + $(MAKE) -f arm.mk clean diff --git a/libraries/libexosphere/arm.mk b/libraries/libexosphere/arm.mk new file mode 100644 index 000000000..7a653f53b --- /dev/null +++ b/libraries/libexosphere/arm.mk @@ -0,0 +1,142 @@ +#--------------------------------------------------------------------------------- +# 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))))/../config/common.mk + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE +SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions +CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) +CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) + +SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source) + +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours + +#--------------------------------------------------------------------------------- +# 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 VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ + $(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 GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr))) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I. + +.PHONY: clean all + +#--------------------------------------------------------------------------------- +all: $(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a + +$(ATMOSPHERE_LIBRARY_DIR): + @[ -d $@ ] || mkdir -p $@ + +$(ATMOSPHERE_BUILD_DIR): + @[ -d $@ ] || mkdir -p $@ + +$(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a : $(ATMOSPHERE_LIBRARY_DIR) $(ATMOSPHERE_BUILD_DIR) $(SOURCES) $(INCLUDES) + @$(MAKE) BUILD=$(ATMOSPHERE_BUILD_DIR) OUTPUT=$(CURDIR)/$@ \ + DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \ + --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \ + -f $(CURDIR)/arm.mk + +dist-bin: all + @tar --exclude=*~ -cjf $(TARGET).tar.bz2 include $(ATMOSPHERE_LIBRARY_DIR) + +dist-src: + @tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source arm.mk + +dist: dist-src dist-bin + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2 + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +$(OFILES) : $(GCH_FILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +libc.o: CFLAGS += -fno-builtin + +#--------------------------------------------------------------------------------- +%_bin.h %.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/libraries/libexosphere/arm64.mk b/libraries/libexosphere/arm64.mk new file mode 100644 index 000000000..c9a17e60d --- /dev/null +++ b/libraries/libexosphere/arm64.mk @@ -0,0 +1,143 @@ +#--------------------------------------------------------------------------------- +# 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))))/../config/common.mk + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -O2 -Werror -fno-non-call-exceptions +CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) +CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) + +SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source) + +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours + +#--------------------------------------------------------------------------------- +# 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 VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ + $(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 GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr))) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I. + +.PHONY: clean all + +#--------------------------------------------------------------------------------- +all: $(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a + +$(ATMOSPHERE_LIBRARY_DIR): + @[ -d $@ ] || mkdir -p $@ + +$(ATMOSPHERE_BUILD_DIR): + @[ -d $@ ] || mkdir -p $@ + +$(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a : $(ATMOSPHERE_LIBRARY_DIR) $(ATMOSPHERE_BUILD_DIR) $(SOURCES) $(INCLUDES) + @$(MAKE) BUILD=$(ATMOSPHERE_BUILD_DIR) OUTPUT=$(CURDIR)/$@ \ + BUILD_CFLAGS="-DNDEBUG=1 -O2" \ + DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \ + --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \ + -f $(CURDIR)/arm64.mk + +dist-bin: all + @tar --exclude=*~ -cjf $(TARGET).tar.bz2 include $(ATMOSPHERE_LIBRARY_DIR) + +dist-src: + @tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source arm64.mk + +dist: dist-src dist-bin + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2 + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +$(OFILES) : $(GCH_FILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +libc.o: CFLAGS += -fno-builtin + +#--------------------------------------------------------------------------------- +%_bin.h %.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp new file mode 100644 index 000000000..db9b2cb2d --- /dev/null +++ b/libraries/libexosphere/include/exosphere.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/actmon.hpp b/libraries/libexosphere/include/exosphere/actmon.hpp new file mode 100644 index 000000000..929b32849 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/actmon.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::actmon { + + using InterruptHandler = void(*)(); + + void SetRegisterAddress(uintptr_t address); + + void HandleInterrupt(); + + void StartMonitoringBpmp(InterruptHandler handler); + void StopMonitoringBpmp(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/clkrst.hpp b/libraries/libexosphere/include/exosphere/clkrst.hpp new file mode 100644 index 000000000..c26dfb118 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/clkrst.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::clkrst { + + void SetRegisterAddress(uintptr_t address); + + void SetFuseVisibility(bool visible); + + void EnableUartAClock(); + void EnableUartBClock(); + void EnableUartCClock(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/common.hpp b/libraries/libexosphere/include/exosphere/common.hpp new file mode 100644 index 000000000..c0f4e4b72 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/common.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#if 1 || defined(AMS_BUILD_FOR_AUDITING) +#define EXOSPHERE_BUILD_FOR_AUDITING +#endif + +#if defined(EXOSPHERE_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) +#define EXOSPHERE_BUILD_FOR_DEBUGGING +#endif + +#ifdef EXOSPHERE_BUILD_FOR_DEBUGGING +#define EXOSPHERE_ENABLE_ASSERTIONS +#define EXOSPHERE_ENABLE_DEBUG_PRINT +#endif diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp new file mode 100644 index 000000000..3df44b8a3 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/fuse.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::fuse { + + enum HardwareType { + HardwareType_Icosa = 0, + HardwareType_Copper = 1, + HardwareType_Hoag = 2, + HardwareType_Iowa = 3, + HardwareType_Calcio = 4, + HardwareType_Five = 5, + + HardwareType_Undefined = 0xF, + }; + + enum HardwareState { + HardwareState_Development = 0, + HardwareState_Production = 1, + HardwareState_Undefined = 2, + }; + + void SetRegisterAddress(uintptr_t address); + void SetWriteSecureOnly(); + void Lockout(); + + u32 GetOdmWord(int index); + + HardwareType GetHardwareType(); + HardwareState GetHardwareState(); + pmic::Regulator GetRegulator(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/gic.hpp b/libraries/libexosphere/include/exosphere/gic.hpp new file mode 100644 index 000000000..72e30432c --- /dev/null +++ b/libraries/libexosphere/include/exosphere/gic.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::gic { + + enum InterruptMode { + InterruptMode_Level = 0, + InterruptMode_Edge = 1, + }; + + constexpr inline s32 HighestPriority = 0; + constexpr inline s32 InterruptCount = 224; + + void SetRegisterAddress(uintptr_t distributor_address, uintptr_t cpu_interface_address); + void InitializeCommon(); + void InitializeCoreUnique(); + + void SetPriority(int interrupt_id, int priority); + void SetInterruptGroup(int interrupt_id, int group); + void SetEnable(int interrupt_id, bool enable); + void SetSpiTargetCpu(int interrupt_id, u32 cpu_mask); + void SetSpiMode(int interrupt_id, InterruptMode mode); + + int GetInterruptRequestId(); + void SetEndOfInterrupt(int interrupt_id); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/hw.hpp b/libraries/libexosphere/include/exosphere/hw.hpp new file mode 100644 index 000000000..73c68b050 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/hw.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_ARCH_ARM64) + #include + namespace ams::hw { using namespace ams::hw::arch::arm64; } +#elif defined(ATMOSPHERE_ARCH_ARM) + #include + namespace ams::hw { using namespace ams::hw::arch::arm; } +#else + #error "Unknown architecture for hw!" +#endif \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm.hpp new file mode 100644 index 000000000..344e30726 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::hw::arch::arm { + +#ifdef __BPMP__ + constexpr inline size_t DataCacheLineSize = 0x1; + + ALWAYS_INLINE void DataSynchronizationBarrier() { + /* ... */ + } + + ALWAYS_INLINE void DataSynchronizationBarrierInnerShareable() { + /* ... */ + } + + ALWAYS_INLINE void DataMemoryBarrier() { + /* ... */ + } + + ALWAYS_INLINE void InstructionSynchronizationBarrier() { + /* ... */ + } + + ALWAYS_INLINE void FlushDataCache(const void *ptr, size_t size) { + AMS_UNUSED(ptr); + AMS_UNUSED(size); + /* ... */ + } +#else + #error "Unknown ARM board for ams::hw" +#endif + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64.hpp new file mode 100644 index 000000000..404a3c5b7 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::hw::arch::arm64 { + + ALWAYS_INLINE void DataSynchronizationBarrier() { + __asm__ __volatile__("dsb sy" ::: "memory"); + } + + ALWAYS_INLINE void DataSynchronizationBarrierInnerShareable() { + __asm__ __volatile__("dsb ish" ::: "memory"); + } + + ALWAYS_INLINE void DataMemoryBarrier() { + __asm__ __volatile__("dmb sy" ::: "memory"); + } + + ALWAYS_INLINE void InstructionSynchronizationBarrier() { + __asm__ __volatile__("isb" ::: "memory"); + } + + ALWAYS_INLINE void WaitForInterrupt() { + __asm__ __volatile__("wfi" ::: "memory"); + } + + ALWAYS_INLINE void WaitForEvent() { + __asm__ __volatile__("wfe" ::: "memory"); + } + + ALWAYS_INLINE void SendEvent() { + __asm__ __volatile__("sev" ::: "memory"); + } + + ALWAYS_INLINE int CountLeadingZeros(u64 v) { + u64 z; + __asm__ __volatile__("clz %[z], %[v]" : [z]"=r"(z) : [v]"r"(v)); + return z; + } + + ALWAYS_INLINE int CountLeadingZeros(u32 v) { + u32 z; + __asm__ __volatile__("clz %w[z], %w[v]" : [z]"=r"(z) : [v]"r"(v)); + return z; + } + + ALWAYS_INLINE int GetCurrentCoreId() { + u64 mpidr; + HW_CPU_GET_MPIDR_EL1(mpidr); + return mpidr & 0xFF; + } + +} diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp new file mode 100644 index 000000000..ac534fe14 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64_cache.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::hw::arch::arm64 { + +#if defined(ATMOSPHERE_CPU_ARM_CORTEX_A57) || defined(ATMOSPHERE_CPU_ARM_CORTEX_A53) + constexpr inline size_t InstructionCacheLineSize = 0x40; + constexpr inline size_t DataCacheLineSize = 0x40; + constexpr inline size_t NumPerformanceCounters = 6; +#else + #error "Unknown CPU for cache line sizes" +#endif + + ALWAYS_INLINE void InvalidateEntireTlb() { + __asm__ __volatile__("tlbi alle3is" ::: "memory"); + } + + ALWAYS_INLINE void InvalidateDataCacheLine(void *ptr) { + __asm __volatile__("dc ivac, %[ptr]" :: [ptr]"r"(ptr) : "memory"); + } + + ALWAYS_INLINE void FlushDataCacheLine(void *ptr) { + __asm __volatile__("dc civac, %[ptr]" :: [ptr]"r"(ptr) : "memory"); + } + + ALWAYS_INLINE void InvalidateEntireInstructionCache() { + __asm__ __volatile__("ic iallu" ::: "memory"); + } + + ALWAYS_INLINE void InvalidateTlb(uintptr_t address) { + __asm__ __volatile__("tlbi vae3is, %[address]" :: [address]"r"(address) : "memory"); + } + + ALWAYS_INLINE void InvalidateTlbLastLevel(uintptr_t address) { + __asm__ __volatile__("tlbi vale3is, %[address]" :: [address]"r"(address) : "memory"); + } + + void FlushDataCache(const void *ptr, size_t size); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp b/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp new file mode 100644 index 000000000..637f3c026 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/hw/hw_arm64_system_registers.hpp @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::hw::arch::arm64 { + + #define HW_CPU_GET_SYSREG(name, value) __asm__ __volatile__("mrs %0, " #name "" : "=&r"(value) :: "memory"); + + #define HW_CPU_SET_SYSREG(name, value) __asm__ __volatile__("msr " #name ", %0" :: "r"(value) : "memory", "cc") + + #define HW_CPU_GET_SCTLR_EL3(value) HW_CPU_GET_SYSREG(sctlr_el3, value) + #define HW_CPU_SET_SCTLR_EL3(value) HW_CPU_SET_SYSREG(sctlr_el3, value) + + #define HW_CPU_GET_SCR_EL3(value) HW_CPU_GET_SYSREG(scr_el3, value) + #define HW_CPU_SET_SCR_EL3(value) HW_CPU_SET_SYSREG(scr_el3, value) + + #define HW_CPU_GET_CPTR_EL3(value) HW_CPU_GET_SYSREG(cptr_el3, value) + #define HW_CPU_SET_CPTR_EL3(value) HW_CPU_SET_SYSREG(cptr_el3, value) + + #define HW_CPU_GET_TTBR0_EL3(value) HW_CPU_GET_SYSREG(ttbr0_el3, value) + #define HW_CPU_SET_TTBR0_EL3(value) HW_CPU_SET_SYSREG(ttbr0_el3, value) + + #define HW_CPU_GET_TCR_EL3(value) HW_CPU_GET_SYSREG(tcr_el3, value) + #define HW_CPU_SET_TCR_EL3(value) HW_CPU_SET_SYSREG(tcr_el3, value) + + #define HW_CPU_GET_MAIR_EL3(value) HW_CPU_GET_SYSREG(mair_el3, value) + #define HW_CPU_SET_MAIR_EL3(value) HW_CPU_SET_SYSREG(mair_el3, value) + + #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_CLIDR_EL1(value) HW_CPU_GET_SYSREG(clidr_el1, value) + #define HW_CPU_SET_CLIDR_EL1(value) HW_CPU_SET_SYSREG(clidr_el1, value) + + #define HW_CPU_GET_CCSIDR_EL1(value) HW_CPU_GET_SYSREG(ccsidr_el1, value) + #define HW_CPU_SET_CCSIDR_EL1(value) HW_CPU_SET_SYSREG(ccsidr_el1, value) + + #define HW_CPU_GET_CSSELR_EL1(value) HW_CPU_GET_SYSREG(csselr_el1, value) + #define HW_CPU_SET_CSSELR_EL1(value) HW_CPU_SET_SYSREG(csselr_el1, value) + + #define HW_CPU_GET_CPUACTLR_EL1(value) HW_CPU_GET_SYSREG(s3_1_c15_c2_0, value) + #define HW_CPU_SET_CPUACTLR_EL1(value) HW_CPU_SET_SYSREG(s3_1_c15_c2_0, value) + + #define HW_CPU_GET_CPUECTLR_EL1(value) HW_CPU_GET_SYSREG(s3_1_c15_c2_1, value) + #define HW_CPU_SET_CPUECTLR_EL1(value) HW_CPU_SET_SYSREG(s3_1_c15_c2_1, value) + + #define HW_CPU_GET_DBGAUTHSTATUS_EL1(value) HW_CPU_GET_SYSREG(dbgauthstatus_el1, value) + #define HW_CPU_SET_DBGAUTHSTATUS_EL1(value) HW_CPU_SET_SYSREG(dbgauthstatus_el1, value) + + #define HW_CPU_GET_MPIDR_EL1(value) HW_CPU_GET_SYSREG(mpidr_el1, value) + + #define HW_CPU_GET_OSLAR_EL1(value) HW_CPU_GET_SYSREG(oslar_el1, value) + #define HW_CPU_SET_OSLAR_EL1(value) HW_CPU_SET_SYSREG(oslar_el1, value) + + #define HW_CPU_GET_OSDTRRX_EL1(value) HW_CPU_GET_SYSREG(osdtrrx_el1, value) + #define HW_CPU_SET_OSDTRRX_EL1(value) HW_CPU_SET_SYSREG(osdtrrx_el1, value) + + #define HW_CPU_GET_OSDTRTX_EL1(value) HW_CPU_GET_SYSREG(osdtrtx_el1, value) + #define HW_CPU_SET_OSDTRTX_EL1(value) HW_CPU_SET_SYSREG(osdtrtx_el1, value) + + #define HW_CPU_GET_MDSCR_EL1(value) HW_CPU_GET_SYSREG(mdscr_el1, value) + #define HW_CPU_SET_MDSCR_EL1(value) HW_CPU_SET_SYSREG(mdscr_el1, value) + + #define HW_CPU_GET_OSECCR_EL1(value) HW_CPU_GET_SYSREG(oseccr_el1, value) + #define HW_CPU_SET_OSECCR_EL1(value) HW_CPU_SET_SYSREG(oseccr_el1, value) + + #define HW_CPU_GET_MDCCINT_EL1(value) HW_CPU_GET_SYSREG(mdccint_el1, value) + #define HW_CPU_SET_MDCCINT_EL1(value) HW_CPU_SET_SYSREG(mdccint_el1, value) + + #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_DBGVCR32_EL2(value) HW_CPU_GET_SYSREG(dbgvcr32_el2, value) + #define HW_CPU_SET_DBGVCR32_EL2(value) HW_CPU_SET_SYSREG(dbgvcr32_el2, value) + + #define HW_CPU_GET_SDER32_EL3(value) HW_CPU_GET_SYSREG(sder32_el3, value) + #define HW_CPU_SET_SDER32_EL3(value) HW_CPU_SET_SYSREG(sder32_el3, value) + + #define HW_CPU_GET_MDCR_EL2(value) HW_CPU_GET_SYSREG(mdcr_el2, value) + #define HW_CPU_SET_MDCR_EL2(value) HW_CPU_SET_SYSREG(mdcr_el2, value) + + #define HW_CPU_GET_MDCR_EL3(value) HW_CPU_GET_SYSREG(mdcr_el3, value) + #define HW_CPU_SET_MDCR_EL3(value) HW_CPU_SET_SYSREG(mdcr_el3, value) + + #define HW_CPU_GET_SPSR_EL3(value) HW_CPU_GET_SYSREG(spsr_el3, value) + #define HW_CPU_SET_SPSR_EL3(value) HW_CPU_SET_SYSREG(spsr_el3, value) + + #define HW_CPU_GET_DBGBVR0_EL1(value) HW_CPU_GET_SYSREG(dbgbvr0_el1, value) + #define HW_CPU_SET_DBGBVR0_EL1(value) HW_CPU_SET_SYSREG(dbgbvr0_el1, value) + #define HW_CPU_GET_DBGBCR0_EL1(value) HW_CPU_GET_SYSREG(dbgbcr0_el1, value) + #define HW_CPU_SET_DBGBCR0_EL1(value) HW_CPU_SET_SYSREG(dbgbcr0_el1, value) + #define HW_CPU_GET_DBGBVR1_EL1(value) HW_CPU_GET_SYSREG(dbgbvr1_el1, value) + #define HW_CPU_SET_DBGBVR1_EL1(value) HW_CPU_SET_SYSREG(dbgbvr1_el1, value) + #define HW_CPU_GET_DBGBCR1_EL1(value) HW_CPU_GET_SYSREG(dbgbcr1_el1, value) + #define HW_CPU_SET_DBGBCR1_EL1(value) HW_CPU_SET_SYSREG(dbgbcr1_el1, value) + #define HW_CPU_GET_DBGBVR2_EL1(value) HW_CPU_GET_SYSREG(dbgbvr2_el1, value) + #define HW_CPU_SET_DBGBVR2_EL1(value) HW_CPU_SET_SYSREG(dbgbvr2_el1, value) + #define HW_CPU_GET_DBGBCR2_EL1(value) HW_CPU_GET_SYSREG(dbgbcr2_el1, value) + #define HW_CPU_SET_DBGBCR2_EL1(value) HW_CPU_SET_SYSREG(dbgbcr2_el1, value) + #define HW_CPU_GET_DBGBVR3_EL1(value) HW_CPU_GET_SYSREG(dbgbvr3_el1, value) + #define HW_CPU_SET_DBGBVR3_EL1(value) HW_CPU_SET_SYSREG(dbgbvr3_el1, value) + #define HW_CPU_GET_DBGBCR3_EL1(value) HW_CPU_GET_SYSREG(dbgbcr3_el1, value) + #define HW_CPU_SET_DBGBCR3_EL1(value) HW_CPU_SET_SYSREG(dbgbcr3_el1, value) + #define HW_CPU_GET_DBGBVR4_EL1(value) HW_CPU_GET_SYSREG(dbgbvr4_el1, value) + #define HW_CPU_SET_DBGBVR4_EL1(value) HW_CPU_SET_SYSREG(dbgbvr4_el1, value) + #define HW_CPU_GET_DBGBCR4_EL1(value) HW_CPU_GET_SYSREG(dbgbcr4_el1, value) + #define HW_CPU_SET_DBGBCR4_EL1(value) HW_CPU_SET_SYSREG(dbgbcr4_el1, value) + #define HW_CPU_GET_DBGBVR5_EL1(value) HW_CPU_GET_SYSREG(dbgbvr5_el1, value) + #define HW_CPU_SET_DBGBVR5_EL1(value) HW_CPU_SET_SYSREG(dbgbvr5_el1, value) + #define HW_CPU_GET_DBGBCR5_EL1(value) HW_CPU_GET_SYSREG(dbgbcr5_el1, value) + #define HW_CPU_SET_DBGBCR5_EL1(value) HW_CPU_SET_SYSREG(dbgbcr5_el1, value) + + #define HW_CPU_GET_DBGWVR0_EL1(value) HW_CPU_GET_SYSREG(dbgwvr0_el1, value) + #define HW_CPU_SET_DBGWVR0_EL1(value) HW_CPU_SET_SYSREG(dbgwvr0_el1, value) + #define HW_CPU_GET_DBGWCR0_EL1(value) HW_CPU_GET_SYSREG(dbgwcr0_el1, value) + #define HW_CPU_SET_DBGWCR0_EL1(value) HW_CPU_SET_SYSREG(dbgwcr0_el1, value) + #define HW_CPU_GET_DBGWVR1_EL1(value) HW_CPU_GET_SYSREG(dbgwvr1_el1, value) + #define HW_CPU_SET_DBGWVR1_EL1(value) HW_CPU_SET_SYSREG(dbgwvr1_el1, value) + #define HW_CPU_GET_DBGWCR1_EL1(value) HW_CPU_GET_SYSREG(dbgwcr1_el1, value) + #define HW_CPU_SET_DBGWCR1_EL1(value) HW_CPU_SET_SYSREG(dbgwcr1_el1, value) + #define HW_CPU_GET_DBGWVR2_EL1(value) HW_CPU_GET_SYSREG(dbgwvr2_el1, value) + #define HW_CPU_SET_DBGWVR2_EL1(value) HW_CPU_SET_SYSREG(dbgwvr2_el1, value) + #define HW_CPU_GET_DBGWCR2_EL1(value) HW_CPU_GET_SYSREG(dbgwcr2_el1, value) + #define HW_CPU_SET_DBGWCR2_EL1(value) HW_CPU_SET_SYSREG(dbgwcr2_el1, value) + #define HW_CPU_GET_DBGWVR3_EL1(value) HW_CPU_GET_SYSREG(dbgwvr3_el1, value) + #define HW_CPU_SET_DBGWVR3_EL1(value) HW_CPU_SET_SYSREG(dbgwvr3_el1, value) + #define HW_CPU_GET_DBGWCR3_EL1(value) HW_CPU_GET_SYSREG(dbgwcr3_el1, value) + #define HW_CPU_SET_DBGWCR3_EL1(value) HW_CPU_SET_SYSREG(dbgwcr3_el1, value) + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/system-control-register-el3 */ + struct SctlrEl3 { + using M = util::BitPack32::Field< 0, 1>; + using A = util::BitPack32::Field< 1, 1>; + using C = util::BitPack32::Field< 2, 1>; + using Sa = util::BitPack32::Field< 3, 1>; + using I = util::BitPack32::Field<12, 1>; + using Wxn = util::BitPack32::Field<19, 1>; + using Ee = util::BitPack32::Field<25, 1>; + + static constexpr u32 Res1 = 0x30C50830; + }; + + /* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/BABGIHHJ.html */ + struct ScrEl3 { + using Ns = util::BitPack32::Field< 0, 1>; + using Irq = util::BitPack32::Field< 1, 1>; + using Fiq = util::BitPack32::Field< 2, 1>; + using Ea = util::BitPack32::Field< 3, 1>; + using Fw = util::BitPack32::Field< 4, 1>; + using Aw = util::BitPack32::Field< 5, 1>; + using Net = util::BitPack32::Field< 6, 1>; + using Smd = util::BitPack32::Field< 7, 1>; + using Hce = util::BitPack32::Field< 8, 1>; + using Sif = util::BitPack32::Field< 9, 1>; + using RwCortexA53 = util::BitPack32::Field<10, 1>; + using StCortexA53 = util::BitPack32::Field<11, 1>; + using Twi = util::BitPack32::Field<12, 1>; + using Twe = util::BitPack32::Field<13, 1>; + }; + + /* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488c/CIHDEBIG.html */ + struct CptrEl3 { + using Tfp = util::BitPack32::Field<10, 1>; + using Tta = util::BitPack32::Field<20, 1>; + using Tcpac = util::BitPack32::Field<31, 1>; + }; + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/translation-control-register-el3 */ + struct TcrEl3 { + using T0sz = util::BitPack32::Field< 0, 6>; + using Irgn0 = util::BitPack32::Field< 8, 2>; + using Orgn0 = util::BitPack32::Field<10, 2>; + using Sh0 = util::BitPack32::Field<12, 2>; + using Tg0 = util::BitPack32::Field<14, 2>; + using Ps = util::BitPack32::Field<16, 3>; + using Tbi = util::BitPack32::Field<20, 1>; + + static constexpr u32 Res1 = 0x80800000; + }; + + struct ClidrEl1 { + using Ctype1 = util::BitPack32::Field< 0, 3>; + using Ctype2 = util::BitPack32::Field< 3, 3>; + using Ctype3 = util::BitPack32::Field< 6, 3>; + using Louis = util::BitPack32::Field<21, 3>; + using Loc = util::BitPack32::Field<24, 3>; + using Louu = util::BitPack32::Field<27, 3>; + }; + + struct CcsidrEl1 { + using LineSize = util::BitPack32::Field< 0, 3>; + using Associativity = util::BitPack32::Field< 3, 10>; + using NumSets = util::BitPack32::Field<13, 15>; + using Wa = util::BitPack32::Field<28, 1>; + using Ra = util::BitPack32::Field<29, 1>; + using Wb = util::BitPack32::Field<30, 1>; + using Wt = util::BitPack32::Field<31, 1>; + }; + + struct CsselrEl1 { + using InD = util::BitPack32::Field<0, 1>; + using Level = util::BitPack32::Field<1, 3>; + }; + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/cpu-auxiliary-control-register-el1 */ + struct CpuactlrEl1CortexA57 { + /* TODO: Other bits */ + using NonCacheableStreamingEnhancement = util::BitPack64::Field<24, 1>; + /* TODO: Other bits */ + using DisableLoadPassDmb = util::BitPack64::Field<59, 1>; + using ForceProcessorRcgEnablesActive = util::BitPack64::Field<63, 1>; + }; + + /* https://developer.arm.com/docs/ddi0488/h/system-control/aarch64-register-descriptions/cpu-extended-control-register-el1 */ + struct CpuectlrEl1CortexA57 { + using ProcessorDynamicRetentionControl = util::BitPack64::Field< 0, 2>; + using Smpen = util::BitPack64::Field< 6, 1>; + using L2LoadStoreDataPrefetchDistance = util::BitPack64::Field<32, 2>; + using L2InstructionFetchPrefetchDistance = util::BitPack64::Field<35, 2>; + using DisableTableWalkDescriptorAccessPrefetch = util::BitPack64::Field<38, 1>; + }; + + /* https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/dbgauthstatus_el1 */ + struct DbgAuthStatusEl1 { + using Nsid = util::BitPack32::Field<0, 2>; + using Nsnid = util::BitPack32::Field<2, 2>; + using Sid = util::BitPack32::Field<4, 2>; + using Snid = util::BitPack32::Field<6, 2>; + }; + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/i2c.hpp b/libraries/libexosphere/include/exosphere/i2c.hpp new file mode 100644 index 000000000..fd3b10d2b --- /dev/null +++ b/libraries/libexosphere/include/exosphere/i2c.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::i2c { + + enum Port { + Port_1 = 0, + /* TODO: Support other ports? */ + Port_5 = 4, + + Port_Count = 5, + }; + + void SetRegisterAddress(Port port, uintptr_t address); + + void Initialize(Port port); + + bool Query(void *dst, size_t dst_size, Port port, int address, int r); + bool Send(Port port, int address, int r, const void *src, size_t src_size); + + inline u8 QueryByte(Port port, int address, int r) { + u8 byte; + Query(std::addressof(byte), sizeof(byte), port, address, r); + return byte; + } + + inline void SendByte(Port port, int address, int r, u8 byte) { + Send(port, address, r, std::addressof(byte), sizeof(byte)); + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/log.hpp b/libraries/libexosphere/include/exosphere/log.hpp new file mode 100644 index 000000000..2f1cca8c5 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/log.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::log { + + void Initialize(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/mmu.hpp b/libraries/libexosphere/include/exosphere/mmu.hpp new file mode 100644 index 000000000..c3b888e1d --- /dev/null +++ b/libraries/libexosphere/include/exosphere/mmu.hpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm.hpp new file mode 100644 index 000000000..bedce5af7 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::mmu::arch::arm { + + /* NOTE: mmu is not supported by libexosphere-on-arm. */ + /* However, we want the memory layout to be parseable, so we will include some arm64 mmu definitions. */ + constexpr inline u64 L1EntryShift = 30; + constexpr inline u64 L2EntryShift = 21; + constexpr inline u64 L3EntryShift = 12; + + constexpr inline u64 L1EntrySize = 1_GB; + constexpr inline u64 L2EntrySize = 2_MB; + constexpr inline u64 L3EntrySize = 4_KB; + + constexpr inline u64 PageSize = L3EntrySize; + + constexpr inline u64 L1EntryMask = ((static_cast(1) << (48 - L1EntryShift)) - 1) << L1EntryShift; + constexpr inline u64 L2EntryMask = ((static_cast(1) << (48 - L2EntryShift)) - 1) << L2EntryShift; + constexpr inline u64 L3EntryMask = ((static_cast(1) << (48 - L3EntryShift)) - 1) << L3EntryShift; + + constexpr inline u64 TableEntryMask = L3EntryMask; + + static_assert(L1EntryMask == 0x0000FFFFC0000000ul); + static_assert(L2EntryMask == 0x0000FFFFFFE00000ul); + static_assert(L3EntryMask == 0x0000FFFFFFFFF000ul); + +} diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp new file mode 100644 index 000000000..d18e2b4f1 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.arch.arm64.hpp @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::mmu::arch::arm64 { + + enum PageTableTableAttribute : u64 { + PageTableTableAttribute_None = (0ul << 0), + PageTableTableAttribute_PrivilegedExecuteNever = (1ul << 59), + PageTableTableAttribute_ExecuteNever = (1ul << 60), + PageTableTableAttribute_NonSecure = (1ul << 63), + + PageTableTableAttributes_El3SecureCode = PageTableTableAttribute_None, + PageTableTableAttributes_El3SecureData = PageTableTableAttribute_ExecuteNever, + PageTableTableAttributes_El3NonSecureCode = PageTableTableAttributes_El3SecureCode | PageTableTableAttribute_NonSecure, + PageTableTableAttributes_El3NonSecureData = PageTableTableAttributes_El3SecureData | PageTableTableAttribute_NonSecure, + }; + + enum PageTableMappingAttribute : u64{ + /* Security. */ + PageTableMappingAttribute_NonSecure = (1ul << 5), + + /* El1 Access. */ + PageTableMappingAttribute_El1NotAllowed = (0ul << 6), + PageTableMappingAttribute_El1Allowed = (1ul << 6), + + /* RW Permission. */ + PageTableMappingAttribute_PermissionReadWrite = (0ul << 7), + PageTableMappingAttribute_PermissionReadOnly = (1ul << 7), + + /* Shareability. */ + PageTableMappingAttribute_ShareabilityNonShareable = (0ul << 8), + PageTableMappingAttribute_ShareabiltiyOuterShareable = (2ul << 8), + PageTableMappingAttribute_ShareabilityInnerShareable = (3ul << 8), + + /* Access flag. */ + PageTableMappingAttribute_AccessFlagNotAccessed = (0ul << 10), + PageTableMappingAttribute_AccessFlagAccessed = (1ul << 10), + + /* Global. */ + PageTableMappingAttribute_Global = (0ul << 11), + PageTableMappingAttribute_NonGlobal = (1ul << 11), + + /* Contiguous */ + PageTableMappingAttribute_NonContiguous = (0ul << 52), + PageTableMappingAttribute_Contiguous = (1ul << 52), + + /* Privileged Execute Never */ + PageTableMappingAttribute_PrivilegedExecuteNever = (1ul << 53), + + /* Execute Never */ + PageTableMappingAttribute_ExecuteNever = (1ul << 54), + + + /* Useful definitions. */ + PageTableMappingAttributes_El3SecureRwCode = ( + PageTableMappingAttribute_PermissionReadWrite | + PageTableMappingAttribute_ShareabilityInnerShareable + ), + + PageTableMappingAttributes_El3SecureRoCode = ( + PageTableMappingAttribute_PermissionReadOnly | + PageTableMappingAttribute_ShareabilityInnerShareable + ), + + PageTableMappingAttributes_El3SecureRoData = ( + PageTableMappingAttribute_PermissionReadOnly | + PageTableMappingAttribute_ShareabilityInnerShareable | + PageTableMappingAttribute_ExecuteNever + ), + + PageTableMappingAttributes_El3SecureRwData = ( + PageTableMappingAttribute_PermissionReadWrite | + PageTableMappingAttribute_ShareabilityInnerShareable | + PageTableMappingAttribute_ExecuteNever + ), + + PageTableMappingAttributes_El3NonSecureRwCode = PageTableMappingAttributes_El3SecureRwCode | PageTableMappingAttribute_NonSecure, + PageTableMappingAttributes_El3NonSecureRoCode = PageTableMappingAttributes_El3SecureRoCode | PageTableMappingAttribute_NonSecure, + PageTableMappingAttributes_El3NonSecureRoData = PageTableMappingAttributes_El3SecureRoData | PageTableMappingAttribute_NonSecure, + PageTableMappingAttributes_El3NonSecureRwData = PageTableMappingAttributes_El3SecureRwData | PageTableMappingAttribute_NonSecure, + + + PageTableMappingAttributes_El3SecureDevice = PageTableMappingAttributes_El3SecureRwData, + PageTableMappingAttributes_El3NonSecureDevice = PageTableMappingAttributes_El3NonSecureRwData, + }; + + enum MemoryRegionAttribute : u64 { + MemoryRegionAttribute_Device_nGnRnE = (0ul << 2), + MemoryRegionAttribute_Device_nGnRE = (1ul << 2), + MemoryRegionAttribute_NormalMemory = (2ul << 2), + MemoryRegionAttribute_NormalMemoryNotCacheable = (3ul << 2), + + MemoryRegionAttribute_NormalInnerShift = 0, + MemoryRegionAttribute_NormalOuterShift = 4, + + #define AMS_MRA_DEFINE_NORMAL_ATTR(__NAME__, __VAL__) \ + MemoryRegionAttribute_NormalInner##__NAME__ = (__VAL__ << MemoryRegionAttribute_NormalInnerShift), \ + MemoryRegionAttribute_NormalOuter##__NAME__ = (__VAL__ << MemoryRegionAttribute_NormalOuterShift) + + AMS_MRA_DEFINE_NORMAL_ATTR(NonCacheable, 4), + + AMS_MRA_DEFINE_NORMAL_ATTR(WriteAllocate, (1ul << 0)), + AMS_MRA_DEFINE_NORMAL_ATTR(ReadAllocate, (1ul << 1)), + + AMS_MRA_DEFINE_NORMAL_ATTR(WriteThroughTransient, (0ul << 2)), + AMS_MRA_DEFINE_NORMAL_ATTR(WriteBackTransient, (1ul << 2)), + AMS_MRA_DEFINE_NORMAL_ATTR(WriteThroughNonTransient, (2ul << 2)), + AMS_MRA_DEFINE_NORMAL_ATTR(WriteBackNonTransient, (3ul << 2)), + + #undef AMS_MRA_DEFINE_NORMAL_ATTR + + MemoryRegionAttributes_Normal = ( + MemoryRegionAttribute_NormalInnerReadAllocate | + MemoryRegionAttribute_NormalOuterReadAllocate | + MemoryRegionAttribute_NormalInnerWriteAllocate | + MemoryRegionAttribute_NormalOuterWriteAllocate | + MemoryRegionAttribute_NormalInnerWriteBackNonTransient | + MemoryRegionAttribute_NormalOuterWriteBackNonTransient + ), + + MemoryRegionAttributes_Device = ( + MemoryRegionAttribute_Device_nGnRE + ), + }; + + constexpr inline u64 MemoryRegionAttributeWidth = 8; + + constexpr PageTableMappingAttribute AddMappingAttributeIndex(PageTableMappingAttribute attr, int index) { + return static_cast(attr | (static_cast::type>(index) << 2)); + } + + constexpr inline u64 L1EntryShift = 30; + constexpr inline u64 L2EntryShift = 21; + constexpr inline u64 L3EntryShift = 12; + + constexpr inline u64 L1EntrySize = 1_GB; + constexpr inline u64 L2EntrySize = 2_MB; + constexpr inline u64 L3EntrySize = 4_KB; + + constexpr inline u64 PageSize = L3EntrySize; + + constexpr inline u64 L1EntryMask = ((1ul << (48 - L1EntryShift)) - 1) << L1EntryShift; + constexpr inline u64 L2EntryMask = ((1ul << (48 - L2EntryShift)) - 1) << L2EntryShift; + constexpr inline u64 L3EntryMask = ((1ul << (48 - L3EntryShift)) - 1) << L3EntryShift; + + constexpr inline u64 TableEntryMask = L3EntryMask; + + static_assert(L1EntryMask == 0x0000FFFFC0000000ul); + static_assert(L2EntryMask == 0x0000FFFFFFE00000ul); + static_assert(L3EntryMask == 0x0000FFFFFFFFF000ul); + + constexpr inline u64 TableEntryIndexMask = 0x1FF; + + constexpr inline u64 EntryBlock = 0x1ul; + constexpr inline u64 EntryPage = 0x3ul; + + constexpr u64 MakeTableEntry(u64 address, PageTableTableAttribute attr) { + return address | static_cast(attr) | 0x3ul; + } + + constexpr u64 MakeL1BlockEntry(u64 address, PageTableMappingAttribute attr) { + return address | static_cast(attr) | 0x1ul; + } + + constexpr u64 MakeL2BlockEntry(u64 address, PageTableMappingAttribute attr) { + return address | static_cast(attr) | 0x1ul; + } + + constexpr u64 MakeL3BlockEntry(u64 address, PageTableMappingAttribute attr) { + return address | static_cast(attr) | 0x3ul; + } + + constexpr uintptr_t GetL2Offset(uintptr_t address) { + return address & ((1ul << L2EntryShift) - 1); + } + + constexpr u64 GetL1EntryIndex(uintptr_t address) { + return ((address >> L1EntryShift) & TableEntryIndexMask); + } + + constexpr u64 GetL2EntryIndex(uintptr_t address) { + return ((address >> L2EntryShift) & TableEntryIndexMask); + } + + constexpr u64 GetL3EntryIndex(uintptr_t address) { + return ((address >> L3EntryShift) & TableEntryIndexMask); + } + + constexpr ALWAYS_INLINE void SetTableImpl(u64 *table, u64 index, u64 value) { + /* Ensure (for constexpr validation purposes) that the entry we set is clear. */ + if (table[index]) { + __builtin_unreachable(); + } + + /* Set the value. */ + table[index] = value; + } + + constexpr void SetL1TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) { + SetTableImpl(table, GetL1EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr)); + } + + constexpr void SetL2TableEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, PageTableTableAttribute attr) { + SetTableImpl(table, GetL2EntryIndex(virt_addr), MakeTableEntry(phys_addr & TableEntryMask, attr)); + } + + constexpr void SetL1BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { + const u64 start = GetL1EntryIndex(virt_addr); + const u64 count = (size >> L1EntryShift); + + for (u64 i = 0; i < count; ++i) { + SetTableImpl(table, start + i, MakeL1BlockEntry((phys_addr & L1EntryMask) + (i << L1EntryShift), attr)); + } + } + + constexpr void SetL2BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { + const u64 start = GetL2EntryIndex(virt_addr); + const u64 count = (size >> L2EntryShift); + + for (u64 i = 0; i < count; ++i) { + SetTableImpl(table, start + i, MakeL2BlockEntry((phys_addr & L2EntryMask) + (i << L2EntryShift), attr)); + } + } + + constexpr void SetL3BlockEntry(u64 *table, uintptr_t virt_addr, uintptr_t phys_addr, size_t size, PageTableMappingAttribute attr) { + const u64 start = GetL3EntryIndex(virt_addr); + const u64 count = (size >> L3EntryShift); + + for (u64 i = 0; i < count; ++i) { + SetTableImpl(table, start + i, MakeL3BlockEntry((phys_addr & L3EntryMask) + (i << L3EntryShift), attr)); + } + } + + constexpr void InvalidateL1Entries(u64 *table, uintptr_t virt_addr, size_t size) { + const u64 start = GetL1EntryIndex(virt_addr); + const u64 count = (size >> L1EntryShift); + const u64 end = start + count; + + for (u64 i = start; i < end; ++i) { + table[i] = 0; + } + } + + constexpr void InvalidateL2Entries(u64 *table, uintptr_t virt_addr, size_t size) { + const u64 start = GetL2EntryIndex(virt_addr); + const u64 count = (size >> L2EntryShift); + const u64 end = start + count; + + for (u64 i = start; i < end; ++i) { + table[i] = 0; + } + } + + constexpr void InvalidateL3Entries(u64 *table, uintptr_t virt_addr, size_t size) { + const u64 start = GetL3EntryIndex(virt_addr); + const u64 count = (size >> L3EntryShift); + const u64 end = start + count; + + for (u64 i = start; i < end; ++i) { + table[i] = 0; + } + } + +} diff --git a/libraries/libexosphere/include/exosphere/mmu/mmu_api.hpp b/libraries/libexosphere/include/exosphere/mmu/mmu_api.hpp new file mode 100644 index 000000000..1c0e02801 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/mmu/mmu_api.hpp @@ -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 . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_ARCH_ARM64) + #include +#elif defined(ATMOSPHERE_ARCH_ARM) + #include +#else + #error "Unknown architecture for mmu!" +#endif + +namespace ams::mmu { + + #if defined(ATMOSPHERE_ARCH_ARM64) + using namespace ams::mmu::arch::arm64; + #elif defined(ATMOSPHERE_ARCH_ARM) + using namespace ams::mmu::arch::arm; + #else + #error "Unknown architecture for mmu!" + #endif + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pkg1.hpp b/libraries/libexosphere/include/exosphere/pkg1.hpp new file mode 100644 index 000000000..d7dbad6af --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1.hpp @@ -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 . + */ +#pragma once +#include +#include +#include +#include +#include +#include diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp new file mode 100644 index 000000000..da73f0112 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::pkg1 { + + enum MemorySize { + MemorySize_4GB = 0, + MemorySize_6GB = 1, + MemorySize_8GB = 2, + }; + + enum MemoryArrange { + MemoryArrange_Normal = 1, + MemoryArrange_AppletDev = 2, + MemoryArrange_SystemDev = 2, + }; + + enum MemoryMode { + MemoryMode_SizeShift = 4, + MemoryMode_SizeMask = 0x30, + + MemoryMode_ArrangeMask = 0x0F, + + MemoryMode_Auto = 0x00, + + MemoryMode_4GB = ((MemorySize_4GB << MemoryMode_SizeShift) | (MemoryArrange_Normal)), + MemoryMode_4GBAppletDev = ((MemorySize_4GB << MemoryMode_SizeShift) | (MemoryArrange_AppletDev)), + MemoryMode_4GBSystemDev = ((MemorySize_4GB << MemoryMode_SizeShift) | (MemoryArrange_SystemDev)), + + MemoryMode_6GB = ((MemorySize_6GB << MemoryMode_SizeShift) | (MemoryArrange_Normal)), + MemoryMode_6GBAppletDev = ((MemorySize_6GB << MemoryMode_SizeShift) | (MemoryArrange_AppletDev)), + + MemoryMode_8GB = ((MemorySize_8GB << MemoryMode_SizeShift) | (MemoryArrange_Normal)), + }; + + constexpr ALWAYS_INLINE MemorySize GetMemorySize(MemoryMode mode) { + return static_cast(mode >> MemoryMode_SizeShift); + } + + constexpr ALWAYS_INLINE MemoryArrange GetMemoryArrange(MemoryMode mode) { + return static_cast(mode & MemoryMode_ArrangeMask); + } + + constexpr ALWAYS_INLINE MemoryMode MakeMemoryMode(MemorySize size, MemoryArrange arrange) { + return static_cast((size << MemoryMode_SizeShift) | (arrange)); + } + + struct BootConfigData { + u32 version; + u32 reserved_04; + u32 reserved_08; + u32 reserved_0C; + u8 flags1[0x10]; + u8 flags0[0x10]; + u64 initial_tsc_value; + u8 padding_38[0x200 - 0x38]; + + constexpr bool IsDevelopmentFunctionEnabled() const { + return (this->flags1[0] & (1 << 1)) != 0; + } + + constexpr bool IsSErrorDebugEnabled() const { + return (this->flags1[0] & (1 << 2)) != 0; + } + + constexpr u8 GetKernelFlags0() const { + return this->flags0[1]; + } + + constexpr u8 GetKernelFlags1() const { + return this->flags1[0]; + } + + constexpr MemoryMode GetMemoryMode() const { + return static_cast(this->flags0[3]); + } + + bool IsTscInitialValueValid() const { + return (this->flags0[4] & (1 << 0)) != 0; + } + }; + static_assert(util::is_pod::value); + static_assert(sizeof(BootConfigData) == 0x200); + + struct BootConfigSignedData { + u32 version; + u32 reserved_04; + u8 flags; + u8 reserved_09[0x10 - 9]; + u8 ecid[0x10]; + u8 flags1[0x10]; + u8 flags0[0x10]; + u8 padding_40[0x100 - 0x40]; + + constexpr bool IsPackage2EncryptionDisabled() const { + return (this->flags & (1 << 0)) != 0; + } + + constexpr bool IsPackage2SignatureVerificationDisabled() const { + return (this->flags & (1 << 1)) != 0; + } + + constexpr bool IsProgramVerificationDisabled() const { + return (this->flags1[0] & (1 << 0)) != 0; + } + }; + static_assert(util::is_pod::value); + static_assert(sizeof(BootConfigSignedData) == 0x100); + + struct BootConfig { + BootConfigData data; + u8 signature[0x100]; + BootConfigSignedData signed_data; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(BootConfig) == 0x400); + +} diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp new file mode 100644 index 000000000..93f70df18 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_bootloader_parameters.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::pkg1 { + + enum BootloaderState { + BootloaderState_Start = 0, + BootloaderState_LoadedBootConfig = 1, + BootloaderState_InitializedDram = 2, + BootloaderState_LoadedPackage2 = 3, + BootloaderState_Done = 4, + }; + + enum SecureMonitorState { + SecureMonitorState_Start = 0, + SecureMonitorState_Initialized = 1, + }; + + struct BctParameters { + u32 bootloader_version; + u32 bootloader_start_block; + u32 bootloader_start_page; + u32 bootloader_attributes; + }; + static_assert(util::is_pod::value && sizeof(BctParameters) == 0x10); + + struct SecureMonitorParameters { + u32 bootloader_start_time; + u32 bootloader_end_time; + u32 secmon_start_time; + u32 secmon_end_time; + BctParameters bct_params; + u8 reserved[0xD8]; + u32 bootloader_state; + u32 secmon_state; + u8 reserved2[0x100]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(SecureMonitorParameters) == 0x200); + + static_assert(offsetof(SecureMonitorParameters, bct_params) == 0x10); + static_assert(offsetof(SecureMonitorParameters, bootloader_state) == 0xF8); + static_assert(offsetof(SecureMonitorParameters, secmon_state) == 0xFC); + + enum BootloaderAttribute { + BootloaderAttribute_None = (0u << 0), + BootloaderAttribute_RecoveryBoot = (1u << 0), + + BootloaderAttribute_RestrictedSmcShift = 1, + BootloaderAttribute_RestrictedSmcMask = (0xFu << BootloaderAttribute_RestrictedSmcShift), + }; + +} diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_error_types.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_error_types.hpp new file mode 100644 index 000000000..76331434d --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_error_types.hpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::pkg1 { + + enum ErrorReason { + ErrorReason_None = 0, + ErrorReason_InvalidPackage2Signature = 1, + ErrorReason_InvalidPackage2Meta = 2, + ErrorReason_InvalidPackage2Version = 3, + ErrorReason_InvalidPackage2Payload = 4, + ErrorReason_UnknownSmc = 5, + ErrorReason_UnknownAbort = 6, + ErrorReason_InvalidCoreContext = 7, + ErrorReason_InvalidSecurityEngineStickyBits = 8, + ErrorReason_UnexpectedReset = 9, + + ErrorReason_Exception = 0x10, + + ErrorReason_TransitionToSafeMode = 0x20, + ErrorReason_SecureInitializerReboot = 0x21, + + ErrorReason_SdmmcError = 0x30, + ErrorReason_InvalidDramId = 0x31, + ErrorReason_InvalidPackage2 = 0x32, + ErrorReason_InvalidBct = 0x33, + ErrorReason_InvalidGpt = 0x34, + ErrorReason_FailedToTransitionToSafeMode = 0x35, + ErrorReason_ActivityMonitorInterrupt = 0x36, + + ErrorReason_KernelPanic = 0x40, + }; + + enum ErrorColor { + ErrorColor_Black = 0x000, + + ErrorColor_Red = 0x00F, + ErrorColor_Yellow = 0x0FF, + ErrorColor_Orange = 0x07F, + ErrorColor_Blue = 0xF00, + ErrorColor_LightBlue = 0xFF0, + ErrorColor_Pink = 0xF7F, + ErrorColor_Purple = 0xF0A, + }; + + enum ErrorInfo { + ErrorInfo_ReasonMask = 0xFF, + ErrorInfo_ColorShift = 20, + + #define MAKE_ERROR_INFO(_COLOR_, _DESC_) ((static_cast(ErrorColor_##_COLOR_) << ErrorInfo_ColorShift) | (ErrorReason_##_DESC_)) + + ErrorInfo_None = MAKE_ERROR_INFO(Black, None), + + ErrorInfo_InvalidPackage2Signature = MAKE_ERROR_INFO(Blue, InvalidPackage2Signature), + ErrorInfo_InvalidPackage2Meta = MAKE_ERROR_INFO(Blue, InvalidPackage2Meta), + ErrorInfo_InvalidPackage2Version = MAKE_ERROR_INFO(Blue, InvalidPackage2Version), + ErrorInfo_InvalidPackage2Payload = MAKE_ERROR_INFO(Blue, InvalidPackage2Payload), + + ErrorInfo_UnknownSmc = MAKE_ERROR_INFO(LightBlue, UnknownSmc), + + ErrorInfo_UnknownAbort = MAKE_ERROR_INFO(Yellow, UnknownAbort), + + ErrorInfo_InvalidCoreContext = MAKE_ERROR_INFO(Pink, InvalidCoreContext), + ErrorInfo_InvalidSecurityEngineStickyBits = MAKE_ERROR_INFO(Pink, InvalidSecurityEngineStickyBits), + ErrorInfo_UnexpectedReset = MAKE_ERROR_INFO(Pink, UnexpectedReset), + + ErrorInfo_Exception = MAKE_ERROR_INFO(Orange, Exception), + + ErrorInfo_TransitionToSafeMode = MAKE_ERROR_INFO(Black, TransitionToSafeMode), + ErrorInfo_SecureInitializerReboot = MAKE_ERROR_INFO(Black, SecureInitializerReboot), + + ErrorInfo_SdmmcError = MAKE_ERROR_INFO(Purple, SdmmcError), + ErrorInfo_InvalidDramId = MAKE_ERROR_INFO(Purple, InvalidDramId), + ErrorInfo_InvalidPackage2 = MAKE_ERROR_INFO(Purple, InvalidPackage2), + ErrorInfo_InvalidBct = MAKE_ERROR_INFO(Purple, InvalidBct), + ErrorInfo_InvalidGpt = MAKE_ERROR_INFO(Purple, InvalidGpt), + ErrorInfo_FailedToTransitionToSafeMode = MAKE_ERROR_INFO(Purple, FailedToTransitionToSafeMode), + ErrorInfo_ActivityMonitorInterrupt = MAKE_ERROR_INFO(Purple, ActivityMonitorInterrupt), + + #undef MAKE_ERROR_INFO + }; + + constexpr inline ErrorReason GetErrorReason(u32 info) { + return static_cast(info & ErrorInfo_ReasonMask); + } + + constexpr inline ErrorInfo MakeKernelPanicResetInfo(u32 color) { + return static_cast((color << ErrorInfo_ColorShift) | (ErrorReason_KernelPanic)); + } + + #define PKG1_SECURE_MONITOR_PMC_ERROR_SCRATCH (0x840) + +} diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp new file mode 100644 index 000000000..711f36ba4 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::pkg1 { + + enum KeyGeneration : int { + + KeyGeneration_1_0_0 = 0x00, + KeyGeneration_3_0_0 = 0x01, + KeyGeneration_3_0_1 = 0x02, + KeyGeneration_4_0_0 = 0x03, + KeyGeneration_5_0_0 = 0x04, + KeyGeneration_6_0_0 = 0x05, + KeyGeneration_6_2_0 = 0x06, + KeyGeneration_7_0_0 = 0x07, + KeyGeneration_8_1_0 = 0x08, + KeyGeneration_9_0_0 = 0x09, + KeyGeneration_9_1_0 = 0x0A, + + KeyGeneration_Count, + + KeyGeneration_Current = KeyGeneration_Count - 1, + + KeyGeneration_Min = 0x00, + KeyGeneration_Max = 0x20, + }; + static_assert(KeyGeneration_Count <= KeyGeneration_Max); + + constexpr inline const int OldMasterKeyCount = KeyGeneration_Count - 1; + constexpr inline const int OldDeviceMasterKeyCount = KeyGeneration_Count - KeyGeneration_4_0_0; + +} diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp new file mode 100644 index 000000000..614aa5a38 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::pkg1 { + + enum AesKeySlot { + AesKeySlot_UserStart = 0, + + AesKeySlot_TzramSave = 2, + + AesKeySlot_UserLast = 5, + AesKeySlot_UserEnd = AesKeySlot_UserLast + 1, + + AesKeySlot_SecmonStart = 8, + + AesKeySlot_Temporary = 8, + AesKeySlot_Smc = 9, + AesKeySlot_RandomForUserWrap = 10, + AesKeySlot_RandomForKeyStorageWrap = 11, + AesKeySlot_DeviceMaster = 12, + AesKeySlot_Master = 13, + AesKeySlot_Device = 15, + + AesKeySlot_SecmonEnd = 16, + + /* Used only during boot. */ + AesKeySlot_Tsec = 12, + AesKeySlot_TsecRoot = 13, + AesKeySlot_SecureBoot = 14, + AesKeySlot_SecureStorage = 15, + + AesKeySlot_MasterKek = 13, + AesKeySlot_DeviceMasterKeySourceKek = 14, + }; + + enum RsaKeySlot { + RsaKeySlot_Temporary = 0, + RsaKeySlot_PrivateKey = 1, + }; + +} diff --git a/libraries/libexosphere/include/exosphere/pmc.hpp b/libraries/libexosphere/include/exosphere/pmc.hpp new file mode 100644 index 000000000..d1e2091b4 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pmc.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::pmc { + + enum SecureRegister { + SecureRegister_Other = (1 << 0), + SecureRegister_DramParameters = (1 << 1), + SecureRegister_ResetVector = (1 << 2), + SecureRegister_Carveout = (1 << 3), + SecureRegister_CmacWrite = (1 << 4), + SecureRegister_CmacRead = (1 << 5), + SecureRegister_KeySourceWrite = (1 << 6), + SecureRegister_KeySourceRead = (1 << 7), + SecureRegister_Srk = (1 << 8), + + SecureRegister_CmacReadWrite = SecureRegister_CmacRead | SecureRegister_CmacWrite, + SecureRegister_KeySourceReadWrite = SecureRegister_KeySourceRead | SecureRegister_KeySourceWrite, + }; + + void SetRegisterAddress(uintptr_t address); + + void InitializeRandomScratch(); + + void LockSecureRegister(SecureRegister reg); + + enum class LockState { + Locked = 0, + NotLocked = 1, + PartiallyLocked = 2, + }; + + LockState GetSecureRegisterLockState(SecureRegister reg); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pmic.hpp b/libraries/libexosphere/include/exosphere/pmic.hpp new file mode 100644 index 000000000..46b10372b --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pmic.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::pmic { + + enum Regulator { + /* Erista regulators. */ + Regulator_Erista_Max77621 = 0, /* Device code 0x3A000001 */ + + /* Mariko regulators. */ + Regulator_Mariko_Max77812_A = 1, /* Device code 0x3A000002 */ + Regulator_Mariko_Max77812_B = 2, /* Device code 0x3A000006 */ + }; + + void EnableVddCpu(Regulator regulator); + void DisableVddCpu(Regulator regulator); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/reg.hpp b/libraries/libexosphere/include/exosphere/reg.hpp new file mode 100644 index 000000000..a16d66193 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/reg.hpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::reg { + + using BitsValue = std::tuple; + using BitsMask = std::tuple; + + constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return std::get<0>(v); } + constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return std::get<0>(v); } + constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return std::get<1>(v); } + constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return std::get<1>(v); } + constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return std::get<2>(v); } + + constexpr ALWAYS_INLINE u32 EncodeMask(const BitsMask v) { + return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(v); + } + + constexpr ALWAYS_INLINE u32 EncodeMask(const BitsValue v) { + return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(v); + } + + constexpr ALWAYS_INLINE u32 EncodeValue(const BitsValue v) { + return ((GetValue(v) << GetOffset(v)) & EncodeMask(v)); + } + + template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + constexpr ALWAYS_INLINE u32 Encode(const Values... values) { + return (EncodeValue(values) | ...); + } + + ALWAYS_INLINE void Write(volatile u32 *reg, u32 val) { *reg = val; } + ALWAYS_INLINE void Write(volatile u32 ®, u32 val) { reg = val; } + ALWAYS_INLINE void Write(uintptr_t reg, u32 val) { Write(reinterpret_cast(reg), val); } + + template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void Write(volatile u32 *reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); } + + template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void Write(volatile u32 ®, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); } + + template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void Write(uintptr_t reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); } + + ALWAYS_INLINE u32 Read(volatile u32 *reg) { return *reg; } + ALWAYS_INLINE u32 Read(volatile u32 ®) { return reg; } + ALWAYS_INLINE u32 Read(uintptr_t reg) { return Read(reinterpret_cast(reg)); } + + ALWAYS_INLINE u32 Read(volatile u32 *reg, u32 mask) { return *reg & mask; } + ALWAYS_INLINE u32 Read(volatile u32 ®, u32 mask) { return reg & mask; } + ALWAYS_INLINE u32 Read(uintptr_t reg, u32 mask) { return Read(reinterpret_cast(reg), mask); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE u32 Read(volatile u32 *reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE u32 Read(volatile u32 ®, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE u32 Read(uintptr_t reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); } + + template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE bool HasValue(volatile u32 *reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); } + + template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE bool HasValue(volatile u32 ®, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); } + + template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE bool HasValue(uintptr_t reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); } + + ALWAYS_INLINE void ReadWrite(volatile u32 *reg, u32 val, u32 mask) { *reg = (*reg & (~mask)) | (val & mask); } + ALWAYS_INLINE void ReadWrite(volatile u32 ®, u32 val, u32 mask) { reg = ( reg & (~mask)) | (val & mask); } + ALWAYS_INLINE void ReadWrite(uintptr_t reg, u32 val, u32 mask) { ReadWrite(reinterpret_cast(reg), val, mask); } + + template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void ReadWrite(volatile u32 *reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); } + + template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void ReadWrite(volatile u32 ®, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); } + + template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void ReadWrite(uintptr_t reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); } + + ALWAYS_INLINE void SetBits(volatile u32 *reg, u32 mask) { *reg = *reg | mask; } + ALWAYS_INLINE void SetBits(volatile u32 ®, u32 mask) { reg = reg | mask; } + ALWAYS_INLINE void SetBits(uintptr_t reg, u32 mask) { SetBits(reinterpret_cast(reg), mask); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void SetBits(volatile u32 *reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void SetBits(volatile u32 ®, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void SetBits(uintptr_t reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); } + + ALWAYS_INLINE void ClearBits(volatile u32 *reg, u32 mask) { *reg = *reg & ~mask; } + ALWAYS_INLINE void ClearBits(volatile u32 ®, u32 mask) { reg = reg & ~mask; } + ALWAYS_INLINE void ClearBits(uintptr_t reg, u32 mask) { ClearBits(reinterpret_cast(reg), mask); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void ClearBits(volatile u32 *reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void ClearBits(volatile u32 ®, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void ClearBits(uintptr_t reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); } + + ALWAYS_INLINE void MaskBits(volatile u32 *reg, u32 mask) { *reg = *reg & mask; } + ALWAYS_INLINE void MaskBits(volatile u32 ®, u32 mask) { reg = reg & mask; } + ALWAYS_INLINE void MaskBits(uintptr_t reg, u32 mask) { MaskBits(reinterpret_cast(reg), mask); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void MaskBits(volatile u32 *reg, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void MaskBits(volatile u32 ®, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); } + + template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void MaskBits(uintptr_t reg, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); } + + #define REG_BITS_MASK(OFFSET, WIDTH) ::ams::reg::BitsMask{OFFSET, WIDTH} + #define REG_BITS_VALUE(OFFSET, WIDTH, VALUE) ::ams::reg::BitsValue{OFFSET, WIDTH, VALUE} + + #define REG_NAMED_BITS_MASK(PREFIX, NAME) REG_BITS_MASK(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH) + #define REG_NAMED_BITS_VALUE(PREFIX, NAME, VALUE) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, VALUE) + #define REG_NAMED_BITS_ENUM(PREFIX, NAME, ENUM) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, PREFIX##_##NAME##_##ENUM) + + #define REG_NAMED_BITS_ENUM_SEL(PREFIX, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, (__COND__) ? PREFIX##_##NAME##_##TRUE_ENUM : PREFIX##_##NAME##_##FALSE_ENUM) + + #define REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, __WIDTH__) \ + constexpr inline u32 PREFIX##_##NAME##_OFFSET = __OFFSET__; \ + constexpr inline u32 PREFIX##_##NAME##_WIDTH = __WIDTH__ + + #define REG_DEFINE_NAMED_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE) \ + REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 1); \ + \ + enum PREFIX##_##NAME { \ + PREFIX##_##NAME##_##ZERO = 0, \ + PREFIX##_##NAME##_##ONE = 1, \ + }; + + #define REG_DEFINE_NAMED_TWO_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) \ + REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 2); \ + \ + enum PREFIX##_##NAME { \ + PREFIX##_##NAME##_##ZERO = 0, \ + PREFIX##_##NAME##_##ONE = 1, \ + PREFIX##_##NAME##_##TWO = 2, \ + PREFIX##_##NAME##_##THREE = 3, \ + }; + + #define REG_DEFINE_NAMED_THREE_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) \ + REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 3); \ + \ + enum PREFIX##_##NAME { \ + PREFIX##_##NAME##_##ZERO = 0, \ + PREFIX##_##NAME##_##ONE = 1, \ + PREFIX##_##NAME##_##TWO = 2, \ + PREFIX##_##NAME##_##THREE = 3, \ + PREFIX##_##NAME##_##FOUR = 4, \ + PREFIX##_##NAME##_##FIVE = 5, \ + PREFIX##_##NAME##_##SIX = 6, \ + PREFIX##_##NAME##_##SEVEN = 7, \ + }; + + #define REG_DEFINE_NAMED_FOUR_BIT_ENUM(PREFIX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) \ + REG_DEFINE_NAMED_REG(PREFIX, NAME, __OFFSET__, 4); \ + \ + enum PREFIX##_##NAME { \ + PREFIX##_##NAME##_##ZERO = 0, \ + PREFIX##_##NAME##_##ONE = 1, \ + PREFIX##_##NAME##_##TWO = 2, \ + PREFIX##_##NAME##_##THREE = 3, \ + PREFIX##_##NAME##_##FOUR = 4, \ + PREFIX##_##NAME##_##FIVE = 5, \ + PREFIX##_##NAME##_##SIX = 6, \ + PREFIX##_##NAME##_##SEVEN = 7, \ + PREFIX##_##NAME##_##EIGHT = 8, \ + PREFIX##_##NAME##_##NINE = 9, \ + PREFIX##_##NAME##_##TEN = 10, \ + PREFIX##_##NAME##_##ELEVEN = 11, \ + PREFIX##_##NAME##_##TWELVE = 12, \ + PREFIX##_##NAME##_##THIRTEEN = 13, \ + PREFIX##_##NAME##_##FOURTEEN = 14, \ + PREFIX##_##NAME##_##FIFTEEN = 15, \ + }; + +} diff --git a/libraries/libexosphere/include/exosphere/se.hpp b/libraries/libexosphere/include/exosphere/se.hpp new file mode 100644 index 000000000..ad9abd632 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#include +#include +#include +#include +#include +#include diff --git a/libraries/libexosphere/include/exosphere/se/se_aes.hpp b/libraries/libexosphere/include/exosphere/se/se_aes.hpp new file mode 100644 index 000000000..c2873f115 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_aes.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::se { + + constexpr inline int AesKeySlotCount = 16; + constexpr inline size_t AesBlockSize = crypto::AesEncryptor128::BlockSize; + + void ClearAesKeySlot(int slot); + void LockAesKeySlot(int slot, u32 flags); + + void SetAesKey(int slot, const void *key, size_t key_size); + + void SetEncryptedAesKey128(int dst_slot, int kek_slot, const void *key, size_t key_size); + void SetEncryptedAesKey256(int dst_slot, int kek_slot, const void *key, size_t key_size); + + void EncryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void DecryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/se/se_common.hpp b/libraries/libexosphere/include/exosphere/se/se_common.hpp new file mode 100644 index 000000000..4a87d642b --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_common.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon { + + u8 *GetSecurityEngineEphemeralWorkBlock(); + +} + +namespace ams::se { + + using DoneHandler = void(*)(); + + enum KeySlotLockFlags { + KeySlotLockFlags_None = 0, + KeySlotLockFlags_KeyRead = (1u << 0), + KeySlotLockFlags_KeyWrite = (1u << 1), + KeySlotLockFlags_OriginalIvRead = (1u << 2), + KeySlotLockFlags_OriginalIvWrite = (1u << 3), + KeySlotLockFlags_UpdatedIvRead = (1u << 4), + KeySlotLockFlags_UpdatedIvWrite = (1u << 5), + KeySlotLockFlags_KeyUse = (1u << 6), + KeySlotLockFlags_DstKeyTableOnly = (1u << 7), + KeySlotLockFlags_PerKey = (1u << 8), + + KeySlotLockFlags_AllReadLock = (KeySlotLockFlags_KeyRead | KeySlotLockFlags_OriginalIvRead | KeySlotLockFlags_UpdatedIvRead), + KeySlotLockFlags_AllLockKek = 0x1FF, + KeySlotLockFlags_AllLockKey = (KeySlotLockFlags_AllLockKek & ~KeySlotLockFlags_DstKeyTableOnly), + + KeySlotLockFlags_EristaMask = 0x7F, + KeySlotLockFlags_MarikoMask = 0xFF, + }; + + ALWAYS_INLINE u8 *GetEphemeralWorkBlock() { + return ::ams::secmon::GetSecurityEngineEphemeralWorkBlock(); + } + +} diff --git a/libraries/libexosphere/include/exosphere/se/se_management.hpp b/libraries/libexosphere/include/exosphere/se/se_management.hpp new file mode 100644 index 000000000..f5a8e6bea --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_management.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::se { + + void SetRegisterAddress(uintptr_t address); + + void Initialize(); + + void SetSecure(bool secure); + void SetTzramSecure(); + void SetPerKeySecure(); + + void Lockout(); + + void HandleInterrupt(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/se/se_rng.hpp b/libraries/libexosphere/include/exosphere/se/se_rng.hpp new file mode 100644 index 000000000..3fa339363 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_rng.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::se { + + void InitializeRandom(); + + void GenerateRandomBytes(void *dst, size_t size); + void SetRandomKey(int slot); + + void GenerateSrk(); + +} diff --git a/libraries/libexosphere/include/exosphere/se/se_rsa.hpp b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp new file mode 100644 index 000000000..e16b99c66 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::se { + + constexpr inline int RsaKeySlotCount = 2; + constexpr inline int RsaSize = 0x100; + + void ClearRsaKeySlot(int slot); + void LockRsaKeySlot(int slot, u32 flags); + + void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size); + +} diff --git a/libraries/libexosphere/include/exosphere/se/se_suspend.hpp b/libraries/libexosphere/include/exosphere/se/se_suspend.hpp new file mode 100644 index 000000000..9f16924b7 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_suspend.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::se { + + /* 256-bit AES keyslots are two 128-bit keys. */ + constexpr inline int AesKeySlotPartCount = 2; + + /* RSA keys are both a modulus and an exponent. */ + constexpr inline int RsaKeySlotPartCount = 2; + + constexpr inline size_t StickyBitContextSize = 2 * AesBlockSize; + + struct Context { + u8 random[AesBlockSize]; + u8 sticky_bits[StickyBitContextSize / AesBlockSize][AesBlockSize]; + u8 aes_key[AesKeySlotCount][AesKeySlotPartCount][AesBlockSize]; + u8 aes_oiv[AesKeySlotCount][AesBlockSize]; + u8 aes_uiv[AesKeySlotCount][AesBlockSize]; + u8 rsa_key[RsaKeySlotCount][RsaKeySlotPartCount][RsaSize / AesBlockSize][AesBlockSize]; + u8 fixed_pattern[AesBlockSize]; + }; + static_assert(sizeof(Context) == 0x840); + static_assert(util::is_pod::value); + + struct StickyBits { + u8 se_security; + u8 tzram_security; + u16 crypto_security_perkey; + u8 crypto_keytable_access[AesKeySlotCount]; + u8 rsa_security_perkey; + u8 rsa_keytable_access[RsaKeySlotCount]; + }; + static_assert(util::is_pod::value); + + bool ValidateStickyBits(const StickyBits &bits); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/secmon.hpp b/libraries/libexosphere/include/exosphere/secmon.hpp new file mode 100644 index 000000000..1b476e0f6 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon.hpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp new file mode 100644 index 000000000..7f094a14b --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::secmon { + + struct ConfigurationContext { + union { + SecureMonitorConfiguration secmon_cfg; + u8 _raw_exosphere_config[0x80]; + }; + union { + EmummcConfiguration emummc_cfg; + u8 _raw_emummc_config[0x120]; + }; + union { + u8 _misc_data[0x400 - sizeof(_raw_exosphere_config) - sizeof(_raw_emummc_config)]; + }; + u8 sealed_device_keys[pkg1::KeyGeneration_Max][se::AesBlockSize]; + u8 sealed_master_keys[pkg1::KeyGeneration_Max][se::AesBlockSize]; + pkg1::BootConfig boot_config; + u8 rsa_private_exponents[4][se::RsaSize]; + }; + static_assert(sizeof(ConfigurationContext) == 0x1000); + static_assert(util::is_pod::value); + static_assert(offsetof(ConfigurationContext, sealed_device_keys) == 0x400); + + namespace impl { + + ALWAYS_INLINE uintptr_t GetConfigurationContextAddress() { + register uintptr_t x18 asm("x18"); + __asm__ __volatile__("" : [x18]"=r"(x18)); + return x18; + } + + ALWAYS_INLINE ConfigurationContext &GetConfigurationContext() { + return *reinterpret_cast(GetConfigurationContextAddress()); + } + + ALWAYS_INLINE u8 *GetMasterKeyStorage(int generation) { + return GetConfigurationContext().sealed_master_keys[generation]; + } + + ALWAYS_INLINE u8 *GetDeviceMasterKeyStorage(int generation) { + return GetConfigurationContext().sealed_device_keys[generation]; + } + + ALWAYS_INLINE u8 *GetRsaPrivateExponentStorage(int which) { + return GetConfigurationContext().rsa_private_exponents[which]; + } + + ALWAYS_INLINE void SetKeyGeneration(int generation) { + GetConfigurationContext().secmon_cfg.key_generation = generation; + } + + } + + ALWAYS_INLINE const ConfigurationContext &GetConfigurationContext() { + return *reinterpret_cast(impl::GetConfigurationContextAddress()); + } + + ALWAYS_INLINE const SecureMonitorConfiguration &GetSecmonConfiguration() { + return GetConfigurationContext().secmon_cfg; + } + + ALWAYS_INLINE const EmummcConfiguration &GetEmummcConfiguration() { + return GetConfigurationContext().emummc_cfg; + } + + ALWAYS_INLINE const pkg1::BootConfig &GetBootConfig() { + return GetConfigurationContext().boot_config; + } + + ALWAYS_INLINE ams::TargetFirmware GetTargetFirmware() { + return GetSecmonConfiguration().GetTargetFirmware(); + } + + ALWAYS_INLINE int GetKeyGeneration() { + return GetSecmonConfiguration().GetKeyGeneration(); + } + +} diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.hpp new file mode 100644 index 000000000..c66816d5c --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_ARCH_ARM64) + #include +#elif defined(ATMOSPHERE_ARCH_ARM) + /* Nothing to include. */ +#else + #error "Unknown architecture for secmon::ConfigurationContext.hpp" +#endif diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp new file mode 100644 index 000000000..044420e8c --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_emummc_context.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon { + + enum EmummcType : u32 { + EmummcType_None = 0, + EmummcType_Partition = 1, + EmummcType_File = 2, + }; + + enum EmummcMmc { + EmummcMmc_Nand = 0, + EmummcMmc_Sd = 1, + EmummcMmc_Gc = 2, + }; + + constexpr inline size_t EmummcFilePathLengthMax = 0x80; + + struct EmummcFilePath { + char str[EmummcFilePathLengthMax]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(EmummcFilePath) == EmummcFilePathLengthMax); + + struct EmummcBaseConfiguration { + static constexpr u32 Magic = util::FourCC<'E','F','S','0'>::Code; + + u32 magic; + EmummcType type; + u32 id; + u32 fs_version; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(EmummcBaseConfiguration) == 0x10); + + struct EmummcPartitionConfiguration { + u64 start_sector; + }; + static_assert(util::is_pod::value); + + struct EmummcFileConfiguration { + EmummcFilePath path; + }; + static_assert(util::is_pod::value); + + struct EmummcConfiguration { + EmummcBaseConfiguration base_cfg; + union { + EmummcPartitionConfiguration partition_cfg; + EmummcFileConfiguration file_cfg; + }; + EmummcFilePath emu_dir_path; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(EmummcConfiguration) <= 0x200); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp new file mode 100644 index 000000000..c40368a3c --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::secmon { + + using Address = u64; + + struct MemoryRegion { + Address start_address; + Address end_address; + + constexpr MemoryRegion(Address address, size_t size) : start_address(address), end_address(address + size) { + if (end_address < start_address) { + __builtin_unreachable(); + } + } + + constexpr Address GetStartAddress() const { + return this->start_address; + } + + constexpr Address GetAddress() const { + return this->GetStartAddress(); + } + + constexpr Address GetEndAddress() const { + return this->end_address; + } + + constexpr Address GetLastAddress() const { + return this->end_address - 1; + } + + constexpr size_t GetSize() const { + return this->end_address - this->start_address; + } + + constexpr bool Contains(Address address, size_t size) const { + return this->start_address <= address && (address + size - 1) <= this->GetLastAddress(); + } + + constexpr bool Contains(const MemoryRegion &rhs) const { + return this->Contains(rhs.GetStartAddress(), rhs.GetSize()); + } + + template requires (std::is_same::value || util::is_pod::value) + ALWAYS_INLINE T *GetPointer() const { + return reinterpret_cast(this->GetAddress()); + } + + template requires (std::is_same::value || util::is_pod::value) + ALWAYS_INLINE T *GetEndPointer() const { + return reinterpret_cast(this->GetEndAddress()); + } + }; + + constexpr inline const MemoryRegion MemoryRegionVirtual = MemoryRegion(UINT64_C(0x1F0000000), 2_MB); + constexpr inline const MemoryRegion MemoryRegionPhysical = MemoryRegion(UINT64_C( 0x40000000), 1_GB); + constexpr inline const MemoryRegion MemoryRegionDram = MemoryRegion(UINT64_C( 0x80000000), 2_GB); + + constexpr inline const MemoryRegion MemoryRegionDramDefaultKernelCarveout = MemoryRegion(UINT64_C(0x80060000), UINT64_C(0x1FFE0000)); + static_assert(MemoryRegionDram.Contains(MemoryRegionDramDefaultKernelCarveout)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalIram = MemoryRegion(UINT64_C(0x40000000), 0x40000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzram = MemoryRegion(UINT64_C(0x7C010000), 0x10000); + static_assert(MemoryRegionPhysical.Contains(MemoryRegionPhysicalIram)); + static_assert(MemoryRegionPhysical.Contains(MemoryRegionPhysicalTzram)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramVolatile(UINT64_C(0x7C010000), 0x2000); + static_assert(MemoryRegionPhysicalTzram.Contains(MemoryRegionPhysicalTzramVolatile)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramNonVolatile(UINT64_C(0x7C012000), 0xE000); + static_assert(MemoryRegionPhysicalTzram.Contains(MemoryRegionPhysicalTzramNonVolatile)); + + static_assert(MemoryRegionPhysicalTzram.GetSize() == MemoryRegionPhysicalTzramNonVolatile.GetSize() + MemoryRegionPhysicalTzramVolatile.GetSize()); + + constexpr inline const MemoryRegion MemoryRegionVirtualL1 = MemoryRegion(util::AlignDown(MemoryRegionVirtual.GetAddress(), mmu::L1EntrySize), mmu::L1EntrySize); + constexpr inline const MemoryRegion MemoryRegionPhysicalL1 = MemoryRegion(util::AlignDown(MemoryRegionPhysical.GetAddress(), mmu::L1EntrySize), mmu::L1EntrySize); + static_assert(MemoryRegionVirtualL1.Contains(MemoryRegionVirtual)); + static_assert(MemoryRegionPhysicalL1.Contains(MemoryRegionPhysical)); + + constexpr inline const MemoryRegion MemoryRegionVirtualL2 = MemoryRegion(util::AlignDown(MemoryRegionVirtual.GetAddress(), mmu::L2EntrySize), mmu::L2EntrySize); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramL2 = MemoryRegion(util::AlignDown(MemoryRegionPhysicalIram.GetAddress(), mmu::L2EntrySize), mmu::L2EntrySize); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramL2 = MemoryRegion(util::AlignDown(MemoryRegionPhysicalTzram.GetAddress(), mmu::L2EntrySize), mmu::L2EntrySize); + static_assert(MemoryRegionVirtualL2.Contains(MemoryRegionVirtual)); + static_assert(MemoryRegionPhysicalIramL2.Contains(MemoryRegionPhysicalIram)); + static_assert(MemoryRegionPhysicalTzramL2.Contains(MemoryRegionPhysicalTzram)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCode = MemoryRegion(UINT64_C(0x40020000), 0x20000); + static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramBootCode)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDevice = MemoryRegion(UINT64_C(0x1F0040000), UINT64_C(0x40000)); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDevice)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceEmpty = MemoryRegion(MemoryRegionVirtualDevice.GetStartAddress(), 0); + + #define AMS_SECMON_FOREACH_DEVICE_REGION(HANDLER, ...) \ + HANDLER(GicDistributor, Empty, UINT64_C(0x50041000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(GicCpuInterface, GicDistributor, UINT64_C(0x50042000), UINT64_C(0x2000), true, ## __VA_ARGS__) \ + HANDLER(Uart, GicCpuInterface, UINT64_C(0x70006000), UINT64_C(0x1000), false, ## __VA_ARGS__) \ + HANDLER(ClkRst, Uart, UINT64_C(0x60006000), UINT64_C(0x1000), false, ## __VA_ARGS__) \ + HANDLER(RtcPmc, ClkRst, UINT64_C(0x7000E000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(Timer, RtcPmc, UINT64_C(0x60005000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(System, Timer, UINT64_C(0x6000C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(SecurityEngine, System, UINT64_C(0x70012000), UINT64_C(0x2000), true, ## __VA_ARGS__) \ + HANDLER(SecurityEngine2, SecurityEngine, UINT64_C(0x70412000), UINT64_C(0x2000), true, ## __VA_ARGS__) \ + HANDLER(SysCtr0, SecurityEngine2, UINT64_C(0x700F0000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(MemoryController, SysCtr0, UINT64_C(0x70019000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(FuseKFuse, MemoryController, UINT64_C(0x7000F000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(ApbMisc, FuseKFuse, UINT64_C(0x70000000), UINT64_C(0x4000), true, ## __VA_ARGS__) \ + HANDLER(FlowController, ApbMisc, UINT64_C(0x60007000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(BootloaderParams, FlowController, UINT64_C(0x40000000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(I2c5, BootloaderParams, UINT64_C(0x7000D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + HANDLER(Gpio, I2c5, UINT64_C(0x6000D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \ + 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__) + + #define DEFINE_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \ + constexpr inline const MemoryRegion MemoryRegionVirtualDevice##_NAME_ = MemoryRegion(MemoryRegionVirtualDevice##_PREV_.GetEndAddress() + 0x1000, _SIZE_); \ + constexpr inline const MemoryRegion MemoryRegionPhysicalDevice##_NAME_ = MemoryRegion(_ADDRESS_, _SIZE_); \ + static_assert(MemoryRegionVirtualDevice.Contains(MemoryRegionVirtualDevice##_NAME_)); \ + static_assert(MemoryRegionPhysical.Contains(MemoryRegionPhysicalDevice##_NAME_)); + + AMS_SECMON_FOREACH_DEVICE_REGION(DEFINE_DEVICE_REGION) + + #undef DEFINE_DEVICE_REGION + + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceFuses = MemoryRegion(MemoryRegionVirtualDeviceFuseKFuse.GetAddress() + 0x800, 0x400); + constexpr inline const MemoryRegion MemoryRegionPhysicalDeviceFuses = MemoryRegion(MemoryRegionPhysicalDeviceFuseKFuse.GetAddress() + 0x800, 0x400); + static_assert(MemoryRegionVirtualDeviceFuseKFuse.Contains(MemoryRegionVirtualDeviceFuses)); + static_assert(MemoryRegionPhysicalDeviceFuseKFuse.Contains(MemoryRegionPhysicalDeviceFuses)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceActivityMonitor = MemoryRegion(MemoryRegionVirtualDeviceSystem.GetAddress() + 0x800, 0x400); + constexpr inline const MemoryRegion MemoryRegionPhysicalDeviceActivityMonitor = MemoryRegion(MemoryRegionPhysicalDeviceSystem.GetAddress() + 0x800, 0x400); + static_assert(MemoryRegionVirtualDeviceSystem.Contains(MemoryRegionVirtualDeviceActivityMonitor)); + static_assert(MemoryRegionPhysicalDeviceSystem.Contains(MemoryRegionPhysicalDeviceActivityMonitor)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceUartA = MemoryRegion(MemoryRegionVirtualDeviceUart.GetAddress() + 0x000, 0x040); + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceUartB = MemoryRegion(MemoryRegionVirtualDeviceUart.GetAddress() + 0x040, 0x040); + constexpr inline const MemoryRegion MemoryRegionVirtualDeviceUartC = MemoryRegion(MemoryRegionVirtualDeviceUart.GetAddress() + 0x200, 0x100); + static_assert(MemoryRegionVirtualDeviceUart.Contains(MemoryRegionVirtualDeviceUartA)); + static_assert(MemoryRegionVirtualDeviceUart.Contains(MemoryRegionVirtualDeviceUartB)); + static_assert(MemoryRegionVirtualDeviceUart.Contains(MemoryRegionVirtualDeviceUartC)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalDeviceUartA = MemoryRegion(MemoryRegionPhysicalDeviceUart.GetAddress() + 0x000, 0x040); + constexpr inline const MemoryRegion MemoryRegionPhysicalDeviceUartB = MemoryRegion(MemoryRegionPhysicalDeviceUart.GetAddress() + 0x040, 0x040); + constexpr inline const MemoryRegion MemoryRegionPhysicalDeviceUartC = MemoryRegion(MemoryRegionPhysicalDeviceUart.GetAddress() + 0x200, 0x100); + static_assert(MemoryRegionPhysicalDeviceUart.Contains(MemoryRegionPhysicalDeviceUartA)); + static_assert(MemoryRegionPhysicalDeviceUart.Contains(MemoryRegionPhysicalDeviceUartB)); + static_assert(MemoryRegionPhysicalDeviceUart.Contains(MemoryRegionPhysicalDeviceUartC)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDevicePmc = MemoryRegion(MemoryRegionVirtualDeviceRtcPmc.GetAddress() + 0x400, 0xC00); + constexpr inline const MemoryRegion MemoryRegionPhysicalDevicePmc = MemoryRegion(MemoryRegionPhysicalDeviceRtcPmc.GetAddress() + 0x400, 0xC00); + static_assert(MemoryRegionVirtualDeviceRtcPmc.Contains(MemoryRegionVirtualDevicePmc)); + static_assert(MemoryRegionPhysicalDeviceRtcPmc.Contains(MemoryRegionPhysicalDevicePmc)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramReadOnlyAlias = MemoryRegion(UINT64_C(0x1F00A0000), MemoryRegionPhysicalTzram.GetSize()); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramReadOnlyAlias = MemoryRegion(MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize()); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramReadOnlyAlias)); + static_assert(MemoryRegionPhysicalTzram.Contains(MemoryRegionPhysicalTzramReadOnlyAlias)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramProgram(UINT64_C(0x1F00C0000), 0xC000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramProgram)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramProgramExceptionVectors(UINT64_C(0x1F00C0000), 0x800); + static_assert(MemoryRegionVirtualTzramProgram.Contains(MemoryRegionVirtualTzramProgramExceptionVectors)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramProgramMain(UINT64_C(0x1F00C0800), 0xB800); + static_assert(MemoryRegionVirtualTzramProgram.Contains(MemoryRegionVirtualTzramProgramMain)); + + static_assert(MemoryRegionVirtualTzramProgram.GetSize() == MemoryRegionVirtualTzramProgramExceptionVectors.GetSize() + MemoryRegionVirtualTzramProgramMain.GetSize()); + + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramProgram(UINT64_C(0x7C012000), 0xC000); + static_assert(MemoryRegionPhysicalTzramNonVolatile.Contains(MemoryRegionPhysicalTzramProgram)); + + constexpr inline const Address PhysicalTzramProgramResetVector = MemoryRegionPhysicalTzramProgram.GetAddress() + MemoryRegionVirtualTzramProgramExceptionVectors.GetSize(); + static_assert(static_cast(PhysicalTzramProgramResetVector) == PhysicalTzramProgramResetVector); + + constexpr uintptr_t GetPhysicalTzramProgramAddress(uintptr_t virtual_address) { + return virtual_address - MemoryRegionVirtualTzramProgram.GetStartAddress() + MemoryRegionPhysicalTzramNonVolatile.GetStartAddress(); + } + + constexpr inline const MemoryRegion MemoryRegionVirtualIramSc7Work = MemoryRegion(UINT64_C(0x1F0120000), MemoryRegionPhysicalTzram.GetSize()); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramSc7Work = MemoryRegion( UINT64_C(0x40020000), MemoryRegionPhysicalTzram.GetSize()); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualIramSc7Work)); + static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramSc7Work)); + + constexpr inline const MemoryRegion MemoryRegionVirtualIramSc7Firmware = MemoryRegion(UINT64_C(0x1F0140000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramSc7Firmware = MemoryRegion( UINT64_C(0x40003000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualIramSc7Firmware)); + static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramSc7Firmware)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDebug = MemoryRegion(UINT64_C(0x1F0160000), 0x10000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualIramSc7Firmware)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramBootCode = MemoryRegion(UINT64_C(0x1F01C0000), 0x2000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramBootCode = MemoryRegion( UINT64_C(0x7C010000), 0x2000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramBootCode)); + static_assert(MemoryRegionPhysicalTzramVolatile.Contains(MemoryRegionPhysicalTzramBootCode)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalDramMonitorConfiguration = MemoryRegion( UINT64_C(0x8000F000), 0x1000); + + constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStore = MemoryRegion(UINT64_C(0x1F0100000), 0x10000); + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSecureDataStore = MemoryRegion( UINT64_C(0x80010000), 0x10000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDramSecureDataStore)); + static_assert(MemoryRegionDram.Contains(MemoryRegionPhysicalDramSecureDataStore)); + + 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); + static_assert(MemoryRegionVirtualDramSecureDataStore.Contains(MemoryRegionVirtualDramSecureDataStoreTzram)); + static_assert(MemoryRegionVirtualDramSecureDataStore.Contains(MemoryRegionVirtualDramSecureDataStoreWarmbootFirmware)); + static_assert(MemoryRegionVirtualDramSecureDataStore.Contains(MemoryRegionVirtualDramSecureDataStoreSecurityEngineState)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSecureDataStoreTzram = MemoryRegion(UINT64_C(0x80010000), 0xE000); + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSecureDataStoreWarmbootFirmware = MemoryRegion(UINT64_C(0x8001E000), 0x17C0); + constexpr inline const MemoryRegion MemoryRegionPhysicalDramSecureDataStoreSecurityEngineState = MemoryRegion(UINT64_C(0x8001F7C0), 0x0840); + static_assert(MemoryRegionPhysicalDramSecureDataStore.Contains(MemoryRegionPhysicalDramSecureDataStoreTzram)); + static_assert(MemoryRegionPhysicalDramSecureDataStore.Contains(MemoryRegionPhysicalDramSecureDataStoreWarmbootFirmware)); + static_assert(MemoryRegionPhysicalDramSecureDataStore.Contains(MemoryRegionPhysicalDramSecureDataStoreSecurityEngineState)); + + constexpr inline const MemoryRegion MemoryRegionVirtualAtmosphereIramPage = MemoryRegion(UINT64_C(0x1F01F0000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualAtmosphereIramPage)); + + constexpr inline const MemoryRegion MemoryRegionVirtualAtmosphereUserPage = MemoryRegion(UINT64_C(0x1F01F2000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualAtmosphereUserPage)); + + constexpr inline const MemoryRegion MemoryRegionVirtualSmcUserPage = MemoryRegion(UINT64_C(0x1F01F4000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualSmcUserPage)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramVolatileData = MemoryRegion(UINT64_C(0x1F01F6000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramVolatileData = MemoryRegion( UINT64_C(0x7C010000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramVolatileData)); + static_assert(MemoryRegionPhysicalTzramVolatile.Contains(MemoryRegionPhysicalTzramVolatileData)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramVolatileStack = MemoryRegion(UINT64_C(0x1F01F8000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramVolatileStack = MemoryRegion( UINT64_C(0x7C011000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramVolatileStack)); + static_assert(MemoryRegionPhysicalTzramVolatile.Contains(MemoryRegionPhysicalTzramVolatileStack)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramConfigurationData = MemoryRegion(UINT64_C(0x1F01FA000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramConfigurationData = MemoryRegion( UINT64_C(0x7C01E000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramConfigurationData)); + static_assert(MemoryRegionPhysicalTzramNonVolatile.Contains(MemoryRegionPhysicalTzramConfigurationData)); + + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramL1PageTable = MemoryRegion(UINT64_C(0x1F01FCFC0), 0x40); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramL1PageTable = MemoryRegion( UINT64_C(0x7C01EFC0), 0x40); + static_assert(MemoryRegionPhysicalTzramConfigurationData.Contains(MemoryRegionPhysicalTzramL1PageTable)); + + constexpr inline const MemoryRegion MemoryRegionVirtualTzramL2L3PageTable = MemoryRegion(UINT64_C(0x1F01FE000), 0x1000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramL2L3PageTable = MemoryRegion( UINT64_C(0x7C01F000), 0x1000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramL2L3PageTable)); + static_assert(MemoryRegionPhysicalTzramNonVolatile.Contains(MemoryRegionPhysicalTzramL2L3PageTable)); + + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramFullProgramImage = MemoryRegion(0x7C010800, 0xD800); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCodeImage = MemoryRegion(0x40032000, 0xC000); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp new file mode 100644 index 000000000..1aa0b679d --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::secmon { + + enum SecureMonitorConfigurationFlag : u32 { + SecureMonitorConfigurationFlag_None = (0u << 0), + SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel = (1u << 1), + SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForUser = (1u << 2), + SecureMonitorConfigurationFlag_DisableUserModeExceptionHandlers = (1u << 3), + SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess = (1u << 4), + SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary = (1u << 5), + SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc = (1u << 6), + + SecureMonitorConfigurationFlag_Default = SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel, + }; + + struct SecureMonitorStorageConfiguration { + static constexpr u32 Magic = util::FourCC<'E','X','O','0'>::Code; + + u32 magic; + ams::TargetFirmware target_firmware; + u32 flags; + u32 reserved[5]; + EmummcConfiguration emummc_cfg; + + constexpr bool IsValid() const { return this->magic == Magic; } + }; + static_assert(util::is_pod::value); + static_assert(sizeof(SecureMonitorStorageConfiguration) == 0x130); + + struct SecureMonitorConfiguration { + ams::TargetFirmware target_firmware; + s32 key_generation; + u32 flags; + u32 reserved[(0x80 - 0x0C) / sizeof(u32)]; + + constexpr void CopyFrom(const SecureMonitorStorageConfiguration &storage) { + this->target_firmware = storage.target_firmware; + this->flags = storage.flags; + } + + constexpr ams::TargetFirmware GetTargetFirmware() const { return this->target_firmware; } + constexpr int GetKeyGeneration() const { return this->key_generation; } + + constexpr bool IsDevelopmentFunctionEnabledForKernel() const { return (this->flags & SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel) != 0; } + constexpr bool IsDevelopmentFunctionEnabledForUser() const { return (this->flags & SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForUser) != 0; } + constexpr bool DisableUserModeExceptionHandlers() const { return (this->flags & SecureMonitorConfigurationFlag_DisableUserModeExceptionHandlers) != 0; } + constexpr bool EnableUserModePerformanceCounterAccess() const { return (this->flags & SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess) != 0; } + constexpr bool ShouldUseBlankCalibrationBinary() const { return (this->flags & SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary) != 0; } + constexpr bool AllowWritingToCalibrationBinarySysmmc() const { return (this->flags & SecureMonitorConfigurationFlag_AllowWritingToCalibrationBinarySysmmc) != 0; } + }; + static_assert(util::is_pod::value); + static_assert(sizeof(SecureMonitorConfiguration) == 0x80); + + constexpr inline const SecureMonitorConfiguration DefaultSecureMonitorConfiguration = { + .target_firmware = ams::TargetFirmware_Current, + .flags = SecureMonitorConfigurationFlag_Default, + }; + +} diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp new file mode 100644 index 000000000..c8bc4e37b --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_volatile_context.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::secmon { + + /* The VolatileStack page is reserved entirely for use for core 3 SMC handling. */ + constexpr inline const Address Core3SmcStackAddress = MemoryRegionVirtualTzramVolatileStack.GetAddress() + MemoryRegionVirtualTzramVolatileStack.GetSize(); + + constexpr inline const size_t CoreExceptionStackSize = 0x80; + + /* Nintendo uses the bottom 0x740 of this as a stack for warmboot setup, and another 0x740 for the core 0/1/2 SMC stacks. */ + /* This is...wasteful. The warmboot stack is not deep. We will thus save 1K+ of nonvolatile storage by keeping the random cache in here. */ + struct VolatileData { + u8 random_cache[0x400]; + u8 se_work_block[crypto::AesEncryptor128::BlockSize]; + u8 reserved_danger_zone[0x30]; /* This memory is "available", but careful consideration must be taken before declaring it used. */ + u8 warmboot_stack[0x380]; + u8 core012_smc_stack[0x6C0]; + u8 core_exception_stacks[3][CoreExceptionStackSize]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(VolatileData) == 0x1000); + + ALWAYS_INLINE VolatileData &GetVolatileData() { + return *MemoryRegionVirtualTzramVolatileData.GetPointer(); + } + + ALWAYS_INLINE u8 *GetRandomBytesCache() { + return GetVolatileData().random_cache; + } + + constexpr ALWAYS_INLINE size_t GetRandomBytesCacheSize() { + return sizeof(VolatileData::random_cache); + } + + ALWAYS_INLINE u8 *GetSecurityEngineEphemeralWorkBlock() { + return GetVolatileData().se_work_block; + } + + constexpr inline const Address WarmbootStackAddress = MemoryRegionVirtualTzramVolatileData.GetAddress() + offsetof(VolatileData, warmboot_stack) + sizeof(VolatileData::warmboot_stack); + constexpr inline const Address Core012SmcStackAddress = MemoryRegionVirtualTzramVolatileData.GetAddress() + offsetof(VolatileData, core012_smc_stack) + sizeof(VolatileData::core012_smc_stack); + + constexpr inline const Address Core0ExceptionStackAddress = MemoryRegionVirtualTzramVolatileData.GetAddress() + offsetof(VolatileData, core_exception_stacks) + CoreExceptionStackSize; + constexpr inline const Address Core1ExceptionStackAddress = Core0ExceptionStackAddress + CoreExceptionStackSize; + constexpr inline const Address Core2ExceptionStackAddress = Core1ExceptionStackAddress + CoreExceptionStackSize; + +} diff --git a/libraries/libexosphere/include/exosphere/tegra.hpp b/libraries/libexosphere/include/exosphere/tegra.hpp new file mode 100644 index 000000000..370bcc3bc --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp new file mode 100644 index 000000000..a131cf68d --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#define AHB_ARBC(x) (0x6000c000 + x) + +#define AHB_ARBITRATION_DISABLE (0x004) +#define AHB_ARBITRATION_PRIORITY_CTRL (0x008) +#define AHB_MASTER_SWID (0x018) +#define AHB_MASTER_SWID_1 (0x038) +#define AHB_GIZMO_TZRAM (0x054) + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp new file mode 100644 index 000000000..af05f3658 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 (0xc00) +#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 (0xc04) +#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 (0xc08) + +#define AHB_MISC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (AHB_MISC, NAME) +#define AHB_MISC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (AHB_MISC, NAME, VALUE) +#define AHB_MISC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (AHB_MISC, NAME, ENUM) +#define AHB_MISC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(AHB_MISC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_AHB_MISC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (AHB_MISC, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_AHB_MISC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (AHB_MISC, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_AHB_MISC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (AHB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_AHB_MISC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AHB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_AHB_MISC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AHB_MISC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +#define DEFINE_SLAVE_SECURITY_REG(RINDEX, INDEX, NAME) DEFINE_AHB_MISC_REG_BIT_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, INDEX, DISABLE, ENABLE) + +DEFINE_SLAVE_SECURITY_REG(0, 29, STM); +DEFINE_SLAVE_SECURITY_REG(0, 24, CEC); +DEFINE_SLAVE_SECURITY_REG(0, 23, ATOMICS); +DEFINE_SLAVE_SECURITY_REG(0, 22, LA); +DEFINE_SLAVE_SECURITY_REG(0, 21, HDA); +DEFINE_SLAVE_SECURITY_REG(0, 20, SATA); +DEFINE_SLAVE_SECURITY_REG(0, 16, KFUSE); +DEFINE_SLAVE_SECURITY_REG(0, 15, FUSE); +DEFINE_SLAVE_SECURITY_REG(0, 14, SE); +DEFINE_SLAVE_SECURITY_REG(0, 13, PMC); +DEFINE_SLAVE_SECURITY_REG(0, 11, RTC); +DEFINE_SLAVE_SECURITY_REG(0, 10, CSITE); +DEFINE_SLAVE_SECURITY_REG(0, 9, QSPI); +DEFINE_SLAVE_SECURITY_REG(0, 8, PWM); +DEFINE_SLAVE_SECURITY_REG(0, 6, DTV); +DEFINE_SLAVE_SECURITY_REG(0, 4, APE); +DEFINE_SLAVE_SECURITY_REG(0, 3, PINMUX_AUX); +DEFINE_SLAVE_SECURITY_REG(0, 2, SATA_AUX); +DEFINE_SLAVE_SECURITY_REG(0, 1, MISC_REGS); + +DEFINE_SLAVE_SECURITY_REG(1, 31, I2C6); +DEFINE_SLAVE_SECURITY_REG(1, 30, DVC); +DEFINE_SLAVE_SECURITY_REG(1, 29, I2C4); +DEFINE_SLAVE_SECURITY_REG(1, 28, I2C3); +DEFINE_SLAVE_SECURITY_REG(1, 27, I2C2); +DEFINE_SLAVE_SECURITY_REG(1, 26, I2C1); +DEFINE_SLAVE_SECURITY_REG(1, 25, SPI6); +DEFINE_SLAVE_SECURITY_REG(1, 24, SPI5); +DEFINE_SLAVE_SECURITY_REG(1, 23, SPI4); +DEFINE_SLAVE_SECURITY_REG(1, 22, SPI3); +DEFINE_SLAVE_SECURITY_REG(1, 21, SPI2); +DEFINE_SLAVE_SECURITY_REG(1, 20, SPI1); +DEFINE_SLAVE_SECURITY_REG(1, 15, UART_D); +DEFINE_SLAVE_SECURITY_REG(1, 14, UART_C); +DEFINE_SLAVE_SECURITY_REG(1, 13, UART_B); +DEFINE_SLAVE_SECURITY_REG(1, 12, UART_A); +DEFINE_SLAVE_SECURITY_REG(1, 11, EMCB); +DEFINE_SLAVE_SECURITY_REG(1, 10, MCB); +DEFINE_SLAVE_SECURITY_REG(1, 9, EMC1); +DEFINE_SLAVE_SECURITY_REG(1, 8, MC1); +DEFINE_SLAVE_SECURITY_REG(1, 5, EMC0); +DEFINE_SLAVE_SECURITY_REG(1, 4, MC0); + +DEFINE_SLAVE_SECURITY_REG(2, 21, FEK); +DEFINE_SLAVE_SECURITY_REG(2, 20, PKA1); +DEFINE_SLAVE_SECURITY_REG(2, 19, SE2); +DEFINE_SLAVE_SECURITY_REG(2, 16, DVFS); +DEFINE_SLAVE_SECURITY_REG(2, 15, MIPI_CAL); +DEFINE_SLAVE_SECURITY_REG(2, 14, XUSB_PADCTL); +DEFINE_SLAVE_SECURITY_REG(2, 13, XUSB_DEV); +DEFINE_SLAVE_SECURITY_REG(2, 12, XUSB_HOST); +DEFINE_SLAVE_SECURITY_REG(2, 11, APB2JTAG); +DEFINE_SLAVE_SECURITY_REG(2, 10, SOC_THERM); +DEFINE_SLAVE_SECURITY_REG(2, 9, DP2); +DEFINE_SLAVE_SECURITY_REG(2, 8, DDS); +DEFINE_SLAVE_SECURITY_REG(2, 7, MIPIBIF); +DEFINE_SLAVE_SECURITY_REG(2, 3, SDMMC4); +DEFINE_SLAVE_SECURITY_REG(2, 2, SDMMC3); +DEFINE_SLAVE_SECURITY_REG(2, 1, SDMMC2); +DEFINE_SLAVE_SECURITY_REG(2, 0, SDMMC1); + +#undef DEFINE_SLAVE_SECURITY_REG + +#define SLAVE_SECURITY_REG_BITS_ENUM(RINDEX, NAME, ENUM) AHB_MISC_REG_BITS_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, ENUM) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_avp_cache.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_avp_cache.hpp new file mode 100644 index 000000000..52e4c7748 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_avp_cache.hpp @@ -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 . + */ +#pragma once +#include + +#define AVP_CACHE_ADDRESS(x) (0x50040000 + x) + +#define AVP_CACHE_CONFIG (0x000) + +#define AVP_CACHE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (AVP_CACHE, NAME) +#define AVP_CACHE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (AVP_CACHE, NAME, VALUE) +#define AVP_CACHE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (AVP_CACHE, NAME, ENUM) +#define AVP_CACHE_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(AVP_CACHE, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_AVP_CACHE_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (AVP_CACHE, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_AVP_CACHE_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_AVP_CACHE_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_AVP_CACHE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_AVP_CACHE_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_AVP_CACHE_REG_BIT_ENUM(DISABLE_WB, 10, FALSE, TRUE); +DEFINE_AVP_CACHE_REG_BIT_ENUM(DISABLE_RB, 11, FALSE, TRUE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp new file mode 100644 index 000000000..f072b0196 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 + +#define EMC_ADDRESS(x) (0x7001B000 + x) +#define EMC0_ADDRESS(x) (0x7001E000 + x) +#define EMC1_ADDRESS(x) (0x7001F000 + x) + +#define EMC_CFG (0x00C) +#define EMC_ADR_CFG (0x010) +#define EMC_TIMING_CONTROL (0x028) +#define EMC_SELF_REF (0x0E0) +#define EMC_MRW (0x0E8) +#define EMC_FBIO_CFG5 (0x104) +#define EMC_MRW3 (0x138) +#define EMC_AUTO_CAL_CONFIG (0x2A4) +#define EMC_REQ_CTRL (0x2B0) +#define EMC_EMC_STATUS (0x2B4) +#define EMC_CFG_DIG_DLL (0x2BC) +#define EMC_ZCAL_INTERVAL (0x2E0) +#define EMC_PMC_SCRATCH3 (0x448) +#define EMC_FBIO_CFG7 (0x584) + +#define EMC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (EMC, NAME) +#define EMC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (EMC, NAME, VALUE) +#define EMC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (EMC, NAME, ENUM) +#define EMC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(EMC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_EMC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (EMC, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_EMC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (EMC, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_EMC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (EMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_EMC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(EMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_EMC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (EMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_EMC_REG_BIT_ENUM(CFG_DYN_SELF_REF, 28, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(CFG_DRAM_ACPD, 29, NO_POWERDOWN, ACTIVE_POWERDOWN); + +DEFINE_EMC_REG_BIT_ENUM(ADR_CFG_EMEM_NUMDEV, 0, N1, N2); + +DEFINE_EMC_REG_BIT_ENUM(TIMING_CONTROL_TIMING_UPDATE, 0, DISABLED, ENABLED); + +DEFINE_EMC_REG_BIT_ENUM(SELF_REF_SELF_REF_CMD, 0, DISABLED, ENABLED); +DEFINE_EMC_REG_BIT_ENUM(SELF_REF_ACTIVE_SELF_REF, 8, DISABLED, ENABLED); +DEFINE_EMC_REG_TWO_BIT_ENUM(SELF_REF_SREF_DEV_SELECTN, 30, BOTH, DEV1, DEV0, RESERVED); + +DEFINE_EMC_REG(MRW_OP, 0, 8); +DEFINE_EMC_REG(MRW_MA, 16, 8); +DEFINE_EMC_REG_TWO_BIT_ENUM(MRW_CNT, 26, SHORT, LONG, EXT1, EXT2); +DEFINE_EMC_REG_TWO_BIT_ENUM(MRW_DEV_SELECTN, 30, BOTH, DEV1, DEV0, RESERVED); + +DEFINE_EMC_REG_TWO_BIT_ENUM(FBIO_CFG5_DRAM_TYPE, 0, DDR4, LPDDR4, LPDDR2, DDR2); + +DEFINE_EMC_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL, 9, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL, 10, DISABLE, ENABLE); +DEFINE_EMC_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, 31, DISABLE, ENABLE); + +DEFINE_EMC_REG(REQ_CTRL_STALL_ALL_READS, 0, 1); +DEFINE_EMC_REG(REQ_CTRL_STALL_ALL_WRITES, 1, 1); + +DEFINE_EMC_REG_TWO_BIT_ENUM(EMC_STATUS_DRAM_IN_SELF_REFRESH, 8, DISABLED, DEV0_ENABLED, DEV1_ENABLED, BOTH_ENABLED); + +DEFINE_EMC_REG_BIT_ENUM(EMC_STATUS_DRAM_DEV0_IN_SELF_REFRESH, 8, DISABLED, ENABLED); + +DEFINE_EMC_REG_BIT_ENUM(EMC_STATUS_NO_OUTSTANDING_TRANSACTIONS, 2, WAITING, COMPLETED); +DEFINE_EMC_REG_BIT_ENUM(EMC_STATUS_TIMING_UPDATE_STALLED, 23, DONE, BUSY); + +DEFINE_EMC_REG_BIT_ENUM(CFG_DIG_DLL_CFG_DLL_EN, 0, DISABLED, ENABLED); + +DEFINE_EMC_REG(ZCAL_INTERVAL_LO, 0, 10); +DEFINE_EMC_REG(ZCAL_INTERVAL_HI, 10, 14); + +DEFINE_EMC_REG(PMC_SCRATCH3_DDR_CNTRL, 0, 19); +DEFINE_EMC_REG_BIT_ENUM(PMC_SCRATCH3_WEAK_BIAS, 30, DISABLED, ENABLED); + +DEFINE_EMC_REG_BIT_ENUM(FBIO_CFG7_CH1_ENABLE, 2, DISABLE, ENABLE); + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp new file mode 100644 index 000000000..27d61f7cf --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 + +#define EVP_CPU_RESET_VECTOR (0x100) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp new file mode 100644 index 000000000..d7d3361e1 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 + + +#define FLOW_CTLR_FLOW_DBG_QUAL (0x050) +#define FLOW_CTLR_BPMP_CLUSTER_CONTROL (0x098) + +#define FLOW_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (FLOW_CTLR, NAME) +#define FLOW_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (FLOW_CTLR, NAME, VALUE) +#define FLOW_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (FLOW_CTLR, NAME, ENUM) +#define FLOW_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(FLOW_CTLR, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_FLOW_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (FLOW_CTLR, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_FLOW_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_FLOW_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_FLOW_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_FLOW_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (FLOW_CTLR, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_FLOW_REG_BIT_ENUM(FLOW_DBG_QUAL_FIQ2CCPLEX_ENABLE, 28, DISABLE, ENABLE); + +DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, 0, FAST, SLOW); +DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_CLUSTER_SWITCH_ENABLE, 1, DISABLE, ENABLE); +DEFINE_FLOW_REG_BIT_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER_LOCK, 2, DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_ictlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_ictlr.hpp new file mode 100644 index 000000000..6f11ee918 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_ictlr.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#define PRI_ICTLR(n) (0x60004000 + n) +#define SEC_ICTLR(n) (0x60004100 + n) +#define TRI_ICTLR(n) (0x60004200 + n) +#define QUAD_ICTLR(n) (0x60004300 + n) +#define PENTA_ICTLR(n) (0x60004400 + n) +#define HEXA_ICTLR(n) (0x60004500 + n) + +#define ICTLR_COP_IER_CLR (0x038) + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp new file mode 100644 index 000000000..b97b5c631 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +#define MC_INTSTATUS (0x000) +#define MC_INTMASK (0x004) +#define MC_ERR_STATUS (0x008) +#define MC_ERR_ADR (0x00c) +#define MC_SMMU_CONFIG (0x010) +#define MC_SMMU_TLB_CONFIG (0x014) +#define MC_SMMU_PTC_CONFIG (0x018) +#define MC_SMMU_PTB_ASID (0x01c) +#define MC_SMMU_PTB_DATA (0x020) +#define MC_SMMU_TLB_FLUSH (0x030) +#define MC_SMMU_PTC_FLUSH (0x034) + +#define MC_SECURITY_CFG0 (0x070) +#define MC_SECURITY_CFG1 (0x074) +#define MC_SECURITY_CFG3 (0x9BC) + +#define MC_SMMU_TRANSLATION_ENABLE_0 (0x228) +#define MC_SMMU_TRANSLATION_ENABLE_1 (0x22C) +#define MC_SMMU_TRANSLATION_ENABLE_2 (0x230) +#define MC_SMMU_TRANSLATION_ENABLE_3 (0x234) +#define MC_SMMU_TRANSLATION_ENABLE_4 (0xB98) + +#define MC_SMMU_ASID_SECURITY (0x038) +#define MC_SMMU_ASID_SECURITY_1 (0x03c) +#define MC_SMMU_ASID_SECURITY_2 (0x9e0) +#define MC_SMMU_ASID_SECURITY_3 (0x9e4) +#define MC_SMMU_ASID_SECURITY_4 (0x9e8) +#define MC_SMMU_ASID_SECURITY_5 (0x9ec) +#define MC_SMMU_ASID_SECURITY_6 (0x9f0) +#define MC_SMMU_ASID_SECURITY_7 (0x9f4) + +#define MC_SEC_CARVEOUT_BOM (0x670) +#define MC_SEC_CARVEOUT_SIZE_MB (0x674) +#define MC_SEC_CARVEOUT_REG_CTRL (0x678) + +#define MC_VIDEO_PROTECT_BOM (0x648) +#define MC_VIDEO_PROTECT_SIZE_MB (0x64c) +#define MC_VIDEO_PROTECT_REG_CTRL (0x650) +#define MC_VIDEO_PROTECT_GPU_OVERRIDE_0 (0x984) +#define MC_VIDEO_PROTECT_GPU_OVERRIDE_1 (0x988) + +#define MC_MTS_CARVEOUT_BOM (0x9a0) +#define MC_MTS_CARVEOUT_SIZE_MB (0x9a4) +#define MC_MTS_CARVEOUT_ADR_HI (0x9a8) +#define MC_MTS_CARVEOUT_REG_CTRL (0x9ac) + +#define MC_SECURITY_CARVEOUT1_CFG0 (0xc08) +#define MC_SECURITY_CARVEOUT1_BOM (0xc0c) +#define MC_SECURITY_CARVEOUT1_BOM_HI (0xc10) +#define MC_SECURITY_CARVEOUT1_SIZE_128KB (0xc14) +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0 (0xc18) +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1 (0xc1c) +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2 (0xc20) +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3 (0xc24) +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4 (0xc28) +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0 (0xc2c) +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1 (0xc30) +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2 (0xc34) +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3 (0xc38) +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4 (0xc3c) + +#define MC_SECURITY_CARVEOUT2_CFG0 (0xc58) +#define MC_SECURITY_CARVEOUT2_BOM (0xc5c) +#define MC_SECURITY_CARVEOUT2_BOM_HI (0xc60) +#define MC_SECURITY_CARVEOUT2_SIZE_128KB (0xc64) +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0 (0xc68) +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1 (0xc6c) +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2 (0xc70) +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3 (0xc74) +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4 (0xc78) +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0 (0xc7c) +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1 (0xc80) +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2 (0xc84) +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3 (0xc88) +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4 (0xc8c) + +#define MC_SECURITY_CARVEOUT3_CFG0 (0xca8) +#define MC_SECURITY_CARVEOUT3_BOM (0xcac) +#define MC_SECURITY_CARVEOUT3_BOM_HI (0xcb0) +#define MC_SECURITY_CARVEOUT3_SIZE_128KB (0xcb4) +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0 (0xcb8) +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1 (0xcbc) +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2 (0xcc0) +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3 (0xcc4) +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4 (0xcc8) +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0 (0xccc) +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1 (0xcd0) +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2 (0xcd4) +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3 (0xcd8) +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4 (0xcdc) + +#define MC_SECURITY_CARVEOUT4_CFG0 (0xcf8) +#define MC_SECURITY_CARVEOUT4_BOM (0xcfc) +#define MC_SECURITY_CARVEOUT4_BOM_HI (0xd00) +#define MC_SECURITY_CARVEOUT4_SIZE_128KB (0xd04) +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0 (0xd08) +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1 (0xd0c) +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2 (0xd10) +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3 (0xd14) +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4 (0xd18) +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0 (0xd1c) +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1 (0xd20) +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2 (0xd24) +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3 (0xd28) +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4 (0xd2c) + +#define MC_SECURITY_CARVEOUT5_CFG0 (0xd48) +#define MC_SECURITY_CARVEOUT5_BOM (0xd4c) +#define MC_SECURITY_CARVEOUT5_BOM_HI (0xd50) +#define MC_SECURITY_CARVEOUT5_SIZE_128KB (0xd54) +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0 (0xd58) +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1 (0xd5c) +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2 (0xd60) +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3 (0xd64) +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4 (0xd68) +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0 (0xd6c) +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1 (0xd70) +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2 (0xd74) +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3 (0xd78) +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4 (0xd7c) + + +#define MC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (MC, NAME) +#define MC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (MC, NAME, VALUE) +#define MC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (MC, NAME, ENUM) +#define MC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(MC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_MC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (MC, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_MC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (MC, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_MC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (MC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_MC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(MC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_MC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (MC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_MC_REG_BIT_ENUM(SMMU_CONFIG_SMMU_ENABLE, 0, DISABLE, ENABLE); + +DEFINE_MC_REG(SMMU_TLB_CONFIG_TLB_ACTIVE_LINES, 0, 6); +DEFINE_MC_REG_BIT_ENUM(SMMU_TLB_CONFIG_TLB_ROUND_ROBIN_ARBITRATION, 28, DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(SMMU_TLB_CONFIG_TLB_HIT_UNDER_MISS, 29, DISABLE, ENABLE); + +DEFINE_MC_REG(SMMU_PTC_CONFIG_PTC_INDEX_MAP, 0, 7); +DEFINE_MC_REG(SMMU_PTC_CONFIG_PTC_REQ_LIMIT, 24, 4); +DEFINE_MC_REG_BIT_ENUM(SMMU_PTC_CONFIG_PTC_CACHE_ENABLE, 29, DISABLE, ENABLE); + +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_0, 0, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_1, 1, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_2, 2, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_3, 3, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_4, 4, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_5, 5, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_6, 6, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_7, 7, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_8, 8, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_9, 9, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_10, 10, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_11, 11, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_12, 12, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_13, 13, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_14, 14, NONSECURE, SECURE); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_SECURE_ASIDS_15, 15, NONSECURE, SECURE); + +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_0, 16, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_1, 17, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_2, 18, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_3, 19, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_4, 20, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_5, 21, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_6, 22, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_7, 23, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_8, 24, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_9, 25, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_10, 26, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_11, 27, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_12, 28, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_13, 29, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_14, 30, NONPROMOTING, PROMOTING); +DEFINE_MC_REG_BIT_ENUM(SMMU_ASID_SECURITY_PROMOTING_ASIDS_15, 31, NONPROMOTING, PROMOTING); + +DEFINE_MC_REG(SECURITY_CFG0_SECURITY_BOM, 20, 12); +DEFINE_MC_REG(SECURITY_CFG1_SECURITY_SIZE, 0, 13); +DEFINE_MC_REG(SECURITY_CFG3_SECURITY_BOM_HI, 0, 2); + +DEFINE_MC_REG_BIT_ENUM(SEC_CARVEOUT_REG_CTRL_SEC_CARVEOUT_WRITE_ACCESS, 0, ENABLED, DISABLED); + +DEFINE_MC_REG_BIT_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_WRITE_ACCESS, 0, ENABLED, DISABLED); +DEFINE_MC_REG_BIT_ENUM(VIDEO_PROTECT_REG_CTRL_VIDEO_PROTECT_ALLOW_TZ_WRITE, 1, DISABLED, ENABLED); + +DEFINE_MC_REG_BIT_ENUM(MTS_CARVEOUT_REG_CTRL_MTS_CARVEOUT_WRITE_ACCESS, 0, ENABLED, DISABLED); + +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_PROTECT_MODE, 0, LOCKBIT_SECURE, TZ_SECURE); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_LOCK_MODE, 1, UNLOCKED, LOCKED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, 2, ANY_ADDRESS, UNTRANSLATED_ONLY); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, 3, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, 4, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, 5, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, 6, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, 7, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, 8, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, 9, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, 10, DISABLED, ENABLED); +DEFINE_MC_REG(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 11, 3); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, 14, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, 15, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, 16, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, 17, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, 18, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, 19, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, 20, ENABLE_CHECKS, DISABLE_CHECKS); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, 21, ENABLE_CHECKS, DISABLE_CHECKS); + +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, 22, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, 23, DISABLED, BYPASS_CHECK); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, 24, DISABLED, BYPASS_CHECK); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, 25, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, 26, DISABLED, ENABLED); +DEFINE_MC_REG_BIT_ENUM(SECURITY_CARVEOUT_CFG0_IS_WPR, 27, DISABLED, ENABLED); + +#define MC_CLIENT_ACCESS_NUM_CLIENTS 32 + +/* _ACCESS0 */ +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_PTCR, ( 0 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0A, ( 1 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0AB, ( 2 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0B, ( 3 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0BB, ( 4 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0C, ( 5 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAY0CB, ( 6 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_AFIR, ( 14 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_AVPCARM7R, ( 15 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAYHC, ( 16 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_DISPLAYHCB, ( 17 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_HDAR, ( 21 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_HOST1XDMAR, ( 22 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_HOST1XR, ( 23 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_NVENCSRD, ( 28 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_PPCSAHBDMAR, ( 29 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_PPCSAHBSLVR, ( 30 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS0_SATAR, ( 31 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 0)), DISABLE, ENABLE); + +/* _ACCESS1 */ +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEBSEVR, ( 34 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEMBER, ( 35 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEMCER, ( 36 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDETPER, ( 37 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCORELPR, ( 38 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCORER, ( 39 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_NVENCSWR, ( 43 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_AFIW, ( 49 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_AVPCARM7W, ( 50 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_HDAW, ( 53 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_HOST1XW, ( 54 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCORELPW, ( 56 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_MPCOREW, ( 57 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_PPCSAHBDMAW, ( 59 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_PPCSAHBSLVW, ( 60 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_SATAW, ( 61 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEBSEVW, ( 62 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS1_VDEDBGW, ( 63 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 1)), DISABLE, ENABLE); + +/* _ACCESS2 */ +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_VDEMBEW, ( 64 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_VDETPMW, ( 65 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPRA, ( 68 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWA, ( 70 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWB, ( 71 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_HOSTR, ( 74 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_HOSTW, ( 75 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_DEVR, ( 76 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_XUSB_DEVW, ( 77 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPRAB, ( 78 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWAB, ( 80 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_ISPWBB, ( 81 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_TSECSRD, ( 84 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_TSECSWR, ( 85 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_A9AVPSCR, ( 86 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_A9AVPSCW, ( 87 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_GPUSRD, ( 88 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_GPUSWR, ( 89 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS2_DISPLAYT, ( 90 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 2)), DISABLE, ENABLE); + +/* _ACCESS3 */ +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCRA, ( 96 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCRAA, ( 97 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCR, ( 98 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCRAB, ( 99 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCWA, (100 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCWAA, (101 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCW, (102 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_SDMMCWAB, (103 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_VICSRD, (108 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_VICSWR, (109 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_VIW, (114 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_DISPLAYD, (115 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVDECSRD, (120 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVDECSWR, (121 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_APER, (122 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_APEW, (123 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVJPGSRD, (126 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS3_NVJPGSWR, (127 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 3)), DISABLE, ENABLE); + +/* _ACCESS4 */ +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_SESRD, (128 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_SESWR, (129 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_AXIAPR, (130 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_AXIAPW, (131 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_ETRR, (132 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_ETRW, (133 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_TSECRDB, (134 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_TSECWRB, (135 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_GPUSRD2, (136 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); +DEFINE_MC_REG_BIT_ENUM(CLIENT_ACCESS4_GPUSWR2, (137 - (MC_CLIENT_ACCESS_NUM_CLIENTS * 4)), DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp new file mode 100644 index 000000000..20c5530df --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp @@ -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 . + */ +#pragma once +#include + +#define MSELECT(x) (0x50060000 + x) + +#define MSELECT_CONFIG (0x000) + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp new file mode 100644 index 000000000..ee10391fa --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +#define APBDEV_PMC_CNTRL (0x000) +#define APBDEV_PMC_DPD_SAMPLE (0x020) +#define APBDEV_PMC_DPD_ENABLE (0x024) +#define APBDEV_PMC_CLAMP_STATUS (0x02C) +#define APBDEV_PMC_PWRGATE_TOGGLE (0x030) +#define APBDEV_PMC_PWRGATE_STATUS (0x038) +#define APBDEV_PMC_SCRATCH0 (0x050) +#define APBDEV_PMC_SCRATCH1 (0x054) +#define APBDEV_PMC_SCRATCH12 (0x080) +#define APBDEV_PMC_SCRATCH13 (0x084) +#define APBDEV_PMC_SCRATCH18 (0x098) +#define APBDEV_PMC_SCRATCH20 (0x0A0) +#define APBDEV_PMC_CRYPTO_OP (0x0F4) +#define APBDEV_PM (0x014) +#define APBDEV_PMC_WAKE2_STATUS (0x168) +#define APBDEV_PMC_WEAK_BIAS (0x2C8) +#define APBDEV_PMC_CNTRL2 (0x440) +#define APBDEV_PMC_FUSE_CTRL (0x450) +#define APBDEV_PMC_IO_DPD3_REQ (0x45C) +#define APBDEV_PMC_IO_DPD3_STATUS (0x460) +#define APBDEV_PMC_IO_DPD4_REQ (0x464) +#define APBDEV_PMC_IO_DPD4_STATUS (0x468) +#define APBDEV_PMC_SET_SW_CLAMP (0x47C) +#define APBDEV_PMC_DDR_CNTRL (0x4E4) +#define APBDEV_PMC_SEC_DISABLE (0x004) +#define APBDEV_PMC_SEC_DISABLE2 (0x2C4) +#define APBDEV_PMC_SEC_DISABLE3 (0x2D8) +#define APBDEV_PMC_SEC_DISABLE4 (0x5B0) +#define APBDEV_PMC_SEC_DISABLE5 (0x5B4) +#define APBDEV_PMC_SEC_DISABLE6 (0x5B8) +#define APBDEV_PMC_SEC_DISABLE7 (0x5BC) +#define APBDEV_PMC_SEC_DISABLE8 (0x5C0) +#define APBDEV_PMC_SCRATCH43 (0x22C) +#define APBDEV_PMC_SCRATCH190 (0x818) +#define APBDEV_PMC_SCRATCH200 (0x840) +#define APBDEV_PMC_SEC_DISABLE3 (0x2D8) +#define APBDEV_PMC_SECURE_SCRATCH4 (0x0C0) +#define APBDEV_PMC_SECURE_SCRATCH5 (0x0C4) +#define APBDEV_PMC_SECURE_SCRATCH6 (0x224) +#define APBDEV_PMC_SECURE_SCRATCH7 (0x228) +#define APBDEV_PMC_SECURE_SCRATCH16 (0x320) +#define APBDEV_PMC_SECURE_SCRATCH21 (0x334) +#define APBDEV_PMC_SECURE_SCRATCH24 (0x340) +#define APBDEV_PMC_SECURE_SCRATCH25 (0x344) +#define APBDEV_PMC_SECURE_SCRATCH26 (0x348) +#define APBDEV_PMC_SECURE_SCRATCH27 (0x34C) +#define APBDEV_PMC_SECURE_SCRATCH32 (0x360) +#define APBDEV_PMC_SECURE_SCRATCH34 (0x368) +#define APBDEV_PMC_SECURE_SCRATCH35 (0x36C) +#define APBDEV_PMC_SECURE_SCRATCH39 (0x37C) +#define APBDEV_PMC_SECURE_SCRATCH51 (0x3AC) +#define APBDEV_PMC_SECURE_SCRATCH55 (0x3BC) +#define APBDEV_PMC_SECURE_SCRATCH74 (0x408) +#define APBDEV_PMC_SECURE_SCRATCH75 (0x40C) +#define APBDEV_PMC_SECURE_SCRATCH76 (0x410) +#define APBDEV_PMC_SECURE_SCRATCH77 (0x414) +#define APBDEV_PMC_SECURE_SCRATCH78 (0x418) +#define APBDEV_PMC_SECURE_SCRATCH99 (0xAE4) +#define APBDEV_PMC_SECURE_SCRATCH100 (0xAE8) +#define APBDEV_PMC_SECURE_SCRATCH101 (0xAEC) +#define APBDEV_PMC_SECURE_SCRATCH102 (0xAF0) +#define APBDEV_PMC_SECURE_SCRATCH103 (0xAF4) +#define APBDEV_PMC_SECURE_SCRATCH112 (0xB18) +#define APBDEV_PMC_SECURE_SCRATCH113 (0xB1C) +#define APBDEV_PMC_SECURE_SCRATCH114 (0xB20) +#define APBDEV_PMC_SECURE_SCRATCH115 (0xB24) + + +#define PMC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (APBDEV_PMC, NAME) +#define PMC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (APBDEV_PMC, NAME, VALUE) +#define PMC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (APBDEV_PMC, NAME, ENUM) +#define PMC_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(APBDEV_PMC, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_PMC_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (APBDEV_PMC, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_PMC_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_PMC_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_PMC_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_PMC_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (APBDEV_PMC, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_PMC_REG_BIT_ENUM(CNTRL_MAIN_RESET, 4, DISABLE, ENABLE) + +DEFINE_PMC_REG_BIT_ENUM(DPD_SAMPLE_ON, 0, DISABLE, ENABLE); + +DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_ON, 0, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_TSC_MULT_EN, 1, DISABLE, ENABLE); + +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CRAIL, 0, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE, 2, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_PCX, 3, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_MPE, 6, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_SAX, 8, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE1, 9, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE2, 10, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE3, 11, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CE0, 14, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_C0NC, 15, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_SOR, 17, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DIS, 18, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DISB, 19, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_XUSBA, 20, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_XUSBB, 21, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_XUSBC, 22, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VIC, 23, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_IRAM, 24, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_NVDEC, 25, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_NVJPG, 26, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_AUD, 27, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DFD, 28, OFF, ON); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE2, 29, OFF, ON); + +DEFINE_PMC_REG(SET_SW_CLAMP_CRAIL, 0, 1); + +DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3); + +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CRAIL, 0, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_TE, 1, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VE, 2, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_PCX, 3, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VDE, 4, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_MPE, 6, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_HEG, 7, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_SAX, 8, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE1, 9, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE2, 10, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE3, 11, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CELP, 12, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CE0, 14, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_C0NC, 15, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_SOR, 17, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_C1NC, 16, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_DIS, 18, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_DISB, 19, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBA, 20, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBB, 21, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_XUSBC, 22, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_VIC, 23, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_IRAM, 24, DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp new file mode 100644 index 000000000..d6827adeb --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#define SB_CSR (0x200) +#define SB_AA64_RESET_LOW (0x230) +#define SB_AA64_RESET_HIGH (0x234) + + +#define SB_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SB, NAME) +#define SB_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SB, NAME, VALUE) +#define SB_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SB, NAME, ENUM) +#define SB_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SB, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_SB_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SB, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_SB_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SB, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_SB_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SB, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_SB_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SB, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_SB_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SB, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_SB_REG_BIT_ENUM(CSR_SECURE_BOOT_FLAG, 0, DISABLE, ENABLE); +DEFINE_SB_REG_BIT_ENUM(CSR_NS_RST_VEC_WR_DIS, 1, ENABLE, DISABLE); +DEFINE_SB_REG_BIT_ENUM(CSR_PIROM_DISABLE, 4, ENABLE, DISABLE); +DEFINE_SB_REG_BIT_ENUM(CSR_HANG, 6, DISABLE, ENABLE); +DEFINE_SB_REG_BIT_ENUM(CSR_SWDM_ENABLE, 7, DISABLE, ENABLE); +DEFINE_SB_REG(CSR_SWDM_FAIL_COUNT, 8, 4); +DEFINE_SB_REG(CSR_COT_FAIL_COUNT, 12, 4); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp new file mode 100644 index 000000000..a191641f9 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#define SYSCTR0_CNTFID0 (0x020) +#define SYSCTR0_CNTFID1 (0x024) + + +#define SYSCTR0_COUNTERID4 (0xFD0) +#define SYSCTR0_COUNTERID5 (0xFD4) +#define SYSCTR0_COUNTERID6 (0xFD8) +#define SYSCTR0_COUNTERID7 (0xFDC) +#define SYSCTR0_COUNTERID0 (0xFE0) +#define SYSCTR0_COUNTERID1 (0xFE4) +#define SYSCTR0_COUNTERID2 (0xFE8) +#define SYSCTR0_COUNTERID3 (0xFEC) +#define SYSCTR0_COUNTERID8 (0xFF0) +#define SYSCTR0_COUNTERID9 (0xFF4) +#define SYSCTR0_COUNTERID10 (0xFF8) +#define SYSCTR0_COUNTERID11 (0xFFC) + +#define SYSCTR0_COUNTERID(n) SYSCTR0_COUNTERID##n \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp new file mode 100644 index 000000000..93a7c8b66 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + + +#define TIMER_SHARED_TIMER_SECURE_CFG (0x1A4) + +#define TIMER_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (TIMER, NAME) +#define TIMER_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (TIMER, NAME, VALUE) +#define TIMER_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (TIMER, NAME, ENUM) +#define TIMER_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(TIMER, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_TIMER_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (TIMER, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_TIMER_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (TIMER, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_TIMER_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_TIMER_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_TIMER_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (TIMER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR5, 5, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR6, 6, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR7, 7, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_TMR8, 8, DISABLE, ENABLE); + +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT0, 12, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT1, 13, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT2, 14, DISABLE, ENABLE); +DEFINE_TIMER_REG_BIT_ENUM(SHARED_TIMER_SECURE_CFG_WDT3, 15, DISABLE, ENABLE); \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/tsec.hpp b/libraries/libexosphere/include/exosphere/tsec.hpp new file mode 100644 index 000000000..454da2efe --- /dev/null +++ b/libraries/libexosphere/include/exosphere/tsec.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::tsec { + + void Lock(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/uart.hpp b/libraries/libexosphere/include/exosphere/uart.hpp new file mode 100644 index 000000000..920f39125 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/uart.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::uart { + + enum Port { + Port_A = 0, + Port_B = 1, + Port_C = 2, + + Port_Count = 3, + + Port_ReservedDebug = Port_A, + Port_RightJoyCon = Port_B, + Port_LeftJoyCon = Port_C, + }; + + enum Flags { + Flag_None = (0u << 0), + Flag_Inverted = (1u << 0), + }; + + void SetRegisterAddress(uintptr_t address); + + void Initialize(Port port, int baud_rate, u32 flags); + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/util.hpp b/libraries/libexosphere/include/exosphere/util.hpp new file mode 100644 index 000000000..088033e7c --- /dev/null +++ b/libraries/libexosphere/include/exosphere/util.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::util { + + void SetRegisterAddress(uintptr_t address); + + u32 GetMicroSeconds(); + void WaitMicroSeconds(int us); + + void ClearMemory(void *ptr, size_t size); + + template requires std::integral && std::integral + constexpr T DivideUp(T x, U y) { + return (x + (y - 1)) / y; + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/wdt.hpp b/libraries/libexosphere/include/exosphere/wdt.hpp new file mode 100644 index 000000000..16a02f263 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/wdt.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::wdt { + + void SetRegisterAddress(uintptr_t address); + void Reboot(); + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/actmon/actmon_api.cpp b/libraries/libexosphere/source/actmon/actmon_api.cpp new file mode 100644 index 000000000..ee7843cbf --- /dev/null +++ b/libraries/libexosphere/source/actmon/actmon_api.cpp @@ -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 . + */ +#include + +namespace ams::actmon { + + namespace { + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceActivityMonitor.GetAddress(); + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void HandleInterrupt() { + /* TODO */ + } + + void StartMonitoringBpmp(InterruptHandler handler) { + /* TODO */ + } + + void StopMonitoringBpmp() { + /* TODO */ + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/clkrst/clkrst_api.cpp b/libraries/libexosphere/source/clkrst/clkrst_api.cpp new file mode 100644 index 000000000..c2de3028e --- /dev/null +++ b/libraries/libexosphere/source/clkrst/clkrst_api.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "clkrst_registers.hpp" + +namespace ams::clkrst { + + namespace { + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + + struct ClockParameters { + uintptr_t reset_offset; + uintptr_t clk_enb_offset; + uintptr_t clk_src_offset; + u8 index; + u8 clk_src; + u8 clk_div; + }; + + void EnableClock(const ClockParameters ¶m) { + /* Hold reset. */ + reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 1)); + + /* Disable clock. */ + reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0)); + + /* Set the clock source. */ + if (param.clk_src != 0) { + reg::Write(g_register_address + param.clk_src_offset, (param.clk_src << 29) | (param.clk_div << 0)); + } + + /* Enable clk. */ + reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 1)); + + /* Release reset. */ + reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 0)); + } + + // void DisableClock(const ClockParameters ¶m) { + // /* Hold reset. */ + // reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 1)); + // + // /* Disable clock. */ + // reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0)); + // } + + #define DEFINE_CLOCK_PARAMETERS(_VARNAME_, _REG_, _NAME_, _CLK_, _DIV_) \ + constexpr inline const ClockParameters _VARNAME_ = { \ + .reset_offset = CLK_RST_CONTROLLER_RST_DEVICES_##_REG_, \ + .clk_enb_offset = CLK_RST_CONTROLLER_CLK_OUT_ENB_##_REG_, \ + .clk_src_offset = CLK_RST_CONTROLLER_CLK_SOURCE_##_NAME_, \ + .index = CLK_RST_CONTROLLER_CLK_ENB_##_NAME_##_INDEX, \ + .clk_src = CLK_RST_CONTROLLER_CLK_SOURCE_##_NAME_##_##_NAME_##_CLK_SRC_##_CLK_, \ + .clk_div = _DIV_, \ + } + + DEFINE_CLOCK_PARAMETERS(UartAClock, L, UARTA, PLLP_OUT0, 0); + DEFINE_CLOCK_PARAMETERS(UartBClock, L, UARTB, PLLP_OUT0, 0); + DEFINE_CLOCK_PARAMETERS(UartCClock, H, UARTC, PLLP_OUT0, 0); + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void SetFuseVisibility(bool visible) { + reg::ReadWrite(g_register_address + CLK_RST_CONTROLLER_MISC_CLK_ENB, CLK_RST_REG_BITS_VALUE(MISC_CLK_ENB_CFG_ALL_VISIBLE, visible ? 1 : 0)); + } + + void EnableUartAClock() { + EnableClock(UartAClock); + } + + void EnableUartBClock() { + EnableClock(UartAClock); + } + + void EnableUartCClock() { + EnableClock(UartAClock); + } + + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/clkrst/clkrst_registers.hpp b/libraries/libexosphere/source/clkrst/clkrst_registers.hpp new file mode 100644 index 000000000..5d5cd0874 --- /dev/null +++ b/libraries/libexosphere/source/clkrst/clkrst_registers.hpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::clkrst { + + /* Clock source enums. */ + #define CLK_RST_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (CLK_RST_CONTROLLER, NAME) + #define CLK_RST_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (CLK_RST_CONTROLLER, NAME, VALUE) + #define CLK_RST_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (CLK_RST_CONTROLLER, NAME, ENUM) + #define CLK_RST_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(CLK_RST_CONTROLLER, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_CLK_RST_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (CLK_RST_CONTROLLER, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_CLK_RST_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_CLK_RST_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_CLK_RST_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_CLK_RST_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + + #define CLK_RST_CONTROLLER_RST_SOURCE (0x000) + + #define CLK_RST_CONTROLLER_MISC_CLK_ENB (0x048) + + DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); + + /* RST_DEVICES */ + #define CLK_RST_CONTROLLER_RST_DEVICES_L (0x004) + #define CLK_RST_CONTROLLER_RST_DEVICES_H (0x008) + #define CLK_RST_CONTROLLER_RST_DEVICES_U (0x00C) + #define CLK_RST_CONTROLLER_RST_DEVICES_X (0x28C) + #define CLK_RST_CONTROLLER_RST_DEVICES_Y (0x2A4) + #define CLK_RST_CONTROLLER_RST_DEVICES_V (0x358) + #define CLK_RST_CONTROLLER_RST_DEVICES_W (0x35C) + + /* CLK_OUT_ENB */ + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_L (0x010) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_H (0x014) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_U (0x018) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_X (0x280) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_Y (0x298) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_V (0x360) + #define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364) + + /* CLK_SOURCE */ + #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178) + #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C) + #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) + + /* CLK_ENB_*_INDEX */ + #define CLK_RST_CONTROLLER_CLK_ENB_UARTA_INDEX (0x06) + #define CLK_RST_CONTROLLER_CLK_ENB_UARTB_INDEX (0x07) + #define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17) + + DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) + DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) + DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) + +} diff --git a/libraries/libexosphere/source/fuse/fuse_api.cpp b/libraries/libexosphere/source/fuse/fuse_api.cpp new file mode 100644 index 000000000..73f48494d --- /dev/null +++ b/libraries/libexosphere/source/fuse/fuse_api.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "fuse_registers.hpp" + +namespace ams::fuse { + + namespace { + + struct OdmWord4 { + using HardwareState1 = util::BitPack32::Field<0, 2, int>; + using HardwareType1 = util::BitPack32::Field; + using DramId = util::BitPack32::Field; + using HardwareType2 = util::BitPack32::Field; + using HardwareState2 = util::BitPack32::Field; + using QuestState = util::BitPack32::Field; + using FormatVersion = util::BitPack32::Field; + using Reserved = util::BitPack32::Field; + using HardwareType3 = util::BitPack32::Field; + }; + + constexpr ALWAYS_INLINE int GetHardwareStateValue(const util::BitPack32 odm_word4) { + constexpr auto HardwareState1Shift = 0; + constexpr auto HardwareState2Shift = OdmWord4::HardwareState1::Count + HardwareState1Shift; + + return (odm_word4.Get() << HardwareState1Shift) | + (odm_word4.Get() << HardwareState2Shift); + } + + constexpr ALWAYS_INLINE int GetHardwareTypeValue(const util::BitPack32 odm_word4) { + constexpr auto HardwareType1Shift = 0; + constexpr auto HardwareType2Shift = OdmWord4::HardwareType1::Count + HardwareType1Shift; + constexpr auto HardwareType3Shift = OdmWord4::HardwareType2::Count + HardwareType2Shift; + + return (odm_word4.Get() << HardwareType1Shift) | + (odm_word4.Get() << HardwareType2Shift) | + (odm_word4.Get() << HardwareType3Shift); + } + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceFuses.GetAddress(); + + ALWAYS_INLINE volatile FuseRegisterRegion *GetRegisterRegion() { + return reinterpret_cast(g_register_address); + } + + ALWAYS_INLINE volatile FuseRegisters &GetRegisters() { + return GetRegisterRegion()->fuse; + } + + ALWAYS_INLINE volatile FuseChipRegisters &GetChipRegisters() { + return GetRegisterRegion()->chip; + } + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void SetWriteSecureOnly() { + reg::Write(GetRegisters().FUSE_PRIVATEKEYDISABLE, FUSE_REG_BITS_ENUM(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, KEY_INVISIBLE)); + } + + void Lockout() { + reg::Write(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_DISABLEREGPROGRAM_VAL, ENABLE)); + } + + u32 GetOdmWord(int index) { + return GetChipRegisters().FUSE_RESERVED_ODM[index]; + } + + HardwareType GetHardwareType() { + /* Read the odm word. */ + const util::BitPack32 odm_word4 = { GetOdmWord(4) }; + + /* Get the value. */ + const auto value = GetHardwareTypeValue(odm_word4); + + switch (value) { + case 0x01: return HardwareType_Icosa; + case 0x02: return (true /* TODO: GetSocType() == SocType_Mariko */) ? HardwareType_Calcio : HardwareType_Copper; + case 0x04: return HardwareType_Iowa; + case 0x08: return HardwareType_Hoag; + case 0x10: return HardwareType_Five; + default: return HardwareType_Undefined; + } + } + + HardwareState GetHardwareState() { + /* Read the odm word. */ + const util::BitPack32 odm_word4 = { GetOdmWord(4) }; + + /* Get the value. */ + const auto value = GetHardwareStateValue(odm_word4); + + switch (value) { + case 3: return HardwareState_Development; + case 4: return HardwareState_Production; + default: return HardwareState_Undefined; + } + } + + pmic::Regulator GetRegulator() { + /* TODO: How should mariko be handled? This reads from ODM word 28 in fuses (not presesnt in erista...). */ + return pmic::Regulator_Erista_Max77621; + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/fuse/fuse_registers.hpp b/libraries/libexosphere/source/fuse/fuse_registers.hpp new file mode 100644 index 000000000..012ef2a87 --- /dev/null +++ b/libraries/libexosphere/source/fuse/fuse_registers.hpp @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::fuse { + + struct FuseRegisters { + u32 FUSE_FUSECTRL; + u32 FUSE_FUSEADDR; + u32 FUSE_FUSERDATA; + u32 FUSE_FUSEWDATA; + u32 FUSE_FUSETIME_RD1; + u32 FUSE_FUSETIME_RD2; + u32 FUSE_FUSETIME_PGM1; + u32 FUSE_FUSETIME_PGM2; + u32 FUSE_PRIV2INTFC_START; + u32 FUSE_FUSEBYPASS; + u32 FUSE_PRIVATEKEYDISABLE; + u32 FUSE_DISABLEREGPROGRAM; + u32 FUSE_WRITE_ACCESS_SW; + u32 FUSE_PWR_GOOD_SW; + u32 _0x38; + u32 FUSE_PRIV2RESHIFT; + u32 _0x40[0x3]; + u32 FUSE_FUSETIME_RD3; + u32 _0x50[0xC]; + u32 FUSE_PRIVATE_KEY0_NONZERO; + u32 FUSE_PRIVATE_KEY1_NONZERO; + u32 FUSE_PRIVATE_KEY2_NONZERO; + u32 FUSE_PRIVATE_KEY3_NONZERO; + u32 FUSE_PRIVATE_KEY4_NONZERO; + u32 _0x94[0x1B]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(FuseRegisters) == 0x100); + + struct FuseChipRegisters { + u32 FUSE_PRODUCTION_MODE; + u32 FUSE_JTAG_SECUREID_VALID; + u32 FUSE_ODM_LOCK; + u32 FUSE_OPT_OPENGL_EN; + u32 FUSE_SKU_INFO; + u32 FUSE_CPU_SPEEDO_0_CALIB; + u32 FUSE_CPU_IDDQ_CALIB; + u32 FUSE_DAC_CRT_CALIB; + u32 FUSE_DAC_HDTV_CALIB; + u32 FUSE_DAC_SDTV_CALIB; + u32 FUSE_OPT_FT_REV; + u32 FUSE_CPU_SPEEDO_1_CALIB; + u32 FUSE_CPU_SPEEDO_2_CALIB; + u32 FUSE_SOC_SPEEDO_0_CALIB; + u32 FUSE_SOC_SPEEDO_1_CALIB; + u32 FUSE_SOC_SPEEDO_2_CALIB; + u32 FUSE_SOC_IDDQ_CALIB; + u32 FUSE_RESERVED_PRODUCTION_WP; + u32 FUSE_FA; + u32 FUSE_RESERVED_PRODUCTION; + u32 FUSE_HDMI_LANE0_CALIB; + u32 FUSE_HDMI_LANE1_CALIB; + u32 FUSE_HDMI_LANE2_CALIB; + u32 FUSE_HDMI_LANE3_CALIB; + u32 FUSE_ENCRYPTION_RATE; + u32 FUSE_PUBLIC_KEY[0x8]; + u32 FUSE_TSENSOR1_CALIB; + u32 FUSE_TSENSOR2_CALIB; + u32 FUSE_VSENSOR_CALIB; + u32 FUSE_OPT_CP_REV; + u32 FUSE_OPT_PFG; + u32 FUSE_TSENSOR0_CALIB; + u32 FUSE_FIRST_BOOTROM_PATCH_SIZE; + u32 FUSE_SECURITY_MODE; + u32 FUSE_PRIVATE_KEY[0x5]; + u32 FUSE_ARM_JTAG_DIS; + u32 FUSE_BOOT_DEVICE_INFO; + u32 FUSE_RESERVED_SW; + u32 FUSE_OPT_VP9_DISABLE; + u32 FUSE_RESERVED_ODM[0x8]; + u32 FUSE_OBS_DIS; + u32 FUSE_NOR_INFO; + u32 FUSE_USB_CALIB; + u32 FUSE_SKU_DIRECT_CONFIG; + u32 FUSE_KFUSE_PRIVKEY_CTRL; + u32 FUSE_PACKAGE_INFO; + u32 FUSE_OPT_VENDOR_CODE; + u32 FUSE_OPT_FAB_CODE; + u32 FUSE_OPT_LOT_CODE_0; + u32 FUSE_OPT_LOT_CODE_1; + u32 FUSE_OPT_WAFER_ID; + u32 FUSE_OPT_X_COORDINATE; + u32 FUSE_OPT_Y_COORDINATE; + u32 FUSE_OPT_SEC_DEBUG_EN; + u32 FUSE_OPT_OPS_RESERVED; + u32 FUSE_SATA_CALIB; + u32 FUSE_GPU_IDDQ_CALIB; + u32 FUSE_TSENSOR3_CALIB; + u32 FUSE_SKU_BOND_OUT_L; + u32 FUSE_SKU_BOND_OUT_H; + u32 FUSE_SKU_BOND_OUT_U; + u32 FUSE_SKU_BOND_OUT_V; + u32 FUSE_SKU_BOND_OUT_W; + u32 FUSE_OPT_SAMPLE_TYPE; + u32 FUSE_OPT_SUBREVISION; + u32 FUSE_OPT_SW_RESERVED_0; + u32 FUSE_OPT_SW_RESERVED_1; + u32 FUSE_TSENSOR4_CALIB; + u32 FUSE_TSENSOR5_CALIB; + u32 FUSE_TSENSOR6_CALIB; + u32 FUSE_TSENSOR7_CALIB; + u32 FUSE_OPT_PRIV_SEC_EN; + u32 FUSE_PKC_DISABLE; + u32 _0x16C; + u32 _0x170; + u32 _0x174; + u32 _0x178; + u32 FUSE_FUSE2TSEC_DEBUG_DISABLE; + u32 FUSE_TSENSOR_COMMON; + u32 FUSE_OPT_CP_BIN; + u32 FUSE_OPT_GPU_DISABLE; + u32 FUSE_OPT_FT_BIN; + u32 FUSE_OPT_DONE_MAP; + u32 _0x194; + u32 FUSE_APB2JTAG_DISABLE; + u32 FUSE_ODM_INFO; + u32 _0x1A0; + u32 _0x1A4; + u32 FUSE_ARM_CRYPT_DE_FEATURE; + u32 _0x1AC; + u32 _0x1B0; + u32 _0x1B4; + u32 _0x1B8; + u32 _0x1BC; + u32 FUSE_WOA_SKU_FLAG; + u32 FUSE_ECO_RESERVE_1; + u32 FUSE_GCPLEX_CONFIG_FUSE; + u32 FUSE_PRODUCTION_MONTH; + u32 FUSE_RAM_REPAIR_INDICATOR; + u32 FUSE_TSENSOR9_CALIB; + u32 _0x1D8; + u32 FUSE_VMIN_CALIBRATION; + u32 FUSE_AGING_SENSOR_CALIBRATION; + u32 FUSE_DEBUG_AUTHENTICATION; + u32 FUSE_SECURE_PROVISION_INDEX; + u32 FUSE_SECURE_PROVISION_INFO; + u32 FUSE_OPT_GPU_DISABLE_CP1; + u32 FUSE_SPARE_ENDIS; + u32 FUSE_ECO_RESERVE_0; + u32 _0x1FC; + u32 _0x200; + u32 FUSE_RESERVED_CALIB0; + u32 FUSE_RESERVED_CALIB1; + u32 FUSE_OPT_GPU_TPC0_DISABLE; + u32 FUSE_OPT_GPU_TPC0_DISABLE_CP1; + u32 FUSE_OPT_CPU_DISABLE; + u32 FUSE_OPT_CPU_DISABLE_CP1; + u32 FUSE_TSENSOR10_CALIB; + u32 FUSE_TSENSOR10_CALIB_AUX; + u32 FUSE_OPT_RAM_SVOP_DP; + u32 FUSE_OPT_RAM_SVOP_PDP; + u32 FUSE_OPT_RAM_SVOP_REG; + u32 FUSE_OPT_RAM_SVOP_SP; + u32 FUSE_OPT_RAM_SVOP_SMPDP; + u32 FUSE_OPT_GPU_TPC0_DISABLE_CP2; + u32 FUSE_OPT_GPU_TPC1_DISABLE; + u32 FUSE_OPT_GPU_TPC1_DISABLE_CP1; + u32 FUSE_OPT_GPU_TPC1_DISABLE_CP2; + u32 FUSE_OPT_CPU_DISABLE_CP2; + u32 FUSE_OPT_GPU_DISABLE_CP2; + u32 FUSE_USB_CALIB_EXT; + u32 FUSE_RESERVED_FIELD; + u32 FUSE_OPT_ECC_EN; + u32 _0x25C; + u32 _0x260; + u32 _0x264; + u32 _0x268; + u32 _0x26C; + u32 _0x270; + u32 _0x274; + u32 _0x278; + u32 FUSE_SPARE_REALIGNMENT_REG; + u32 FUSE_SPARE_BIT[0x20]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(FuseChipRegisters) == 0x300); + + struct FuseRegisterRegion { + FuseRegisters fuse; + FuseChipRegisters chip; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(FuseRegisterRegion) == secmon::MemoryRegionPhysicalDeviceFuses.GetSize()); + + #define FUSE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (FUSE, NAME) + #define FUSE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (FUSE, NAME, VALUE) + #define FUSE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (FUSE, NAME, ENUM) + #define FUSE_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(FUSE, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_FUSE_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (FUSE, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_FUSE_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (FUSE, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_FUSE_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_FUSE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_FUSE_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + DEFINE_FUSE_REG_BIT_ENUM(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, 4, KEY_VISIBLE, KEY_INVISIBLE); + DEFINE_FUSE_REG_BIT_ENUM(PRIVATEKEYDISABLE_PRIVATEKEYDISABLE_VAL_KEY, 0, VISIBLE, INVISIBLE); + + DEFINE_FUSE_REG_BIT_ENUM(DISABLEREGPROGRAM_DISABLEREGPROGRAM_VAL, 0, DISABLE, ENABLE); + +} diff --git a/libraries/libexosphere/source/gic/gic_api.cpp b/libraries/libexosphere/source/gic/gic_api.cpp new file mode 100644 index 000000000..70e36002c --- /dev/null +++ b/libraries/libexosphere/source/gic/gic_api.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::gic { + + namespace { + + struct GicDistributor { + u32 ctlr; + u32 typer; + u32 iidr; + u32 reserved_0x0c; + u32 statusr; + u32 reserved_0x14[3]; + u32 impldef_0x20[8]; + u32 setspi_nsr; + u32 reserved_0x44; + u32 clrspi_nsr; + u32 reserved_0x4c; + u32 setspi_sr; + u32 reserved_0x54; + u32 clrspi_sr; + u32 reserved_0x5c[9]; + u32 igroupr[32]; + u32 isenabler[32]; + u32 icenabler[32]; + u32 ispendr[32]; + u32 icpendr[32]; + u32 isactiver[32]; + u32 icactiver[32]; + union { + u8 bytes[1020]; + u32 words[255]; + } ipriorityr; + u32 _0x7fc; + union { + u8 bytes[1020]; + u32 words[255]; + } itargetsr; + u32 _0xbfc; + u32 icfgr[64]; + u32 igrpmodr[32]; + u32 _0xd80[32]; + u32 nsacr[64]; + u32 sgir; + u32 _0xf04[3]; + u32 cpendsgir[4]; + u32 spendsgir[4]; + u32 reserved_0xf30[52]; + + static constexpr size_t SgirCpuTargetListShift = 16; + + enum SgirTargetListFilter : u32 { + SgirTargetListFilter_CpuTargetList = (0 << 24), + SgirTargetListFilter_Others = (1 << 24), + SgirTargetListFilter_Self = (2 << 24), + SgirTargetListFilter_Reserved = (3 << 24), + }; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(GicDistributor) == 0x1000); + static_assert(sizeof(GicDistributor) == secmon::MemoryRegionPhysicalDeviceGicDistributor.GetSize()); + + struct GicCpuInterface { + u32 ctlr; + u32 pmr; + u32 bpr; + u32 iar; + u32 eoir; + u32 rpr; + u32 hppir; + u32 abpr; + u32 aiar; + u32 aeoir; + u32 ahppir; + u32 statusr; + u32 reserved_30[4]; + u32 impldef_40[36]; + u32 apr[4]; + u32 nsapr[4]; + u32 reserved_f0[3]; + u32 iidr; + u32 reserved_100[960]; + u32 dir; + u32 _0x1004[1023]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(GicCpuInterface) == 0x2000); + static_assert(sizeof(GicCpuInterface) == secmon::MemoryRegionPhysicalDeviceGicCpuInterface.GetSize()); + + constexpr inline int InterruptWords = InterruptCount / BITSIZEOF(u32); + constexpr inline int SpiIndex = BITSIZEOF(u32); + + constinit uintptr_t g_distributor_address = secmon::MemoryRegionPhysicalDeviceGicDistributor.GetAddress(); + constinit uintptr_t g_cpu_interface_address = secmon::MemoryRegionPhysicalDeviceGicCpuInterface.GetAddress(); + + volatile GicDistributor *GetDistributor() { + return reinterpret_cast(g_distributor_address); + } + + volatile GicCpuInterface *GetCpuInterface() { + return reinterpret_cast(g_cpu_interface_address); + } + + void ReadWrite(uintptr_t address, int width, int i, u32 value) { + /* This code will never be invoked with a negative interrupt id. */ + AMS_ASSUME(i >= 0); + + const int scale = BITSIZEOF(u32) / width; + const int word = i / scale; + const int bit = (i % scale) * width; + + reg::ReadWrite(address + sizeof(u32) * word, REG_BITS_VALUE(bit, width, value)); + } + + void Write(uintptr_t address, int width, int i, u32 value) { + /* This code will never be invoked with a negative interrupt id. */ + AMS_ASSUME(i >= 0); + + const int scale = BITSIZEOF(u32) / width; + const int word = i / scale; + const int bit = (i % scale) * width; + + reg::Write(address + sizeof(u32) * word, value << bit); + } + + } + + void SetRegisterAddress(uintptr_t distributor_address, uintptr_t cpu_interface_address) { + g_distributor_address = distributor_address; + g_cpu_interface_address = cpu_interface_address; + } + + void InitializeCommon() { + /* Get the gicd registers. */ + auto *gicd = GetDistributor(); + + /* Set IGROUPR for to be FFs. */ + for (int i = SpiIndex / BITSIZEOF(u32); i < InterruptWords; ++i) { + gicd->igroupr[i] = 0xFFFFFFFFu; + } + + /* Set IPRIORITYR for spi interrupts to be 0x80. */ + for (int i = SpiIndex; i < InterruptCount; ++i) { + gicd->ipriorityr.bytes[i] = 0x80; + } + + /* Enable group 0. */ + gicd->ctlr = 1; + } + + void InitializeCoreUnique() { + /* Get the registers. */ + auto *gicd = GetDistributor(); + auto *gicc = GetCpuInterface(); + + /* Set IGROUPR0 to be FFs. */ + gicd->igroupr[0] = 0xFFFFFFFFu; + + /* Set IPRIORITYR for core local interrupts to be 0x80. */ + for (int i = 0; i < SpiIndex; ++i) { + gicd->ipriorityr.bytes[i] = 0x80; + } + + /* Enable group 0 as FIQs. */ + gicc->ctlr = 0x1D9; + + /* Set PMR. */ + gicc->pmr = 0x80; + + /* Set BPR. */ + gicc->bpr = 7; + } + + void SetPriority(int interrupt_id, int priority) { + ReadWrite(g_distributor_address + offsetof(GicDistributor, ipriorityr), BITSIZEOF(u8), interrupt_id, priority); + } + + void SetInterruptGroup(int interrupt_id, int group) { + ReadWrite(g_distributor_address + offsetof(GicDistributor, igroupr), 1, interrupt_id, group); + } + + void SetEnable(int interrupt_id, bool enable) { + Write(g_distributor_address + offsetof(GicDistributor, isenabler), 1, interrupt_id, enable); + } + + void SetSpiTargetCpu(int interrupt_id, u32 cpu_mask) { + ReadWrite(g_distributor_address + offsetof(GicDistributor, itargetsr), BITSIZEOF(u8), interrupt_id, cpu_mask); + } + + void SetSpiMode(int interrupt_id, InterruptMode mode) { + ReadWrite(g_distributor_address + offsetof(GicDistributor, icfgr), 2, interrupt_id, static_cast(mode) << 1); + } + + int GetInterruptRequestId() { + return reg::Read(GetCpuInterface()->iar); + } + + void SetEndOfInterrupt(int interrupt_id) { + reg::Write(GetCpuInterface()->eoir, interrupt_id); + } + +} diff --git a/libraries/libexosphere/source/hw/hw_cache.arch.arm64.cpp b/libraries/libexosphere/source/hw/hw_cache.arch.arm64.cpp new file mode 100644 index 000000000..be19c1183 --- /dev/null +++ b/libraries/libexosphere/source/hw/hw_cache.arch.arm64.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::hw::arch::arm64 { + + void FlushDataCache(const void *ptr, size_t size) { + const uintptr_t start = reinterpret_cast(ptr); + const uintptr_t end = util::AlignUp(start + size, hw::DataCacheLineSize); + + for (uintptr_t cur = start; cur < end; cur += hw::DataCacheLineSize) { + FlushDataCacheLine(reinterpret_cast(cur)); + } + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/i2c/i2c_api.cpp b/libraries/libexosphere/source/i2c/i2c_api.cpp new file mode 100644 index 000000000..108429768 --- /dev/null +++ b/libraries/libexosphere/source/i2c/i2c_api.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "i2c_registers.hpp" + +namespace ams::i2c { + + namespace { + + constexpr inline size_t MaxTransferSize = sizeof(u32); + + constinit std::array g_register_addresses = [] { + std::array arr = {}; + + arr[Port_1] = secmon::MemoryRegionPhysicalDeviceI2c1.GetAddress(); + arr[Port_5] = secmon::MemoryRegionPhysicalDeviceI2c5.GetAddress(); + + return arr; + }(); + + void LoadConfig(uintptr_t address) { + /* Configure for TIMEOUT and MSTR config load. */ + /* NOTE: Nintendo writes value 1 to reserved bit 5 here. This bit is documented as having no meaning. */ + /* We will reproduce the write just in case it is undocumented. */ + reg::Write(address + I2C_CONFIG_LOAD, I2C_REG_BITS_VALUE(CONFIG_LOAD_RESERVED_BIT_5, 1), + I2C_REG_BITS_ENUM (CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, ENABLE), + I2C_REG_BITS_ENUM (CONFIG_LOAD_SLV_CONFIG_LOAD, DISABLE), + I2C_REG_BITS_ENUM (CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); + + /* Wait up to 20 microseconds for the master config to be loaded. */ + for (int i = 0; i < 20; ++i) { + if (reg::HasValue(address + I2C_CONFIG_LOAD, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, DISABLE))) { + return; + } + util::WaitMicroSeconds(1); + } + } + + void ClearBus(uintptr_t address) { + /* Configure the bus clear register. */ + reg::Write(address + I2C_BUS_CLEAR_CONFIG, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_STOP_COND, NO_STOP), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE)); + + /* Load the config. */ + LoadConfig(address); + + /* Wait up to 250us (in 25 us increments) until the bus clear is done. */ + for (int i = 0; i < 10; ++i) { + if (reg::HasValue(address + I2C_INTERRUPT_STATUS_REGISTER, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, SET))) { + break; + } + + util::WaitMicroSeconds(25); + } + + /* Read the bus clear status. */ + reg::Read(address + I2C_BUS_CLEAR_STATUS); + } + + void InitializePort(uintptr_t address) { + /* Calculate the divisor. */ + constexpr int Divisor = util::DivideUp(19200, 8 * 400); + + /* Set the divisor. */ + reg::Write(address + I2C_CLK_DIVISOR_REGISTER, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, Divisor - 1), + I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, 1)); + + /* Clear the bus. */ + ClearBus(address); + + /* Clear the status. */ + reg::Write(address + I2C_INTERRUPT_STATUS_REGISTER, reg::Read(address + I2C_INTERRUPT_STATUS_REGISTER)); + } + + bool Write(uintptr_t base_address, Port port, int address, const void *src, size_t src_size, bool unused) { + /* Ensure we don't write too much. */ + u32 data = 0; + if (src_size > MaxTransferSize) { + return false; + } + + /* Copy the data to a transfer word. */ + std::memcpy(std::addressof(data), src, src_size); + + + /* Configure the to write the 7-bit address. */ + reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address), + I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, WRITE)); + + /* Configure to write the data. */ + reg::Write(base_address + I2C_I2C_CMD_DATA1, data); + + /* Configure to write the correct amount of data. */ + reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T), + I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE), + I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, WRITE), + I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, src_size - 1)); + + /* Load the configuration. */ + LoadConfig(base_address); + + /* Start the command. */ + reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO)); + + /* Wait for the command to be done. */ + while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ } + + /* Check if the transfer was successful. */ + return reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL)); + } + + bool Read(uintptr_t base_address, Port port, void *dst, size_t dst_size, int address, bool unused) { + /* Ensure we don't read too much. */ + if (dst_size > MaxTransferSize) { + return false; + } + + /* Configure the to read the 7-bit address. */ + reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address), + I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, READ)); + + /* Configure to read the correct amount of data. */ + reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T), + I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE), + I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, READ), + I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, dst_size - 1)); + + /* Load the configuration. */ + LoadConfig(base_address); + + /* Start the command. */ + reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO)); + + /* Wait for the command to be done. */ + while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ } + + /* Check that the transfer was successful. */ + if (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL))) { + return false; + } + + /* Read and copy out the data. */ + u32 data = reg::Read(base_address + I2C_I2C_CMD_DATA1); + std::memcpy(dst, std::addressof(data), dst_size); + return true; + } + + } + + void SetRegisterAddress(Port port, uintptr_t address) { + g_register_addresses[port] = address; + } + + void Initialize(Port port) { + InitializePort(g_register_addresses[port]); + } + + bool Query(void *dst, size_t dst_size, Port port, int address, int r) { + const uintptr_t base_address = g_register_addresses[port]; + + /* Select the register we want to read. */ + bool success = Write(base_address, port, address, std::addressof(r), 1, false); + if (success) { + /* If we successfully selected, read data from the register. */ + success = Read(base_address, port, dst, dst_size, address, true); + } + + return success; + } + + bool Send(Port port, int address, int r, const void *src, size_t src_size) { + const uintptr_t base_address = g_register_addresses[port]; + + /* Create a transfer buffer, make sure we can use it. */ + u8 buffer[MaxTransferSize]; + if (src_size > sizeof(buffer) - 1) { + return false; + } + + /* Copy data into the buffer. */ + buffer[0] = static_cast(r); + std::memcpy(buffer + 1, src, src_size); + + return Write(base_address, port, address, buffer, src_size + 1, false); + } + +} diff --git a/libraries/libexosphere/source/i2c/i2c_registers.hpp b/libraries/libexosphere/source/i2c/i2c_registers.hpp new file mode 100644 index 000000000..48f659574 --- /dev/null +++ b/libraries/libexosphere/source/i2c/i2c_registers.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::i2c { + + #define I2C_I2C_CNFG (0x000) + #define I2C_I2C_CMD_ADDR0 (0x004) + #define I2C_I2C_CMD_DATA1 (0x00C) + #define I2C_I2C_STATUS (0x01C) + #define I2C_INTERRUPT_STATUS_REGISTER (0x068) + #define I2C_CLK_DIVISOR_REGISTER (0x06C) + #define I2C_BUS_CLEAR_CONFIG (0x084) + #define I2C_BUS_CLEAR_STATUS (0x088) + #define I2C_CONFIG_LOAD (0x08C) + + #define I2C_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (I2C, NAME) + #define I2C_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (I2C, NAME, VALUE) + #define I2C_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (I2C, NAME, ENUM) + #define I2C_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(I2C, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_I2C_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (I2C, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_I2C_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_I2C_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_I2C_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_I2C_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + /* I2C_CNFG */ + DEFINE_I2C_REG(I2C_CNFG_LENGTH, 1, 3); + DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_CMD1, 6, WRITE, READ); + DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_SEND, 9, NOP, GO); + DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_NEW_MASTER_FSM, 11, DISABLE, ENABLE); + DEFINE_I2C_REG_THREE_BIT_ENUM(I2C_CNFG_DEBOUNCE_CNT, 12, NO_DEBOUNCE, DEBOUNCE_2T, DEBOUNCE_4T, DEBOUNCE_6T, DEBOUNCE_8T, DEBOUNCE_10T, DEBOUNCE_12T, DEBOUNCE_14T); + + /* I2C_CMD_ADDR0 */ + DEFINE_I2C_REG_BIT_ENUM(I2C_CMD_ADDR0_7BIT_RW, 0, WRITE, READ); + DEFINE_I2C_REG(I2C_CMD_ADDR0_7BIT_ADDR, 1, 7); + + /* I2C_STATUS */ + DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD1_STAT, 0, SL1_XFER_SUCCESSFUL, SL1_NOACK_FOR_BYTE1, SL1_NOACK_FOR_BYTE2, SL1_NOACK_FOR_BYTE3, SL1_NOACK_FOR_BYTE4, SL1_NOACK_FOR_BYTE5, SL1_NOACK_FOR_BYTE6, SL1_NOACK_FOR_BYTE7, SL1_NOACK_FOR_BYTE8, SL1_NOACK_FOR_BYTE9, SL1_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); + DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD2_STAT, 4, SL2_XFER_SUCCESSFUL, SL2_NOACK_FOR_BYTE1, SL2_NOACK_FOR_BYTE2, SL2_NOACK_FOR_BYTE3, SL2_NOACK_FOR_BYTE4, SL2_NOACK_FOR_BYTE5, SL2_NOACK_FOR_BYTE6, SL2_NOACK_FOR_BYTE7, SL2_NOACK_FOR_BYTE8, SL2_NOACK_FOR_BYTE9, SL2_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); + DEFINE_I2C_REG_BIT_ENUM(I2C_STATUS_BUSY, 8, NOT_BUSY, BUSY); + + /* INTERRUPT_STATUS_REGISTER */ + DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, 11, UNSET, SET); + + /* CLK_DIVISOR_REGISTER */ + DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_HSMODE, 0, 16); + DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_STD_FAST_MODE, 16, 16); + + /* BUS_CLEAR_CONFIG */ + DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, 0, DISABLE, ENABLE); + DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, 1, THRESHOLD, IMMEDIATE); + DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, 2, NO_STOP, STOP); + DEFINE_I2C_REG(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 16, 8); + + /* CONFIG_LOAD */ + DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, 0, DISABLE, ENABLE); + DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_SLV_CONFIG_LOAD, 1, DISABLE, ENABLE); + DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, 2, DISABLE, ENABLE); + DEFINE_I2C_REG(CONFIG_LOAD_RESERVED_BIT_5, 5, 1); + + +} diff --git a/libraries/libexosphere/source/libc/libc.c b/libraries/libexosphere/source/libc/libc.c new file mode 100644 index 000000000..2117f2483 --- /dev/null +++ b/libraries/libexosphere/source/libc/libc.c @@ -0,0 +1,1141 @@ +/* Note: copied from newlib */ +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* + * Copyright (C) 2004 CodeSourcery, LLC + * + * Permission to use, copy, modify, and distribute this file + * for any purpose is hereby granted without fee, provided that + * the above copyright notice and this notice appears in all + * copies. + * + * This file is distributed WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* Handle ELF .{pre_init,init,fini}_array sections. */ +#include + +#ifndef HAVE_INITFINI_ARRAY +#define HAVE_INITFINI_ARRAY +#endif + +#undef HAVE_INIT_FINI + +#ifdef HAVE_INITFINI_ARRAY + +/* These magic symbols are provided by the linker. */ +extern void (*__preinit_array_start []) (void) __attribute__((weak)); +extern void (*__preinit_array_end []) (void) __attribute__((weak)); +extern void (*__init_array_start []) (void) __attribute__((weak)); +extern void (*__init_array_end []) (void) __attribute__((weak)); + +#ifdef HAVE_INIT_FINI +extern void _init (void); +#endif + +/* Iterate over all the init routines. */ +void +__libc_init_array (void) +{ + size_t count; + size_t i; + + count = __preinit_array_end - __preinit_array_start; + for (i = 0; i < count; i++) + __preinit_array_start[i] (); + +#ifdef HAVE_INIT_FINI + _init (); +#endif + + count = __init_array_end - __init_array_start; + for (i = 0; i < count; i++) + __init_array_start[i] (); +} +#endif + +#ifdef HAVE_INITFINI_ARRAY +extern void (*__fini_array_start []) (void) __attribute__((weak)); +extern void (*__fini_array_end []) (void) __attribute__((weak)); + +#ifdef HAVE_INIT_FINI +extern void _fini (void); +#endif + +/* Run all the cleanup routines. */ +void +__libc_fini_array (void) +{ + size_t count; + size_t i; + + count = __fini_array_end - __fini_array_start; + for (i = count; i > 0; i--) + __fini_array_start[i-1] (); + +#ifdef HAVE_INIT_FINI + _fini (); +#endif +} +#endif + +/* +FUNCTION + <>---move possibly overlapping memory +INDEX + memmove +SYNOPSIS + #include + void *memmove(void *<[dst]>, const void *<[src]>, size_t <[length]>); +DESCRIPTION + This function moves <[length]> characters from the block of + memory starting at <<*<[src]>>> to the memory starting at + <<*<[dst]>>>. <> reproduces the characters correctly + at <<*<[dst]>>> even if the two areas overlap. +RETURNS + The function returns <[dst]> as passed. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + memmove ansi pure +*/ + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the 4X unrolled loop. */ +#define BIGBLOCKSIZE (sizeof (long) << 2) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LITTLEBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#undef TOO_SMALL +#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) + +/*SUPPRESS 20*/ +void * +//__inhibit_loop_to_libcall +memmove (void *dst_void, + const void *src_void, + size_t length) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dst = dst_void; + const char *src = src_void; + + if (src < dst && dst < src + length) + { + /* Have to copy backwards */ + src += length; + dst += length; + while (length--) + { + *--dst = *--src; + } + } + else + { + while (length--) + { + *dst++ = *src++; + } + } + + return dst_void; +#else + char *dst = dst_void; + const char *src = src_void; + long *aligned_dst; + const long *aligned_src; + + if (src < dst && dst < src + length) + { + /* Destructive overlap...have to copy backwards */ + src += length; + dst += length; + while (length--) + { + *--dst = *--src; + } + } + else + { + /* Use optimizing algorithm for a non-destructive copy to closely + match memcpy. If the size is small or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(length) && !UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* Copy 4X long words at a time if possible. */ + while (length >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + length -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (length >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + length -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (length--) + { + *dst++ = *src++; + } + } + + return dst_void; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---copy memory regions +SYNOPSIS + #include + void* memcpy(void *restrict <[out]>, const void *restrict <[in]>, + size_t <[n]>); +DESCRIPTION + This function copies <[n]> bytes from the memory region + pointed to by <[in]> to the memory region pointed to by + <[out]>. + If the regions overlap, the behavior is undefined. +RETURNS + <> returns a pointer to the first byte of the <[out]> + region. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + memcpy ansi pure + */ + +void * +memcpy (void * dst0, + const void * __restrict src0, + size_t len0) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dst = (char *) dst0; + char *src = (char *) src0; + + void *save = dst0; + + while (len0--) + { + *dst++ = *src++; + } + + return save; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If the size is small, or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(len0) && !UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* Copy 4X long words at a time if possible. */ + while (len0 >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + len0 -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (len0 >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + len0 -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (len0--) + *dst++ = *src++; + + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---set an area of memory +INDEX + memset +SYNOPSIS + #include + void *memset(void *<[dst]>, int <[c]>, size_t <[length]>); +DESCRIPTION + This function converts the argument <[c]> into an unsigned + char and fills the first <[length]> characters of the array + pointed to by <[dst]> to the value. +RETURNS + <> returns the value of <[dst]>. +PORTABILITY +<> is ANSI C. + <> requires no supporting OS subroutines. +QUICKREF + memset ansi pure +*/ + +#include + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define LBLOCKSIZE (sizeof(long)) +#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +void * +memset (void *m, + int c, + size_t n) +{ + char *s = (char *) m; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned int i; + unsigned long buffer; + unsigned long *aligned_addr; + unsigned int d = c & 0xff; /* To avoid sign extension, copy C to an + unsigned variable. */ + + while (UNALIGNED (s)) + { + if (n--) + *s++ = (char) c; + else + return m; + } + + if (!TOO_SMALL (n)) + { + /* If we get this far, we know that n is large and s is word-aligned. */ + aligned_addr = (unsigned long *) s; + + /* Store D into each char sized location in BUFFER so that + we can set large blocks quickly. */ + buffer = (d << 8) | d; + buffer |= (buffer << 16); + for (i = 32; i < LBLOCKSIZE * 8; i <<= 1) + buffer = (buffer << i) | buffer; + + /* Unroll the loop. */ + while (n >= LBLOCKSIZE*4) + { + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + n -= 4*LBLOCKSIZE; + } + + while (n >= LBLOCKSIZE) + { + *aligned_addr++ = buffer; + n -= LBLOCKSIZE; + } + /* Pick up the remainder with a bytewise loop. */ + s = (char*)aligned_addr; + } + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (n--) + *s++ = (char) c; + + return m; +} + +/* +FUNCTION + <>---find character in memory +INDEX + memchr +SYNOPSIS + #include + void *memchr(const void *<[src]>, int <[c]>, size_t <[length]>); +DESCRIPTION + This function searches memory starting at <<*<[src]>>> for the + character <[c]>. The search only ends with the first + occurrence of <[c]>, or after <[length]> characters; in + particular, <> does not terminate the search. +RETURNS + If the character <[c]> is found within <[length]> characters + of <<*<[src]>>>, a pointer to the character is returned. If + <[c]> is not found, then <> is returned. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + memchr ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X) ((long)X & (sizeof (long) - 1)) + +/* How many bytes are loaded each iteration of the word copy loop. */ +#define LBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the bytewise iterator. */ +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +#if LONG_MAX == 2147483647L +#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080) +#else +#if LONG_MAX == 9223372036854775807L +/* Nonzero if X (a long int) contains a NULL byte. */ +#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080) +#else +#error long int is not a 32bit or 64bit type. +#endif +#endif + +#ifndef DETECTNULL +#error long int is not a 32bit or 64bit byte +#endif + +/* DETECTCHAR returns nonzero if (long)X contains the byte used + to fill (long)MASK. */ +#define DETECTCHAR(X,MASK) (DETECTNULL(X ^ MASK)) + +void * +memchr (const void *src_void, + int c, + size_t length) +{ + const unsigned char *src = (const unsigned char *) src_void; + unsigned char d = c; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned long *asrc; + unsigned long mask; + unsigned int i; + + while (UNALIGNED (src)) + { + if (!length--) + return NULL; + if (*src == d) + return (void *) src; + src++; + } + + if (!TOO_SMALL (length)) + { + /* If we get this far, we know that length is large and src is + word-aligned. */ + /* The fast code reads the source one word at a time and only + performs the bytewise search on word-sized segments if they + contain the search character, which is detected by XORing + the word-sized segment with a word-sized block of the search + character and then detecting for the presence of NUL in the + result. */ + asrc = (unsigned long *) src; + mask = d << 8 | d; + mask = mask << 16 | mask; + for (i = 32; i < LBLOCKSIZE * 8; i <<= 1) + mask = (mask << i) | mask; + + while (length >= LBLOCKSIZE) + { + if (DETECTCHAR (*asrc, mask)) + break; + length -= LBLOCKSIZE; + asrc++; + } + + /* If there are fewer than LBLOCKSIZE characters left, + then we resort to the bytewise loop. */ + + src = (unsigned char *) asrc; + } + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (length--) + { + if (*src == d) + return (void *) src; + src++; + } + + return NULL; +} + +/* +FUNCTION + <>---compare two memory areas +INDEX + memcmp +SYNOPSIS + #include + int memcmp(const void *<[s1]>, const void *<[s2]>, size_t <[n]>); +DESCRIPTION + This function compares not more than <[n]> characters of the + object pointed to by <[s1]> with the object pointed to by <[s2]>. +RETURNS + The function returns an integer greater than, equal to or + less than zero according to whether the object pointed to by + <[s1]> is greater than, equal to or less than the object + pointed to by <[s2]>. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + memcmp ansi pure +*/ + + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +int +memcmp (const void *m1, + const void *m2, + size_t n) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + unsigned char *s1 = (unsigned char *) m1; + unsigned char *s2 = (unsigned char *) m2; + + while (n--) + { + if (*s1 != *s2) + { + return *s1 - *s2; + } + s1++; + s2++; + } + return 0; +#else + unsigned char *s1 = (unsigned char *) m1; + unsigned char *s2 = (unsigned char *) m2; + unsigned long *a1; + unsigned long *a2; + + /* If the size is too small, or either pointer is unaligned, + then we punt to the byte compare loop. Hopefully this will + not turn up in inner loops. */ + if (!TOO_SMALL(n) && !UNALIGNED(s1,s2)) + { + /* Otherwise, load and compare the blocks of memory one + word at a time. */ + a1 = (unsigned long*) s1; + a2 = (unsigned long*) s2; + while (n >= LBLOCKSIZE) + { + if (*a1 != *a2) + break; + a1++; + a2++; + n -= LBLOCKSIZE; + } + + /* check m mod LBLOCKSIZE remaining characters */ + + s1 = (unsigned char*)a1; + s2 = (unsigned char*)a2; + } + + while (n--) + { + if (*s1 != *s2) + return *s1 - *s2; + s1++; + s2++; + } + + return 0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---search for character in string +INDEX + strchr +SYNOPSIS + #include + char * strchr(const char *<[string]>, int <[c]>); +DESCRIPTION + This function finds the first occurence of <[c]> (converted to + a char) in the string pointed to by <[string]> (including the + terminating null character). +RETURNS + Returns a pointer to the located character, or a null pointer + if <[c]> does not occur in <[string]>. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strchr ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + + +/* Nonzero if X is not aligned on a "long" boundary. */ +#define UNALIGNED(X) ((long)X & (sizeof (long) - 1)) + +/* How many bytes are loaded each iteration of the word copy loop. */ +#define LBLOCKSIZE (sizeof (long)) + +char * +strchr (const char *s1, + int i) +{ + const unsigned char *s = (const unsigned char *)s1; + unsigned char c = i; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned long mask,j; + unsigned long *aligned_addr; + + /* Special case for finding 0. */ + if (!c) + { + while (UNALIGNED (s)) + { + if (!*s) + return (char *) s; + s++; + } + /* Operate a word at a time. */ + aligned_addr = (unsigned long *) s; + while (!DETECTNULL (*aligned_addr)) + aligned_addr++; + /* Found the end of string. */ + s = (const unsigned char *) aligned_addr; + while (*s) + s++; + return (char *) s; + } + + /* All other bytes. Align the pointer, then search a long at a time. */ + while (UNALIGNED (s)) + { + if (!*s) + return NULL; + if (*s == c) + return (char *) s; + s++; + } + + mask = c; + for (j = 8; j < LBLOCKSIZE * 8; j <<= 1) + mask = (mask << j) | mask; + + aligned_addr = (unsigned long *) s; + while (!DETECTNULL (*aligned_addr) && !DETECTCHAR (*aligned_addr, mask)) + aligned_addr++; + + /* The block of bytes currently pointed to by aligned_addr + contains either a null or the target char, or both. We + catch it using the bytewise search. */ + + s = (unsigned char *) aligned_addr; + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (*s && *s != c) + s++; + if (*s == c) + return (char *)s; + return NULL; +} + +/* +FUNCTION + <>---character string compare + +INDEX + strcmp +SYNOPSIS + #include + int strcmp(const char *<[a]>, const char *<[b]>); +DESCRIPTION + <> compares the string at <[a]> to + the string at <[b]>. +RETURNS + If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>, + <> returns a number greater than zero. If the two + strings match, <> returns zero. If <<*<[a]>>> + sorts lexicographically before <<*<[b]>>>, <> returns a + number less than zero. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strcmp ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +int +strcmp (const char *s1, + const char *s2) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + while (*s1 != '\0' && *s1 == *s2) + { + s1++; + s2++; + } + + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#else + unsigned long *a1; + unsigned long *a2; + + /* If s1 or s2 are unaligned, then compare bytes. */ + if (!UNALIGNED (s1, s2)) + { + /* If s1 and s2 are word-aligned, compare them a word at a time. */ + a1 = (unsigned long*)s1; + a2 = (unsigned long*)s2; + while (*a1 == *a2) + { + /* To get here, *a1 == *a2, thus if we find a null in *a1, + then the strings must be equal, so return zero. */ + if (DETECTNULL (*a1)) + return 0; + + a1++; + a2++; + } + + /* A difference was detected in last few bytes of s1, so search bytewise */ + s1 = (char*)a1; + s2 = (char*)a2; + } + + while (*s1 != '\0' && *s1 == *s2) + { + s1++; + s2++; + } + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---copy string +INDEX + strcpy +SYNOPSIS + #include + char *strcpy(char *<[dst]>, const char *<[src]>); +DESCRIPTION + <> copies the string pointed to by <[src]> + (including the terminating null character) to the array + pointed to by <[dst]>. +RETURNS + This function returns the initial value of <[dst]>. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strcpy ansi pure +*/ + +/*SUPPRESS 560*/ +/*SUPPRESS 530*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +char* +strcpy (char *dst0, + const char *src0) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *s = dst0; + + while ((*dst0++ = *src0++)) + ; + + return s; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If SRC or DEST is unaligned, then copy bytes. */ + if (!UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* SRC and DEST are both "long int" aligned, try to do "long int" + sized copies. */ + while (!DETECTNULL(*aligned_src)) + { + *aligned_dst++ = *aligned_src++; + } + + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while ((*dst++ = *src++)) + ; + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---character string length +INDEX + strlen +SYNOPSIS + #include + size_t strlen(const char *<[str]>); +DESCRIPTION + The <> function works out the length of the string + starting at <<*<[str]>>> by counting chararacters until it + reaches a <> character. +RETURNS + <> returns the character count. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strlen ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define LBLOCKSIZE (sizeof (long)) +#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) +size_t +strlen (const char *str) +{ + const char *start = str; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned long *aligned_addr; + + /* Align the pointer, so we can search a word at a time. */ + while (UNALIGNED (str)) + { + if (!*str) + return str - start; + str++; + } + + /* If the string is word-aligned, we can check for the presence of + a null in each word-sized block. */ + aligned_addr = (unsigned long *)str; + while (!DETECTNULL (*aligned_addr)) + aligned_addr++; + + /* Once a null is detected, we check each byte in that block for a + precise position of the null. */ + str = (char *) aligned_addr; + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (*str) + str++; + return str - start; +} + +/* +FUNCTION + <>---character string compare + +INDEX + strncmp +SYNOPSIS + #include + int strncmp(const char *<[a]>, const char * <[b]>, size_t <[length]>); +DESCRIPTION + <> compares up to <[length]> characters + from the string at <[a]> to the string at <[b]>. +RETURNS + If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>, + <> returns a number greater than zero. If the two + strings are equivalent, <> returns zero. If <<*<[a]>>> + sorts lexicographically before <<*<[b]>>>, <> returns a + number less than zero. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strncmp ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +int +strncmp (const char *s1, + const char *s2, + size_t n) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + if (n == 0) + return 0; + + while (n-- != 0 && *s1 == *s2) + { + if (n == 0 || *s1 == '\0') + break; + s1++; + s2++; + } + + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#else + unsigned long *a1; + unsigned long *a2; + + if (n == 0) + return 0; + + /* If s1 or s2 are unaligned, then compare bytes. */ + if (!UNALIGNED (s1, s2)) + { + /* If s1 and s2 are word-aligned, compare them a word at a time. */ + a1 = (unsigned long*)s1; + a2 = (unsigned long*)s2; + while (n >= sizeof (long) && *a1 == *a2) + { + n -= sizeof (long); + + /* If we've run out of bytes or hit a null, return zero + since we already know *a1 == *a2. */ + if (n == 0 || DETECTNULL (*a1)) + return 0; + + a1++; + a2++; + } + + /* A difference was detected in last few bytes of s1, so search bytewise */ + s1 = (char*)a1; + s2 = (char*)a2; + } + + while (n-- > 0 && *s1 == *s2) + { + /* If we've run out of bytes or hit a null, return zero + since we already know *s1 == *s2. */ + if (n == 0 || *s1 == '\0') + return 0; + s1++; + s2++; + } + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---counted copy string +INDEX + strncpy +SYNOPSIS + #include + char *strncpy(char *restrict <[dst]>, const char *restrict <[src]>, + size_t <[length]>); +DESCRIPTION + <> copies not more than <[length]> characters from the + the string pointed to by <[src]> (including the terminating + null character) to the array pointed to by <[dst]>. If the + string pointed to by <[src]> is shorter than <[length]> + characters, null characters are appended to the destination + array until a total of <[length]> characters have been + written. +RETURNS + This function returns the initial value of <[dst]>. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strncpy ansi pure +*/ + +/*SUPPRESS 560*/ +/*SUPPRESS 530*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +#define TOO_SMALL(LEN) ((LEN) < sizeof (long)) + +char * +strncpy (char *__restrict dst0, + const char *__restrict src0, + size_t count) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dscan; + const char *sscan; + + dscan = dst0; + sscan = src0; + while (count > 0) + { + --count; + if ((*dscan++ = *sscan++) == '\0') + break; + } + while (count-- > 0) + *dscan++ = '\0'; + + return dst0; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If SRC and DEST is aligned and count large enough, then copy words. */ + if (!UNALIGNED (src, dst) && !TOO_SMALL (count)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* SRC and DEST are both "long int" aligned, try to do "long int" + sized copies. */ + while (count >= sizeof (long int) && !DETECTNULL(*aligned_src)) + { + count -= sizeof (long int); + *aligned_dst++ = *aligned_src++; + } + + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (count > 0) + { + --count; + if ((*dst++ = *src++) == '\0') + break; + } + + while (count-- > 0) + *dst++ = '\0'; + + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---character string length + +INDEX + strnlen +SYNOPSIS + #include + size_t strnlen(const char *<[str]>, size_t <[n]>); +DESCRIPTION + The <> function works out the length of the string + starting at <<*<[str]>>> by counting chararacters until it + reaches a NUL character or the maximum: <[n]> number of + characters have been inspected. +RETURNS + <> returns the character count or <[n]>. +PORTABILITY +<> is a GNU extension. +<> requires no supporting OS subroutines. +*/ + +size_t +strnlen (const char *str, + size_t n) +{ + const char *start = str; + + while (n-- > 0 && *str) + str++; + + return str - start; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/libraries/libexosphere/source/log/log_api.cpp b/libraries/libexosphere/source/log/log_api.cpp new file mode 100644 index 000000000..0cf20e207 --- /dev/null +++ b/libraries/libexosphere/source/log/log_api.cpp @@ -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 . + */ +#include + +namespace ams::log { + + namespace { + + constexpr inline uart::Port UartLogPort = uart::Port_ReservedDebug; + constinit bool g_initialized_uart = false; + + constexpr inline u32 UartPortFlags = [] { + if constexpr (UartLogPort == uart::Port_ReservedDebug) { + /* Logging to the debug port. */ + /* Don't invert transactions. */ + return uart::Flag_None; + } else if constexpr (UartLogPort == uart::Port_LeftJoyCon) { + /* Logging to left joy-con (e.g. with Joyless). */ + /* Invert transactions. */ + return uart::Flag_Inverted; + } else if constexpr (UartLogPort == uart::Port_RightJoyCon) { + /* Logging to right joy-con (e.g. with Joyless). */ + /* Invert transactions. */ + return uart::Flag_Inverted; + } else { + __builtin_unreachable(); + } + }(); + + } + + void Initialize() { + /* Initialize the target uart port. */ + uart::Initialize(UartLogPort, 115200, UartPortFlags); + + /* Note that we've initialized. */ + g_initialized_uart = true; + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/pmc/pmc_api.cpp b/libraries/libexosphere/source/pmc/pmc_api.cpp new file mode 100644 index 000000000..67739e583 --- /dev/null +++ b/libraries/libexosphere/source/pmc/pmc_api.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::pmc { + + namespace { + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + + constexpr inline u32 WriteMask = 0x1; + constexpr inline u32 ReadMask = 0x2; + + enum class LockMode { + Read, + Write, + ReadWrite, + }; + + template + constexpr inline u32 LockMask = [] { + switch (Mode) { + case LockMode::Read: return ReadMask; + case LockMode::Write: return WriteMask; + case LockMode::ReadWrite: return ReadMask | WriteMask; + default: __builtin_unreachable(); + } + }(); + + constexpr inline size_t NumSecureScratchRegisters = 120; + constexpr inline size_t NumSecureDisableRegisters = 8; + + template requires (SecureScratch < NumSecureScratchRegisters) + constexpr inline std::pair DisableRegisterIndex = [] { + if constexpr (SecureScratch < 8) { + return std::pair{0, 4 + 2 * SecureScratch}; + } else { + constexpr size_t Relative = SecureScratch - 8; + return std::pair{1 + (Relative / 16), 2 * (Relative % 16)}; + } + }(); + + struct LockInfo { + size_t scratch; + LockMode mode; + }; + + template + constexpr ALWAYS_INLINE void SetSecureScratchMask(std::array &disables) { + constexpr std::pair Location = DisableRegisterIndex; + disables[Location.first] |= LockMask << Location.second; + } + + template + constexpr ALWAYS_INLINE void SetSecureScratchMasks(std::array &disables) { + (SetSecureScratchMask(disables), ...); + } + + template + constexpr ALWAYS_INLINE void SetSecureScratchReadWriteMasks(std::array &disables) { + (SetSecureScratchMask(disables), ...); + } + + template + constexpr ALWAYS_INLINE void SetSecureScratchReadMasks(std::array &disables) { + (SetSecureScratchMask(disables), ...); + } + + template + constexpr ALWAYS_INLINE void SetSecureScratchWriteMasks(std::array &disables) { + (SetSecureScratchMask(disables), ...); + } + + template + constexpr ALWAYS_INLINE std::array GetSecureScratchMasks() { + std::array disables = {}; + + if constexpr ((Register & SecureRegister_Other) != 0) { + constexpr std::array NonOtherDisables = GetSecureScratchMasks(~SecureRegister_Other)>(); + for (size_t i = 0; i < NumSecureDisableRegisters; i++) { + disables[i] |= ~NonOtherDisables[i]; + } + disables[0] &= 0x007FFFF0; + } + if constexpr ((Register & SecureRegister_DramParameters) != 0) { + SetSecureScratchReadWriteMasks< 8, 9, 10, 11, 12, 13, 14, 15, + 17, 18, 19, 20, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 52, 53, 54, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 79, 80, 81, 82, 83, 84, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 104, 105, 106, 107 + >(disables); + } + if constexpr ((Register & SecureRegister_ResetVector) != 0) { + SetSecureScratchReadWriteMasks<34, 35>(disables); + } + if constexpr ((Register & SecureRegister_Carveout) != 0) { + SetSecureScratchReadWriteMasks<16, 39, 51, 55, 74, 75, 76, 77, 78, 99, 100, 101, 102, 103>(disables); + } + if constexpr ((Register & SecureRegister_CmacWrite) != 0) { + SetSecureScratchWriteMasks<112, 113, 114, 115>(disables); + } + if constexpr ((Register & SecureRegister_CmacRead) != 0) { + SetSecureScratchReadMasks<112, 113, 114, 115>(disables); + } + if constexpr ((Register & SecureRegister_KeySourceWrite) != 0) { + SetSecureScratchWriteMasks<24, 25, 26, 27>(disables); + } + if constexpr ((Register & SecureRegister_KeySourceRead) != 0) { + SetSecureScratchReadMasks<24, 25, 26, 27>(disables); + } + if constexpr ((Register & SecureRegister_Srk) != 0) { + SetSecureScratchReadWriteMasks<4, 5, 6, 7>(disables); + } + + return disables; + } + + /* Validate that the secure scratch masks produced are correct. */ + #include "pmc_secure_scratch_test.inc" + + ALWAYS_INLINE void LockBits(uintptr_t address, u32 mask) { + reg::Write(address, reg::Read(address) | mask); + } + + template + ALWAYS_INLINE void SetSecureScratchMasks(uintptr_t address) { + constexpr auto Masks = GetSecureScratchMasks(); + + if constexpr (Masks[0] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE , Masks[0]); } + if constexpr (Masks[1] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE2, Masks[1]); } + if constexpr (Masks[2] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE3, Masks[2]); } + if constexpr (Masks[3] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE4, Masks[3]); } + if constexpr (Masks[4] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE5, Masks[4]); } + if constexpr (Masks[5] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE6, Masks[5]); } + if constexpr (Masks[6] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE7, Masks[6]); } + if constexpr (Masks[7] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE8, Masks[7]); } + + static_assert(Masks.size() == 8); + } + + template + ALWAYS_INLINE bool TestSecureScratchMasks(uintptr_t address) { + constexpr auto Masks = GetSecureScratchMasks(); + + if constexpr (Masks[0] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE ) & Masks[0]) != Masks[0]) { return false; } } + if constexpr (Masks[1] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE2) & Masks[1]) != Masks[1]) { return false; } } + if constexpr (Masks[2] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE3) & Masks[2]) != Masks[2]) { return false; } } + if constexpr (Masks[3] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE4) & Masks[3]) != Masks[3]) { return false; } } + if constexpr (Masks[4] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE5) & Masks[4]) != Masks[4]) { return false; } } + if constexpr (Masks[5] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE6) & Masks[5]) != Masks[5]) { return false; } } + if constexpr (Masks[6] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE7) & Masks[6]) != Masks[6]) { return false; } } + if constexpr (Masks[7] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE8) & Masks[7]) != Masks[7]) { return false; } } + static_assert(Masks.size() == 8); + + return true; + } + + NOINLINE void WriteRandomValueToRegister(uintptr_t offset) { + /* Create an aligned buffer. */ + util::AlignedBuffer buf; + + /* Generate random bytes into it. */ + se::GenerateRandomBytes(buf, sizeof(u32)); + + /* Read the random value. */ + const u32 random = *reinterpret_cast(static_cast(buf)); + + /* Get the address. */ + const uintptr_t address = g_register_address + offset; + + /* Write the value. */ + reg::Write(address, random); + + /* Verify it was written. */ + AMS_ABORT_UNLESS(reg::Read(address) == random); + } + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void InitializeRandomScratch() { + /* Write random data to the scratch that contains the SRK. */ + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH4); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH5); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH6); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH7); + + /* Lock the SRK scratch. */ + LockSecureRegister(SecureRegister_Srk); + + /* Write random data to the scratch used for tzram cmac. */ + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH112); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH113); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH114); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH115); + + /* Write random data to the scratch used for tzram key source. */ + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH24); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH25); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH26); + WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH27); + + /* Here, Nintendo locks the SRK scratch a second time. */ + /* This may just be "to be sure". */ + LockSecureRegister(SecureRegister_Srk); + } + + void LockSecureRegister(SecureRegister reg) { + /* Get the address. */ + const uintptr_t address = g_register_address; + + /* Apply each mask. */ + #define PMC_PROCESS_REG(REG) do { if ((reg & SecureRegister_##REG) != 0) { SetSecureScratchMasks(address); } } while (0) + PMC_PROCESS_REG(Other); + PMC_PROCESS_REG(DramParameters); + PMC_PROCESS_REG(ResetVector); + PMC_PROCESS_REG(Carveout); + PMC_PROCESS_REG(CmacWrite); + PMC_PROCESS_REG(CmacRead); + PMC_PROCESS_REG(KeySourceWrite); + PMC_PROCESS_REG(KeySourceRead); + PMC_PROCESS_REG(Srk); + #undef PMC_PROCESS_REG + + } + + LockState GetSecureRegisterLockState(SecureRegister reg) { + bool all_valid = true; + bool any_valid = false; + + /* Get the address. */ + const uintptr_t address = g_register_address; + + /* Test each mask. */ + #define PMC_PROCESS_REG(REG) do { if ((reg & SecureRegister_##REG) != 0) { const bool test = TestSecureScratchMasks(address); all_valid &= test; any_valid |= test; } } while (0) + PMC_PROCESS_REG(Other); + PMC_PROCESS_REG(DramParameters); + PMC_PROCESS_REG(ResetVector); + PMC_PROCESS_REG(Carveout); + PMC_PROCESS_REG(CmacWrite); + PMC_PROCESS_REG(CmacRead); + PMC_PROCESS_REG(KeySourceWrite); + PMC_PROCESS_REG(KeySourceRead); + PMC_PROCESS_REG(Srk); + #undef PMC_PROCESS_REG + + if (all_valid) { + return LockState::Locked; + } else if (any_valid) { + return LockState::PartiallyLocked; + } else { + return LockState::NotLocked; + } + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/pmc/pmc_secure_scratch_test.inc b/libraries/libexosphere/source/pmc/pmc_secure_scratch_test.inc new file mode 100644 index 000000000..b33268a24 --- /dev/null +++ b/libraries/libexosphere/source/pmc/pmc_secure_scratch_test.inc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace test { + + constexpr inline auto Other = GetSecureScratchMasks(); + static_assert(Other[0] == 0x00700FF0u); + static_assert(Other[1] == 0xFC000000u); + static_assert(Other[2] == 0x3F0FFF00u); + static_assert(Other[3] == 0x00000000u); + static_assert(Other[4] == 0x00000000u); + static_assert(Other[5] == 0x0C000000u); + static_assert(Other[6] == 0x00000000u); + static_assert(Other[7] == 0xFF00FF00u); + + constexpr inline auto DramParameters = GetSecureScratchMasks(); + static_assert(DramParameters[0] == 0x00000000u); + static_assert(DramParameters[1] == 0x03FCFFFFu); + static_assert(DramParameters[2] == 0x00000000u); + static_assert(DramParameters[3] == 0x3F3FFFFFu); + static_assert(DramParameters[4] == 0xFFFFFFFFu); + static_assert(DramParameters[5] == 0xF3FFC00Fu); + static_assert(DramParameters[6] == 0x003FFFFFu); + static_assert(DramParameters[7] == 0x000000FFu); + + constexpr inline auto ResetVector = GetSecureScratchMasks(); + static_assert(ResetVector[0] == 0x00000000u); + static_assert(ResetVector[1] == 0x00000000u); + static_assert(ResetVector[2] == 0x00F00000u); + static_assert(ResetVector[3] == 0x00000000u); + static_assert(ResetVector[4] == 0x00000000u); + static_assert(ResetVector[5] == 0x00000000u); + static_assert(ResetVector[6] == 0x00000000u); + static_assert(ResetVector[7] == 0x00000000u); + + constexpr inline auto CmacWrite = GetSecureScratchMasks(); + static_assert(CmacWrite[0] == 0x00000000u); + static_assert(CmacWrite[1] == 0x00000000u); + static_assert(CmacWrite[2] == 0x00000000u); + static_assert(CmacWrite[3] == 0x00000000u); + static_assert(CmacWrite[4] == 0x00000000u); + static_assert(CmacWrite[5] == 0x00000000u); + static_assert(CmacWrite[6] == 0x00000000u); + static_assert(CmacWrite[7] == 0x00550000u); + + constexpr inline auto CmacRead = GetSecureScratchMasks(); + static_assert(CmacRead[0] == 0x00000000u); + static_assert(CmacRead[1] == 0x00000000u); + static_assert(CmacRead[2] == 0x00000000u); + static_assert(CmacRead[3] == 0x00000000u); + static_assert(CmacRead[4] == 0x00000000u); + static_assert(CmacRead[5] == 0x00000000u); + static_assert(CmacRead[6] == 0x00000000u); + static_assert(CmacRead[7] == 0x00AA0000u); + + constexpr inline auto KeySourceWrite = GetSecureScratchMasks(); + static_assert(KeySourceWrite[0] == 0x00000000u); + static_assert(KeySourceWrite[1] == 0x00000000u); + static_assert(KeySourceWrite[2] == 0x00000055u); + static_assert(KeySourceWrite[3] == 0x00000000u); + static_assert(KeySourceWrite[4] == 0x00000000u); + static_assert(KeySourceWrite[5] == 0x00000000u); + static_assert(KeySourceWrite[6] == 0x00000000u); + static_assert(KeySourceWrite[7] == 0x00000000u); + + constexpr inline auto KeySourceRead = GetSecureScratchMasks(); + static_assert(KeySourceRead[0] == 0x00000000u); + static_assert(KeySourceRead[1] == 0x00000000u); + static_assert(KeySourceRead[2] == 0x000000AAu); + static_assert(KeySourceRead[3] == 0x00000000u); + static_assert(KeySourceRead[4] == 0x00000000u); + static_assert(KeySourceRead[5] == 0x00000000u); + static_assert(KeySourceRead[6] == 0x00000000u); + static_assert(KeySourceRead[7] == 0x00000000u); + + constexpr inline auto Srk = GetSecureScratchMasks(); + static_assert(Srk[0] == 0x000FF000u); + static_assert(Srk[1] == 0x00000000u); + static_assert(Srk[2] == 0x00000000u); + static_assert(Srk[3] == 0x00000000u); + static_assert(Srk[4] == 0x00000000u); + static_assert(Srk[5] == 0x00000000u); + static_assert(Srk[6] == 0x00000000u); + static_assert(Srk[7] == 0x00000000u); + + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/pmic/max77620.h b/libraries/libexosphere/source/pmic/max77620.h new file mode 100644 index 000000000..b1335544e --- /dev/null +++ b/libraries/libexosphere/source/pmic/max77620.h @@ -0,0 +1,340 @@ +/* + * Defining registers address and its bit definitions of MAX77620 and MAX20024 + * + * Copyright (c) 2016 NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef _MFD_MAX77620_H_ +#define _MFD_MAX77620_H_ + +#define MAX77620_I2C_ADDR 0x3C + +/* GLOBAL, PMIC, GPIO, FPS, ONOFFC, CID Registers */ +#define MAX77620_REG_CNFGGLBL1 0x00 +#define MAX77620_CNFGGLBL1_LBDAC_EN (1 << 7) +#define MAX77620_CNFGGLBL1_MPPLD (1 << 6) +#define MAX77620_CNFGGLBL1_LBHYST ((1 << 5) | (1 << 4)) +#define MAX77620_CNFGGLBL1_LBHYST_100 (0 << 4) +#define MAX77620_CNFGGLBL1_LBHYST_200 (1 << 4) +#define MAX77620_CNFGGLBL1_LBHYST_300 (2 << 4) +#define MAX77620_CNFGGLBL1_LBHYST_400 (3 << 4) +#define MAX77620_CNFGGLBL1_LBDAC_MASK 0x0E +#define MAX77620_CNFGGLBL1_LBDAC_2700 (0 << 1) +#define MAX77620_CNFGGLBL1_LBDAC_2800 (1 << 1) +#define MAX77620_CNFGGLBL1_LBDAC_2900 (2 << 1) +#define MAX77620_CNFGGLBL1_LBDAC_3000 (3 << 1) +#define MAX77620_CNFGGLBL1_LBDAC_3100 (4 << 1) +#define MAX77620_CNFGGLBL1_LBDAC_3200 (5 << 1) +#define MAX77620_CNFGGLBL1_LBDAC_3300 (6 << 1) +#define MAX77620_CNFGGLBL1_LBDAC_3400 (7 << 1) +#define MAX77620_CNFGGLBL1_LBRSTEN (1 << 0) + +#define MAX77620_REG_CNFGGLBL2 0x01 +#define MAX77620_REG_CNFGGLBL3 0x02 +#define MAX77620_WDTC_MASK 0x3 +#define MAX77620_WDTOFFC (1 << 4) +#define MAX77620_WDTSLPC (1 << 3) +#define MAX77620_WDTEN (1 << 2) +#define MAX77620_TWD_MASK 0x3 +#define MAX77620_TWD_2s 0x0 +#define MAX77620_TWD_16s 0x1 +#define MAX77620_TWD_64s 0x2 +#define MAX77620_TWD_128s 0x3 + +#define MAX77620_REG_CNFG1_32K 0x03 +#define MAX77620_CNFG1_32K_OUT0_EN (1 << 2) + +#define MAX77620_REG_CNFGBBC 0x04 +#define MAX77620_CNFGBBC_ENABLE (1 << 0) +#define MAX77620_CNFGBBC_CURRENT_MASK 0x06 +#define MAX77620_CNFGBBC_CURRENT_SHIFT 1 +#define MAX77620_CNFGBBC_VOLTAGE_MASK 0x18 +#define MAX77620_CNFGBBC_VOLTAGE_SHIFT 3 +#define MAX77620_CNFGBBC_LOW_CURRENT_DISABLE (1 << 5) +#define MAX77620_CNFGBBC_RESISTOR_MASK 0xC0 +#define MAX77620_CNFGBBC_RESISTOR_SHIFT 6 +#define MAX77620_CNFGBBC_RESISTOR_100 (0 << MAX77620_CNFGBBC_RESISTOR_SHIFT) +#define MAX77620_CNFGBBC_RESISTOR_1K (1 << MAX77620_CNFGBBC_RESISTOR_SHIFT) +#define MAX77620_CNFGBBC_RESISTOR_3K (2 << MAX77620_CNFGBBC_RESISTOR_SHIFT) +#define MAX77620_CNFGBBC_RESISTOR_6K (3 << MAX77620_CNFGBBC_RESISTOR_SHIFT) + +#define MAX77620_REG_IRQTOP 0x05 +#define MAX77620_IRQ_TOP_GLBL_MASK (1 << 7) +#define MAX77620_IRQ_TOP_SD_MASK (1 << 6) +#define MAX77620_IRQ_TOP_LDO_MASK (1 << 5) +#define MAX77620_IRQ_TOP_GPIO_MASK (1 << 4) +#define MAX77620_IRQ_TOP_RTC_MASK (1 << 3) +#define MAX77620_IRQ_TOP_32K_MASK (1 << 2) +#define MAX77620_IRQ_TOP_ONOFF_MASK (1 << 1) + +#define MAX77620_REG_INTLBT 0x06 +#define MAX77620_REG_IRQTOPM 0x0D +#define MAX77620_IRQ_LBM_MASK (1 << 3) +#define MAX77620_IRQ_TJALRM1_MASK (1 << 2) +#define MAX77620_IRQ_TJALRM2_MASK (1 << 1) + +#define MAX77620_REG_IRQSD 0x07 +#define MAX77620_REG_IRQ_LVL2_L0_7 0x08 +#define MAX77620_REG_IRQ_LVL2_L8 0x09 +#define MAX77620_REG_IRQ_LVL2_GPIO 0x0A +#define MAX77620_REG_ONOFFIRQ 0x0B +#define MAX77620_REG_NVERC 0x0C + +#define MAX77620_REG_INTENLBT 0x0E +#define MAX77620_GLBLM_MASK (1 << 0) + +#define MAX77620_REG_IRQMASKSD 0x0F +#define MAX77620_REG_IRQ_MSK_L0_7 0x10 +#define MAX77620_REG_IRQ_MSK_L8 0x11 +#define MAX77620_REG_ONOFFIRQM 0x12 +#define MAX77620_REG_STATLBT 0x13 +#define MAX77620_REG_STATSD 0x14 +#define MAX77620_REG_ONOFFSTAT 0x15 + +/* SD and LDO Registers */ +#define MAX77620_REG_SD0 0x16 +#define MAX77620_REG_SD1 0x17 +#define MAX77620_REG_SD2 0x18 +#define MAX77620_REG_SD3 0x19 +#define MAX77620_REG_SD4 0x1A +#define MAX77620_SDX_VOLT_MASK 0xFF +#define MAX77620_SD0_VOLT_MASK 0x3F +#define MAX77620_SD1_VOLT_MASK 0x7F +#define MAX77620_LDO_VOLT_MASK 0x3F +#define MAX77620_REG_DVSSD0 0x1B +#define MAX77620_REG_DVSSD1 0x1C +#define MAX77620_REG_SD0_CFG 0x1D +#define MAX77620_REG_SD1_CFG 0x1E +#define MAX77620_REG_SD2_CFG 0x1F +#define MAX77620_REG_SD3_CFG 0x20 +#define MAX77620_REG_SD4_CFG 0x21 +#define MAX77620_REG_SD_CFG2 0x22 +#define MAX77620_REG_LDO0_CFG 0x23 +#define MAX77620_REG_LDO0_CFG2 0x24 +#define MAX77620_REG_LDO1_CFG 0x25 +#define MAX77620_REG_LDO1_CFG2 0x26 +#define MAX77620_REG_LDO2_CFG 0x27 +#define MAX77620_REG_LDO2_CFG2 0x28 +#define MAX77620_REG_LDO3_CFG 0x29 +#define MAX77620_REG_LDO3_CFG2 0x2A +#define MAX77620_REG_LDO4_CFG 0x2B +#define MAX77620_REG_LDO4_CFG2 0x2C +#define MAX77620_REG_LDO5_CFG 0x2D +#define MAX77620_REG_LDO5_CFG2 0x2E +#define MAX77620_REG_LDO6_CFG 0x2F +#define MAX77620_REG_LDO6_CFG2 0x30 +#define MAX77620_REG_LDO7_CFG 0x31 +#define MAX77620_REG_LDO7_CFG2 0x32 +#define MAX77620_REG_LDO8_CFG 0x33 +#define MAX77620_REG_LDO8_CFG2 0x34 +#define MAX77620_LDO_POWER_MODE_MASK 0xC0 +#define MAX77620_LDO_POWER_MODE_SHIFT 6 +#define MAX77620_POWER_MODE_NORMAL 3 +#define MAX77620_POWER_MODE_LPM 2 +#define MAX77620_POWER_MODE_GLPM 1 +#define MAX77620_POWER_MODE_DISABLE 0 +#define MAX20024_LDO_CFG2_MPOK_MASK (1 << 2) +#define MAX77620_LDO_CFG2_ADE_MASK (1 << 1) +#define MAX77620_LDO_CFG2_ADE_DISABLE (0 << 1) +#define MAX77620_LDO_CFG2_ADE_ENABLE (1 << 1) +#define MAX77620_LDO_CFG2_SS_MASK (1 << 0) +#define MAX77620_LDO_CFG2_SS_FAST (1 << 0) +#define MAX77620_LDO_CFG2_SS_SLOW 0 + +#define MAX77620_REG_LDO_CFG3 0x35 +#define MAX77620_TRACK4_MASK (1 << 5) +#define MAX77620_TRACK4_SHIFT 5 + +#define MAX77620_LDO_SLEW_RATE_MASK 0x1 + +#define MAX77620_REG_GPIO0 0x36 +#define MAX77620_REG_GPIO1 0x37 +#define MAX77620_REG_GPIO2 0x38 +#define MAX77620_REG_GPIO3 0x39 +#define MAX77620_REG_GPIO4 0x3A +#define MAX77620_REG_GPIO5 0x3B +#define MAX77620_REG_GPIO6 0x3C +#define MAX77620_REG_GPIO7 0x3D +#define MAX77620_REG_PUE_GPIO 0x3E +#define MAX77620_REG_PDE_GPIO 0x3F +#define MAX77620_REG_AME_GPIO 0x40 +#define MAX77620_CNFG_GPIO_DRV_MASK (1 << 0) +#define MAX77620_CNFG_GPIO_DRV_PUSHPULL (1 << 0) +#define MAX77620_CNFG_GPIO_DRV_OPENDRAIN (0 << 0) +#define MAX77620_CNFG_GPIO_DIR_MASK (1 << 1) +#define MAX77620_CNFG_GPIO_DIR_INPUT (1 << 1) +#define MAX77620_CNFG_GPIO_DIR_OUTPUT (0 << 1) +#define MAX77620_CNFG_GPIO_INPUT_VAL_MASK (1 << 2) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK (1 << 3) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH (1 << 3) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW (0 << 3) +#define MAX77620_CNFG_GPIO_INT_MASK (0x3 << 4) +#define MAX77620_CNFG_GPIO_INT_FALLING (1 << 4) +#define MAX77620_CNFG_GPIO_INT_RISING (1 << 5) +#define MAX77620_CNFG_GPIO_DBNC_MASK (0x3 << 6) +#define MAX77620_CNFG_GPIO_DBNC_None (0x0 << 6) +#define MAX77620_CNFG_GPIO_DBNC_8ms (0x1 << 6) +#define MAX77620_CNFG_GPIO_DBNC_16ms (0x2 << 6) +#define MAX77620_CNFG_GPIO_DBNC_32ms (0x3 << 6) + +#define MAX77620_REG_ONOFFCNFG1 0x41 +#define MAX77620_ONOFFCNFG1_SFT_RST (1 << 7) +#define MAX77620_ONOFFCNFG1_MRT_MASK 0x38 +#define MAX77620_ONOFFCNFG1_MRT_SHIFT 0x3 +#define MAX77620_ONOFFCNFG1_SLPEN (1 << 2) +#define MAX77620_ONOFFCNFG1_PWR_OFF (1 << 1) +#define MAX20024_ONOFFCNFG1_CLRSE 0x18 + +#define MAX77620_REG_ONOFFCNFG2 0x42 +#define MAX77620_ONOFFCNFG2_SFT_RST_WK (1 << 7) +#define MAX77620_ONOFFCNFG2_WD_RST_WK (1 << 6) +#define MAX77620_ONOFFCNFG2_SLP_LPM_MSK (1 << 5) +#define MAX77620_ONOFFCNFG2_WK_ALARM1 (1 << 2) +#define MAX77620_ONOFFCNFG2_WK_EN0 (1 << 0) + +/* FPS Registers */ +#define MAX77620_REG_FPS_CFG0 0x43 +#define MAX77620_REG_FPS_CFG1 0x44 +#define MAX77620_REG_FPS_CFG2 0x45 +#define MAX77620_REG_FPS_LDO0 0x46 +#define MAX77620_REG_FPS_LDO1 0x47 +#define MAX77620_REG_FPS_LDO2 0x48 +#define MAX77620_REG_FPS_LDO3 0x49 +#define MAX77620_REG_FPS_LDO4 0x4A +#define MAX77620_REG_FPS_LDO5 0x4B +#define MAX77620_REG_FPS_LDO6 0x4C +#define MAX77620_REG_FPS_LDO7 0x4D +#define MAX77620_REG_FPS_LDO8 0x4E +#define MAX77620_REG_FPS_SD0 0x4F +#define MAX77620_REG_FPS_SD1 0x50 +#define MAX77620_REG_FPS_SD2 0x51 +#define MAX77620_REG_FPS_SD3 0x52 +#define MAX77620_REG_FPS_SD4 0x53 +#define MAX77620_REG_FPS_NONE 0 +#define MAX77620_FPS_SRC_MASK 0xC0 +#define MAX77620_FPS_SRC_SHIFT 6 +#define MAX77620_FPS_PU_PERIOD_MASK 0x38 +#define MAX77620_FPS_PU_PERIOD_SHIFT 3 +#define MAX77620_FPS_PD_PERIOD_MASK 0x07 +#define MAX77620_FPS_PD_PERIOD_SHIFT 0 + +/* Minimum and maximum FPS period time (in microseconds) are + * different for MAX77620 and Max20024. + */ +#define MAX77620_FPS_COUNT 3 + +#define MAX77620_FPS_PERIOD_MIN_US 40 +#define MAX20024_FPS_PERIOD_MIN_US 20 + +#define MAX77620_FPS_PERIOD_MAX_US 2560 +#define MAX20024_FPS_PERIOD_MAX_US 5120 + +#define MAX77620_REG_FPS_GPIO1 0x54 +#define MAX77620_REG_FPS_GPIO2 0x55 +#define MAX77620_REG_FPS_GPIO3 0x56 +#define MAX77620_FPS_TIME_PERIOD_MASK 0x38 +#define MAX77620_FPS_TIME_PERIOD_SHIFT 3 +#define MAX77620_FPS_EN_SRC_MASK 0x06 +#define MAX77620_FPS_EN_SRC_SHIFT 1 +#define MAX77620_FPS_ENFPS_SW_MASK 0x01 +#define MAX77620_FPS_ENFPS_SW 0x01 + +#define MAX77620_REG_FPS_RSO 0x57 +#define MAX77620_REG_CID0 0x58 +#define MAX77620_REG_CID1 0x59 +#define MAX77620_REG_CID2 0x5A +#define MAX77620_REG_CID3 0x5B +#define MAX77620_REG_CID4 0x5C +#define MAX77620_REG_CID5 0x5D + +#define MAX77620_REG_DVSSD4 0x5E +#define MAX20024_REG_MAX_ADD 0x70 + +#define MAX77620_CID_DIDM_MASK 0xF0 +#define MAX77620_CID_DIDM_SHIFT 4 + +/* CNCG2SD */ +#define MAX77620_SD_CNF2_ROVS_EN_SD1 (1 << 1) +#define MAX77620_SD_CNF2_ROVS_EN_SD0 (1 << 2) + +/* Device Identification Metal */ +#define MAX77620_CID5_DIDM(n) (((n) >> 4) & 0xF) +/* Device Indentification OTP */ +#define MAX77620_CID5_DIDO(n) ((n) & 0xF) + +/* SD CNFG1 */ +#define MAX77620_SD_SR_MASK 0xC0 +#define MAX77620_SD_SR_SHIFT 6 +#define MAX77620_SD_POWER_MODE_MASK 0x30 +#define MAX77620_SD_POWER_MODE_SHIFT 4 +#define MAX77620_SD_CFG1_ADE_MASK (1 << 3) +#define MAX77620_SD_CFG1_ADE_DISABLE 0 +#define MAX77620_SD_CFG1_ADE_ENABLE (1 << 3) +#define MAX77620_SD_FPWM_MASK 0x04 +#define MAX77620_SD_FPWM_SHIFT 2 +#define MAX77620_SD_FSRADE_MASK 0x01 +#define MAX77620_SD_FSRADE_SHIFT 0 +#define MAX77620_SD_CFG1_FPWM_SD_MASK (1 << 2) +#define MAX77620_SD_CFG1_FPWM_SD_SKIP 0 +#define MAX77620_SD_CFG1_FPWM_SD_FPWM (1 << 2) +#define MAX20024_SD_CFG1_MPOK_MASK (1 << 1) +#define MAX77620_SD_CFG1_FSRADE_SD_MASK (1 << 0) +#define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0 +#define MAX77620_SD_CFG1_FSRADE_SD_ENABLE (1 << 0) + +#define MAX77620_IRQ_LVL2_GPIO_EDGE0 (1 << 0) +#define MAX77620_IRQ_LVL2_GPIO_EDGE1 (1 << 1) +#define MAX77620_IRQ_LVL2_GPIO_EDGE2 (1 << 2) +#define MAX77620_IRQ_LVL2_GPIO_EDGE3 (1 << 3) +#define MAX77620_IRQ_LVL2_GPIO_EDGE4 (1 << 4) +#define MAX77620_IRQ_LVL2_GPIO_EDGE5 (1 << 5) +#define MAX77620_IRQ_LVL2_GPIO_EDGE6 (1 << 6) +#define MAX77620_IRQ_LVL2_GPIO_EDGE7 (1 << 7) + +/* Interrupts */ +enum { + MAX77620_IRQ_TOP_GLBL, /* Low-Battery */ + MAX77620_IRQ_TOP_SD, /* SD power fail */ + MAX77620_IRQ_TOP_LDO, /* LDO power fail */ + MAX77620_IRQ_TOP_GPIO, /* TOP GPIO internal int to MAX77620 */ + MAX77620_IRQ_TOP_RTC, /* RTC */ + MAX77620_IRQ_TOP_32K, /* 32kHz oscillator */ + MAX77620_IRQ_TOP_ONOFF, /* ON/OFF oscillator */ + MAX77620_IRQ_LBT_MBATLOW, /* Thermal alarm status, > 120C */ + MAX77620_IRQ_LBT_TJALRM1, /* Thermal alarm status, > 120C */ + MAX77620_IRQ_LBT_TJALRM2, /* Thermal alarm status, > 140C */ +}; + +/* GPIOs */ +enum { + MAX77620_GPIO0, + MAX77620_GPIO1, + MAX77620_GPIO2, + MAX77620_GPIO3, + MAX77620_GPIO4, + MAX77620_GPIO5, + MAX77620_GPIO6, + MAX77620_GPIO7, + MAX77620_GPIO_NR, +}; + +/* FPS Source */ +enum max77620_fps_src { + MAX77620_FPS_SRC_0, + MAX77620_FPS_SRC_1, + MAX77620_FPS_SRC_2, + MAX77620_FPS_SRC_NONE, + MAX77620_FPS_SRC_DEF, +}; + +enum max77620_chip_id { + MAX77620, + MAX20024, +}; + +#endif /* _MFD_MAX77620_H_ */ \ No newline at end of file diff --git a/libraries/libexosphere/source/pmic/max7762x.h b/libraries/libexosphere/source/pmic/max7762x.h new file mode 100644 index 000000000..1c8202515 --- /dev/null +++ b/libraries/libexosphere/source/pmic/max7762x.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _MAX7762X_H_ +#define _MAX7762X_H_ + +/* +* Switch Power domains (max77620): +* Name | Usage | uV step | uV min | uV default | uV max | Init +*-------+---------------+---------+--------+------------+---------+------------------ +* sd0 | core | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1) +* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1) +* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv) +* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 | +* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1) +* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 | +* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv) +* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | +* ldo5 | GC ASIC | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv) +* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V +* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | +* ldo8 | XUSB, DC | 50000 | 800000 | 1050000 | 1050000 | +*/ + +/* +* MAX77620_AME_GPIO: control GPIO modes (bits 0 - 7 correspond to GPIO0 - GPIO7); 0 -> GPIO, 1 -> alt-mode +* MAX77620_REG_GPIOx: 0x9 sets output and enable +*/ + +/*! MAX77620 partitions. */ +#define REGULATOR_SD0 0 +#define REGULATOR_SD1 1 +#define REGULATOR_SD2 2 +#define REGULATOR_SD3 3 +#define REGULATOR_LDO0 4 +#define REGULATOR_LDO1 5 +#define REGULATOR_LDO2 6 +#define REGULATOR_LDO3 7 +#define REGULATOR_LDO4 8 +#define REGULATOR_LDO5 9 +#define REGULATOR_LDO6 10 +#define REGULATOR_LDO7 11 +#define REGULATOR_LDO8 12 +#define REGULATOR_MAX 12 + +#define MAX77621_CPU_I2C_ADDR 0x1B +#define MAX77621_GPU_I2C_ADDR 0x1C + +#define MAX77621_VOUT_REG 0 +#define MAX77621_VOUT_DVC_REG 1 +#define MAX77621_CONTROL1_REG 2 +#define MAX77621_CONTROL2_REG 3 + +/* MAX77621_VOUT */ +#define MAX77621_VOUT_DISABLE (0 << 7) +#define MAX77621_VOUT_ENABLE (1 << 7) +#define MAX77621_VOUT_MASK 0x7F +#define MAX77621_VOUT_0_95V 0x37 +#define MAX77621_VOUT_1_09V 0x4F + +/* MAX77621_VOUT_DVC_DVS */ +#define MAX77621_DVS_VOUT_MASK 0x7F + +/* MAX77621_CONTROL1 */ +#define MAX77621_SNS_ENABLE (1 << 7) +#define MAX77621_FPWM_EN_M (1 << 6) +#define MAX77621_NFSR_ENABLE (1 << 5) +#define MAX77621_AD_ENABLE (1 << 4) +#define MAX77621_BIAS_ENABLE (1 << 3) +#define MAX77621_FREQSHIFT_9PER (1 << 2) + +#define MAX77621_RAMP_12mV_PER_US 0x0 +#define MAX77621_RAMP_25mV_PER_US 0x1 +#define MAX77621_RAMP_50mV_PER_US 0x2 +#define MAX77621_RAMP_200mV_PER_US 0x3 +#define MAX77621_RAMP_MASK 0x3 + +/* MAX77621_CONTROL2 */ +#define MAX77621_WDTMR_ENABLE (1 << 6) +#define MAX77621_DISCH_ENBABLE (1 << 5) +#define MAX77621_FT_ENABLE (1 << 4) +#define MAX77621_T_JUNCTION_120 (1 << 7) + +#define MAX77621_CKKADV_TRIP_DISABLE 0xC +#define MAX77621_CKKADV_TRIP_75mV_PER_US 0x0 +#define MAX77621_CKKADV_TRIP_150mV_PER_US 0x4 +#define MAX77621_CKKADV_TRIP_75mV_PER_US_HIST_DIS 0x8 + +#define MAX77621_INDUCTOR_MIN_30_PER 0x0 +#define MAX77621_INDUCTOR_NOMINAL 0x1 +#define MAX77621_INDUCTOR_PLUS_30_PER 0x2 +#define MAX77621_INDUCTOR_PLUS_60_PER 0x3 + +#endif diff --git a/libraries/libexosphere/source/pmic/pmic_api.cpp b/libraries/libexosphere/source/pmic/pmic_api.cpp new file mode 100644 index 000000000..e50723dc6 --- /dev/null +++ b/libraries/libexosphere/source/pmic/pmic_api.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "max77620.h" +#include "max7762x.h" + +namespace ams::pmic { + + namespace { + + constexpr inline int I2cAddressEristaMax77621 = 0x1B; + constexpr inline int I2cAddressMarikoMax77812_A = 0x31; + constexpr inline int I2cAddressMarikoMax77812_B = 0x33; + + + /* https://github.com/Atmosphere-NX/Atmosphere/blob/master/emummc/source/power/max7762x.h */ + /* TODO: Find datasheet, link to it instead. */ + /* NOTE: Tentatively, Max77620 "mostly" matches https://datasheets.maximintegrated.com/en/ds/MAX77863.pdf. */ + /* This does not contain Max77621 documentation, though. */ + constexpr inline int Max77620RegisterGpio0 = 0x36; + constexpr inline int Max77620RegisterAmeGpio = 0x40; + + constexpr inline int Max77621RegisterVOut = 0x00; + constexpr inline int Max77621RegisterVOutDvc = 0x01; + constexpr inline int Max77621RegisterControl1 = 0x02; + constexpr inline int Max77621RegisterControl2 = 0x03; + + + /* https://datasheets.maximintegrated.com/en/ds/MAX77812.pdf */ + constexpr inline int Max77812RegisterEnCtrl = 0x06; + constexpr inline int Max77812RegisterM4VOut = 0x26; + + void Max77620EnableGpio(int gpio) { + u8 val; + + /* Clear the AE for the GPIO */ + if (i2c::Query(std::addressof(val), sizeof(val), i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterAmeGpio)) { + val &= ~(1 << gpio); + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterAmeGpio, val); + } + + /* Set GPIO_DRV_PUSHPULL (bit 0), GPIO_OUTPUT_VAL_HIGH (bit 3). */ + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterGpio0 + gpio, MAX77620_CNFG_GPIO_DRV_PUSHPULL | MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH); + } + + void EnableVddCpuErista() { + /* Enable GPIO 5. */ + /* TODO: What does this control? */ + Max77620EnableGpio(5); + + /* Configure Max77621 control registers. */ + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterControl1, MAX77621_AD_ENABLE | MAX77621_NFSR_ENABLE | MAX77621_SNS_ENABLE | MAX77621_RAMP_12mV_PER_US); + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterControl2, MAX77621_T_JUNCTION_120 | MAX77621_WDTMR_ENABLE | MAX77621_CKKADV_TRIP_75mV_PER_US| MAX77621_INDUCTOR_NOMINAL); + + /* Configure Max77621 VOut to 0.95v */ + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOut, MAX77621_VOUT_ENABLE | MAX77621_VOUT_0_95V); + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOutDvc, MAX77621_VOUT_ENABLE | MAX77621_VOUT_0_95V); + } + + void DisableVddCpuErista() { + /* Disable Max77621 VOut. */ + i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOut, MAX77621_VOUT_DISABLE); + } + + int GetI2cAddressForMarikoMax77812(Regulator regulator) { + switch (regulator) { + case Regulator_Mariko_Max77812_A: return I2cAddressMarikoMax77812_A; + case Regulator_Mariko_Max77812_B: return I2cAddressMarikoMax77812_B; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void EnableVddCpuMariko(Regulator regulator) { + const int address = GetI2cAddressForMarikoMax77812(regulator); + + /* Set EN_M3_LPM to enable BUCK Master 3 low power mode. */ + u8 ctrl; + if (i2c::Query(std::addressof(ctrl), sizeof(ctrl), i2c::Port_5, address, Max77812RegisterEnCtrl)) { + ctrl |= 0x40; + i2c::SendByte(i2c::Port_5, address, Max77812RegisterEnCtrl, ctrl); + } + + /* Set BUCK Master 4 output voltage to 110. */ + i2c::SendByte(i2c::Port_5, address, Max77812RegisterM4VOut, 110); + } + + void DisableVddCpuMariko(Regulator regulator) { + const int address = GetI2cAddressForMarikoMax77812(regulator); + + /* Clear EN_M3_LPM to disable BUCK Master 3 low power mode. */ + u8 ctrl; + if (i2c::Query(std::addressof(ctrl), sizeof(ctrl), i2c::Port_5, address, Max77812RegisterEnCtrl)) { + ctrl &= ~0x40; + i2c::SendByte(i2c::Port_5, address, Max77812RegisterEnCtrl, ctrl); + } + } + + } + + void EnableVddCpu(Regulator regulator) { + switch (regulator) { + case Regulator_Erista_Max77621: + return EnableVddCpuErista(); + case Regulator_Mariko_Max77812_A: + case Regulator_Mariko_Max77812_B: + return EnableVddCpuMariko(regulator); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void DisableVddCpu(Regulator regulator) { + switch (regulator) { + case Regulator_Erista_Max77621: + return DisableVddCpuErista(); + case Regulator_Mariko_Max77812_A: + case Regulator_Mariko_Max77812_B: + return DisableVddCpuMariko(regulator); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + +} diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp new file mode 100644 index 000000000..f9aac61b8 --- /dev/null +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + constexpr inline int AesKeySizeMax = 256 / BITSIZEOF(u8); + + enum AesMode { + AesMode_Aes128 = ((SE_CONFIG_ENC_MODE_AESMODE_KEY128 << SE_CONFIG_ENC_MODE_OFFSET) | (SE_CONFIG_DEC_MODE_AESMODE_KEY128 << SE_CONFIG_DEC_MODE_OFFSET)) >> SE_CONFIG_DEC_MODE_OFFSET, + AesMode_Aes192 = ((SE_CONFIG_ENC_MODE_AESMODE_KEY192 << SE_CONFIG_ENC_MODE_OFFSET) | (SE_CONFIG_DEC_MODE_AESMODE_KEY192 << SE_CONFIG_DEC_MODE_OFFSET)) >> SE_CONFIG_DEC_MODE_OFFSET, + AesMode_Aes256 = ((SE_CONFIG_ENC_MODE_AESMODE_KEY256 << SE_CONFIG_ENC_MODE_OFFSET) | (SE_CONFIG_DEC_MODE_AESMODE_KEY256 << SE_CONFIG_DEC_MODE_OFFSET)) >> SE_CONFIG_DEC_MODE_OFFSET, + }; + + enum MemoryInterface { + MemoryInterface_Ahb = SE_CRYPTO_CONFIG_MEMIF_AHB, + MemoryInterface_Mc = SE_CRYPTO_CONFIG_MEMIF_MCCIF, + }; + + constexpr inline u32 AesConfigEcb = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + + void SetConfig(volatile SecurityEngineRegisters *SE, bool encrypt, SE_CONFIG_DST dst) { + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM (CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM (CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM_SEL(CONFIG_ENC_ALG, encrypt, AES_ENC, NOP), + SE_REG_BITS_ENUM_SEL(CONFIG_DEC_ALG, encrypt, NOP, AES_DEC), + SE_REG_BITS_VALUE (CONFIG_DST, dst)); + } + + void SetAesConfig(volatile SecurityEngineRegisters *SE, int slot, bool encrypt, u32 config) { + const u32 encoded = reg::Encode(SE_REG_BITS_ENUM (CRYPTO_CONFIG_MEMIF, AHB), + SE_REG_BITS_VALUE (CRYPTO_CONFIG_KEY_INDEX, slot), + SE_REG_BITS_ENUM_SEL(CRYPTO_CONFIG_CORE_SEL, encrypt, ENCRYPT, DECRYPT)); + + reg::Write(SE->SE_CRYPTO_CONFIG, (config | encoded)); + } + + void SetBlockCount(volatile SecurityEngineRegisters *SE, int count) { + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, count - 1); + } + + void UpdateAesMode(volatile SecurityEngineRegisters *SE, AesMode mode) { + reg::ReadWrite(SE->SE_CONFIG, REG_BITS_VALUE(16, 16, mode)); + } + + // void UpdateMemoryInterface(volatile SecurityEngineRegisters *SE, MemoryInterface memif) { + // reg::ReadWrite(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_VALUE(CRYPTO_CONFIG_MEMIF, memif)); + // } + + void SetEncryptedAesKey(int dst_slot, int kek_slot, const void *key, size_t key_size, AesMode mode) { + AMS_ABORT_UNLESS(key_size <= AesKeySizeMax); + AMS_ABORT_UNLESS(0 <= dst_slot && dst_slot < AesKeySlotCount); + AMS_ABORT_UNLESS(0 <= kek_slot && kek_slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure for single AES ECB decryption to key table. */ + SetConfig(SE, false, SE_CONFIG_DST_KEYTABLE); + SetAesConfig(SE, kek_slot, false, AesConfigEcb); + UpdateAesMode(SE, mode); + SetBlockCount(SE, 1); + + /* Select the destination keyslot. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_DST, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_DST_KEY_INDEX, dst_slot), SE_REG_BITS_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, KEYS_0_3)); + + /* Ensure that the se sees the keydata we want it to. */ + hw::FlushDataCache(key, key_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, key, key_size); + } + + void EncryptAes(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, AesMode mode) { + /* If nothing to decrypt, succeed. */ + if (src_size == 0) { return; } + + /* Validate input. */ + AMS_ABORT_UNLESS(dst_size == AesBlockSize); + AMS_ABORT_UNLESS(src_size == AesBlockSize); + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure for AES-ECB encryption to memory. */ + SetConfig(SE, true, SE_CONFIG_DST_MEMORY); + SetAesConfig(SE, slot, true, AesConfigEcb); + UpdateAesMode(SE, mode); + + /* Execute the operation. */ + ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size); + } + + } + + void ClearAesKeySlot(int slot) { + /* Validate the key slot. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + for (int i = 0; i < 16; ++i) { + /* Select the keyslot. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_WORD, i)); + + /* Write the data. */ + SE->SE_CRYPTO_KEYTABLE_DATA = 0; + } + } + + void LockAesKeySlot(int slot, u32 flags) { + /* Validate the key slot. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Set non per-key flags. */ + if ((flags & ~KeySlotLockFlags_PerKey) != 0) { + /* TODO: KeySlotLockFlags_DstKeyTableOnly is Mariko-only. How should we handle this? */ + /* TODO: Mariko bit support. */ + reg::ReadWrite(SE->SE_CRYPTO_KEYTABLE_ACCESS[slot], REG_BITS_VALUE(0, 7, ~flags)); + } + + /* Set per-key flag. */ + if ((flags & KeySlotLockFlags_PerKey) != 0) { + reg::ReadWrite(SE->SE_CRYPTO_SECURITY_PERKEY, REG_BITS_VALUE(slot, 1, 0)); + } + } + + void SetAesKey(int slot, const void *key, size_t key_size) { + /* Validate the key slot and key size. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + AMS_ABORT_UNLESS(key_size <= AesKeySizeMax); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Set each key word in order. */ + const u32 *key_u32 = static_cast(key); + const int num_words = key_size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + /* Select the keyslot. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, KEY), + SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i)); + + /* Set the key word. */ + SE->SE_CRYPTO_KEYTABLE_DATA = *(key_u32++); + } + } + + void SetEncryptedAesKey128(int dst_slot, int kek_slot, const void *key, size_t key_size) { + return SetEncryptedAesKey(dst_slot, kek_slot, key, key_size, AesMode_Aes128); + } + + void SetEncryptedAesKey256(int dst_slot, int kek_slot, const void *key, size_t key_size) { + return SetEncryptedAesKey(dst_slot, kek_slot, key, key_size, AesMode_Aes256); + } + + void EncryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) { + return EncryptAes(dst, dst_size, slot, src, src_size, AesMode_Aes128); + } + + void DecryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) { + /* If nothing to decrypt, succeed. */ + if (src_size == 0) { return; } + + /* Validate input. */ + AMS_ABORT_UNLESS(dst_size == AesBlockSize); + AMS_ABORT_UNLESS(src_size == AesBlockSize); + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure for AES-ECB decryption to memory. */ + SetConfig(SE, false, SE_CONFIG_DST_MEMORY); + SetAesConfig(SE, slot, false, AesConfigEcb); + + ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size); + } + +} diff --git a/libraries/libexosphere/source/se/se_execute.cpp b/libraries/libexosphere/source/se/se_execute.cpp new file mode 100644 index 000000000..dbe3cbcc7 --- /dev/null +++ b/libraries/libexosphere/source/se/se_execute.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + struct LinkedListEntry { + u32 zero; + u32 address; + u32 size; + }; + static_assert(util::is_pod::value); + + uintptr_t GetPhysicalAddress(const void *ptr) { + const uintptr_t virt_address = reinterpret_cast(ptr); + + #if defined(ATMOSPHERE_ARCH_ARM64) + u64 phys_address; + __asm__ __volatile__("at s1e3r, %[virt]; mrs %[phys], par_el1" : [phys]"=r"(phys_address) : [virt]"r"(virt_address) : "memory", "cc"); + return (phys_address & 0x0000FFFFFFFFF000ul) | (virt_address & 0x0000000000000FFFul); + #elif defined(ATMOSPHERE_ARCH_ARM) + return virt_address; + #else + #error "Unknown architecture for Tegra Security Engine physical address translation" + #endif + } + + constexpr void SetLinkedListEntry(LinkedListEntry *entry, const void *ptr, size_t size) { + /* Clear the zero field. */ + entry->zero = 0; + + /* Set the address. */ + if (ptr != nullptr) { + entry->address = GetPhysicalAddress(ptr); + entry->size = static_cast(size); + } else { + entry->address = 0; + entry->size = 0; + } + } + + void StartOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op) { + /* Write back the current values of the error and interrupt status. */ + reg::Write(SE->SE_ERR_STATUS, reg::Read(SE->SE_ERR_STATUS)); + reg::Write(SE->SE_INT_STATUS, reg::Read(SE->SE_INT_STATUS)); + + /* Write the operation. */ + reg::Write(SE->SE_OPERATION, SE_REG_BITS_VALUE(OPERATION_OP, op)); + } + + void WaitForOperationComplete(volatile SecurityEngineRegisters *SE) { + /* Spin until the operation is done. */ + while (reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_SE_OP_DONE, CLEAR))) { /* ... */ } + + /* Check for operation success. */ + ValidateAesOperationResult(SE); + } + + } + + void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Set the linked list entries. */ + LinkedListEntry src_entry; + LinkedListEntry dst_entry; + + SetLinkedListEntry(std::addressof(src_entry), src, src_size); + SetLinkedListEntry(std::addressof(dst_entry), dst, dst_size); + + /* Ensure the linked list entry data is seen correctly. */ + hw::FlushDataCache(std::addressof(src_entry), sizeof(src_entry)); + hw::FlushDataCache(std::addressof(dst_entry), sizeof(dst_entry)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure the linked list addresses. */ + reg::Write(SE->SE_IN_LL_ADDR, static_cast(GetPhysicalAddress(std::addressof(src_entry)))); + reg::Write(SE->SE_OUT_LL_ADDR, static_cast(GetPhysicalAddress(std::addressof(dst_entry)))); + + /* Start the operation. */ + StartOperation(SE, op); + + /* Wait for the operation to complete. */ + WaitForOperationComplete(SE); + } + + void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Validate sizes. */ + AMS_ABORT_UNLESS(dst_size <= AesBlockSize); + AMS_ABORT_UNLESS(src_size == AesBlockSize); + + /* Set the block count to 1. */ + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); + + /* Create an aligned buffer. */ + util::AlignedBuffer aligned; + std::memcpy(aligned, src, AesBlockSize); + hw::FlushDataCache(aligned, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, aligned, AesBlockSize, aligned, AesBlockSize); + + /* Ensure that the CPU will see the correct output. */ + hw::DataSynchronizationBarrierInnerShareable(); + hw::FlushDataCache(aligned, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Copy the output to the destination. */ + std::memcpy(dst, aligned, dst_size); + } + + void ValidateAesOperationResult(volatile SecurityEngineRegisters *SE) { + /* Ensure no error occurred. */ + AMS_ABORT_UNLESS(reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_ERR_STAT, CLEAR))); + + /* Ensure the security engine is idle. */ + AMS_ABORT_UNLESS(reg::HasValue(SE->SE_STATUS, SE_REG_BITS_ENUM(STATUS_STATE, IDLE))); + + /* Ensure there is no error status. */ + AMS_ABORT_UNLESS(reg::Read(SE->SE_ERR_STATUS) == 0); + } + +} diff --git a/libraries/libexosphere/source/se/se_execute.hpp b/libraries/libexosphere/source/se/se_execute.hpp new file mode 100644 index 000000000..76b0a34af --- /dev/null +++ b/libraries/libexosphere/source/se/se_execute.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "se_registers.hpp" + +namespace ams::se { + + volatile SecurityEngineRegisters *GetRegisters(); + + void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size); + void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size); + + void ValidateAesOperationResult(volatile SecurityEngineRegisters *SE); + +} diff --git a/libraries/libexosphere/source/se/se_management.cpp b/libraries/libexosphere/source/se/se_management.cpp new file mode 100644 index 000000000..de845c2f0 --- /dev/null +++ b/libraries/libexosphere/source/se/se_management.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceSecurityEngine.GetAddress(); + constinit DoneHandler g_done_handler = nullptr; + + } + + volatile SecurityEngineRegisters *GetRegisters() { + return reinterpret_cast(g_register_address); + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void Initialize() { + auto *SE = GetRegisters(); + AMS_ABORT_UNLESS(reg::HasValue(SE->SE_STATUS, SE_REG_BITS_ENUM(STATUS_STATE, IDLE))); + } + + void SetSecure(bool secure) { + auto *SE = GetRegisters(); + + /* Set the security software setting. */ + if (secure) { + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, SECURE)); + } else { + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, NONSECURE)); + } + + /* Read the status register to force an update. */ + reg::Read(SE->SE_SE_SECURITY); + } + + void SetTzramSecure() { + auto *SE = GetRegisters(); + + /* Set the TZRAM setting to secure. */ + SE->SE_TZRAM_SECURITY = SE_TZRAM_SETTING_SECURE; + } + + void SetPerKeySecure() { + auto *SE = GetRegisters(); + + /* Clear AES PerKey security. */ + SE->SE_CRYPTO_SECURITY_PERKEY = 0; + + /* Clear RSA PerKey security. */ + SE->SE_RSA_SECURITY_PERKEY = 0; + + /* Update PERKEY_SETTING to secure. */ + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_PERKEY_SETTING, SECURE)); + } + + void Lockout() { + auto *SE = GetRegisters(); + + /* Lock access to the AES keyslots. */ + for (int i = 0; i < AesKeySlotCount; ++i) { + SE->SE_CRYPTO_KEYTABLE_ACCESS[i] = 0; + } + + /* Lock access to the RSA keyslots. */ + for (int i = 0; i < RsaKeySlotCount; ++i) { + SE->SE_RSA_KEYTABLE_ACCESS[i] = 0; + } + + /* Set Per Key secure. */ + SetPerKeySecure(); + + /* Configure SE_SECURITY. */ + { + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_HARD_SETTING, SECURE), + SE_REG_BITS_ENUM(SECURITY_ENG_DIS, DISABLE), + SE_REG_BITS_ENUM(SECURITY_PERKEY_SETTING, SECURE), + SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, SECURE)); + } + } + + void HandleInterrupt() { + /* Get the registers. */ + auto *SE = GetRegisters(); + + /* Disable the SE interrupt. */ + reg::Write(SE->SE_INT_ENABLE, 0); + + /* Execute the handler if we have one. */ + if (const auto handler = g_done_handler; handler != nullptr) { + g_done_handler = nullptr; + handler(); + } + } + +} diff --git a/libraries/libexosphere/source/se/se_registers.hpp b/libraries/libexosphere/source/se/se_registers.hpp new file mode 100644 index 000000000..d76e0571c --- /dev/null +++ b/libraries/libexosphere/source/se/se_registers.hpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::se { + + struct SecurityEngineRegisters { + u32 SE_SE_SECURITY; + u32 SE_TZRAM_SECURITY; + u32 SE_OPERATION; + u32 SE_INT_ENABLE; + u32 SE_INT_STATUS; + u32 SE_CONFIG; + u32 SE_IN_LL_ADDR; + u32 SE_IN_CUR_BYTE_ADDR; + u32 SE_IN_CUR_LL_ID; + u32 SE_OUT_LL_ADDR; + u32 SE_OUT_CUR_BYTE_ADDR; + u32 SE_OUT_CUR_LL_ID; + u32 SE_HASH_RESULT[0x10]; + u32 SE_CTX_SAVE_CONFIG; + u32 _0x74[0x63]; + u32 SE_SHA_CONFIG; + u32 SE_SHA_MSG_LENGTH[0x4]; + u32 SE_SHA_MSG_LEFT[0x4]; + u32 _0x224[0x17]; + u32 SE_CRYPTO_SECURITY_PERKEY; + u32 SE_CRYPTO_KEYTABLE_ACCESS[0x10]; + u32 _0x2C4[0x10]; + u32 SE_CRYPTO_CONFIG; + u32 SE_CRYPTO_LINEAR_CTR[0x4]; + u32 SE_CRYPTO_LAST_BLOCK; + u32 SE_CRYPTO_KEYTABLE_ADDR; + u32 SE_CRYPTO_KEYTABLE_DATA; + u32 _0x324[0x3]; + u32 SE_CRYPTO_KEYTABLE_DST; + u32 _0x334[0x3]; + u32 SE_RNG_CONFIG; + u32 SE_RNG_SRC_CONFIG; + u32 SE_RNG_RESEED_INTERVAL; + u32 _0x34C[0x2D]; + u32 SE_RSA_CONFIG; + u32 SE_RSA_KEY_SIZE; + u32 SE_RSA_EXP_SIZE; + u32 SE_RSA_SECURITY_PERKEY; + u32 SE_RSA_KEYTABLE_ACCESS[0x2]; + u32 _0x418[0x2]; + u32 SE_RSA_KEYTABLE_ADDR; + u32 SE_RSA_KEYTABLE_DATA; + u32 SE_RSA_OUTPUT[0x40]; + u32 _0x528[0xB6]; + u32 SE_STATUS; + u32 SE_ERR_STATUS; + u32 SE_MISC; + u32 SE_SPARE; + u32 SE_ENTROPY_DEBUG_COUNTER; + u32 _0x814; + u32 _0x818; + u32 _0x81C; + u32 _0x820[0x5F8]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(SecurityEngineRegisters) == secmon::MemoryRegionPhysicalDeviceSecurityEngine.GetSize()); + + static_assert(AesKeySlotCount == util::size(SecurityEngineRegisters{}.SE_CRYPTO_KEYTABLE_ACCESS)); + static_assert(RsaKeySlotCount == util::size(SecurityEngineRegisters{}.SE_RSA_KEYTABLE_ACCESS)); + + #define SE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SE, NAME) + #define SE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SE, NAME, VALUE) + #define SE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SE, NAME, ENUM) + #define SE_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SE, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_SE_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SE, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_SE_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SE, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_SE_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_SE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_SE_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + #define DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(NAME, __OFFSET__) \ + REG_DEFINE_NAMED_REG(SE, NAME, __OFFSET__, 1); \ + \ + enum SE_##NAME { \ + SE_##NAME##_##CLEAR = 0, \ + SE_##NAME##_##ACTIVE = 1, \ + SE_##NAME##_##SW_CLEAR = 1, \ + }; + + /* SE_STATUS. */ + DEFINE_SE_REG_TWO_BIT_ENUM(STATUS_STATE, 0, IDLE, BUSY, WAIT_OUT, WAIT_IN); + + /* SE_SECURITY */ + DEFINE_SE_REG_BIT_ENUM(SECURITY_HARD_SETTING, 0, SECURE, NONSECURE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_ENG_DIS, 1, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_PERKEY_SETTING, 2, SECURE, NONSECURE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_SOFT_SETTING, 16, SECURE, NONSECURE); + + /* SE_TZRAM_SECURITY */ + DEFINE_SE_REG(TZRAM_SETTING, 0, BITSIZEOF(u32)); + constexpr inline u32 SE_TZRAM_SETTING_SECURE = 0; + + /* SE_OPERATION */ + DEFINE_SE_REG_THREE_BIT_ENUM(OPERATION_OP, 0, ABORT, START, RESTART_OUT, CTX_SAVE, RESTART_IN, RESERVED_5, RESERVED_6, RESERVED_7); + + /* SE_INT_ENABLE */ + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_IN_LL_BUF_RD, 0, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_IN_DONE, 1, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_OUT_LL_BUF_WR, 2, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_OUT_DONE, 3, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_SE_OP_DONE, 4, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_RESEED_CNTR_EXHAUSTED, 5, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_ERR_STAT, 16, DISABLE, ENABLE); + + /* SE_INT_STATUS */ + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_IN_LL_BUF_RD, 0); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_IN_DONE, 1); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_OUT_LL_BUF_WR, 2); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_OUT_DONE, 3); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_SE_OP_DONE, 4); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_RESEED_CNTR_EXHAUSTED, 5); + DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_ERR_STAT, 16); + + /* SE_CONFIG */ + DEFINE_SE_REG(CONFIG_DST, 2, 3); + DEFINE_SE_REG(CONFIG_DEC_ALG, 8, 4); + DEFINE_SE_REG(CONFIG_ENC_ALG, 12, 4); + DEFINE_SE_REG(CONFIG_DEC_MODE, 16, 8); + DEFINE_SE_REG(CONFIG_ENC_MODE, 24, 8); + + enum SE_CONFIG_DST { + SE_CONFIG_DST_MEMORY = 0, + SE_CONFIG_DST_HASH_REG = 1, + SE_CONFIG_DST_KEYTABLE = 2, + SE_CONFIG_DST_SRK = 3, + SE_CONFIG_DST_RSA_REG = 4, + }; + + enum SE_CONFIG_DEC_ALG { + SE_CONFIG_DEC_ALG_NOP = 0, + SE_CONFIG_DEC_ALG_AES_DEC = 1, + }; + + enum SE_CONFIG_ENC_ALG { + SE_CONFIG_ENC_ALG_NOP = 0, + SE_CONFIG_ENC_ALG_AES_ENC = 1, + SE_CONFIG_ENC_ALG_RNG = 2, + SE_CONFIG_ENC_ALG_SHA = 3, + SE_CONFIG_ENC_ALG_RSA = 4, + }; + + enum SE_CONFIG_DEC_MODE { + SE_CONFIG_DEC_MODE_AESMODE_KEY128 = 0, + SE_CONFIG_DEC_MODE_AESMODE_KEY192 = 1, + SE_CONFIG_DEC_MODE_AESMODE_KEY256 = 2, + }; + + enum SE_CONFIG_ENC_MODE { + SE_CONFIG_ENC_MODE_AESMODE_KEY128 = 0, + SE_CONFIG_ENC_MODE_AESMODE_KEY192 = 1, + SE_CONFIG_ENC_MODE_AESMODE_KEY256 = 2, + + SE_CONFIG_ENC_MODE_AESMODE_SHA1 = 1, + SE_CONFIG_ENC_MODE_AESMODE_SHA224 = 4, + SE_CONFIG_ENC_MODE_AESMODE_SHA256 = 5, + SE_CONFIG_ENC_MODE_AESMODE_SHA384 = 6, + SE_CONFIG_ENC_MODE_AESMODE_SHA512 = 7, + }; + + + /* SE_CRYPTO_KEYTABLE_ADDR */ + DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_WORD, 0, 4); + DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_IV_WORD, 0, 2); + DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, 0, 3); + + enum SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD { + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_0 = 0u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_1 = 1u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_2 = 2u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_3 = 3u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_4 = 4u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_5 = 5u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_6 = 6u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_7 = 7u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_0 = 8u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_1 = 9u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_2 = 10u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_3 = 11u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_0 = 12u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_1 = 13u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_2 = 14u, + SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_3 = 15u, + }; + + DEFINE_SE_REG_BIT_ENUM(CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, 2, ORIGINAL_IV, UPDATED_IV); + DEFINE_SE_REG_BIT_ENUM(CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, 3, KEY, IV); + + DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, 4, 4); + + /* SE_RSA_KEYTABLE_ADDR */ + DEFINE_SE_REG(RSA_KEYTABLE_ADDR_WORD_ADDR, 0, 6); + DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ADDR_EXPMOD_SEL, 6, EXPONENT, MODULUS); + DEFINE_SE_REG(RSA_KEYTABLE_ADDR_KEY_SLOT, 7, 1); + DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ADDR_INPUT_MODE, 8, REGISTER, MEMORY); + + /* SE_RSA_KEYTABLE_ACCESS */ + DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ACCESS_KEYREAD, 0, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ACCESS_KEYUPDATE, 1, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ACCESS_KEYUSE, 2, DISABLE, ENABLE); + + /* SE_CRYPTO_CONFIG */ + DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_HASH_ENB, 0, DISABLE, ENABLE); + DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_CONFIG_XOR_POS, 1, BYPASS, RESERVED, TOP, BOTTOM); + DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_CONFIG_INPUT_SEL, 3, MEMORY, RANDOM, INIT_AESOUT, LINEAR_CTR); + DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_CONFIG_VCTRAM_SEL, 5, MEMORY, RESERVED, INIT_AESOUT, INIT_PREV_MEMORY); + DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_IV_SELECT, 7, ORIGINAL, UPDATED); + DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_CORE_SEL, 8, DECRYPT, ENCRYPT); + DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_KEYSCH_BYPASS, 10, DISABLE, ENABLE); + DEFINE_SE_REG(CRYPTO_CONFIG_CTR_CNTN, 11, 8); + DEFINE_SE_REG(CRYPTO_CONFIG_KEY_INDEX, 24, 4); + DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_MEMIF, 31, AHB, MCCIF); + + /* SE_CRYPTO_KEYTABLE_DST */ + DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, 0, KEYS_0_3, KEYS_4_7, ORIGINAL_IV, UPDATED_IV); + DEFINE_SE_REG(CRYPTO_KEYTABLE_DST_KEY_INDEX, 8, 4); + + /* SE_RNG_CONFIG */ + DEFINE_SE_REG_TWO_BIT_ENUM(RNG_CONFIG_MODE, 0, NORMAL, FORCE_INSTANTIATION, FORCE_RESEED, RESERVED3); + DEFINE_SE_REG_TWO_BIT_ENUM(RNG_CONFIG_SRC, 2, NONE, ENTROPY, LFSR, RESERVED3); + + /* SE_RNG_SRC_CONFIG */ + DEFINE_SE_REG_BIT_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE_LOCK, 0, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE, 1, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(RNG_SRC_CONFIG_HW_DISABLE_CYA, 2, DISABLE, ENABLE); + DEFINE_SE_REG(RNG_SRC_CONFIG_RO_ENTROPY_SUBSAMPLE, 4, 3); + DEFINE_SE_REG(RNG_SRC_CONFIG_RO_ENTROPY_DATA_FLUSH, 8, 1); + +} diff --git a/libraries/libexosphere/source/se/se_rng.cpp b/libraries/libexosphere/source/se/se_rng.cpp new file mode 100644 index 000000000..4cb099bde --- /dev/null +++ b/libraries/libexosphere/source/se/se_rng.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + constexpr inline int RngReseedInterval = 70001; + + void ConfigRng(volatile SecurityEngineRegisters *SE, SE_CONFIG_DST dst, SE_RNG_CONFIG_MODE mode) { + /* Configure the engine to do RNG encryption. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM (CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM (CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM (CONFIG_ENC_ALG, RNG), + SE_REG_BITS_ENUM (CONFIG_DEC_ALG, NOP), + SE_REG_BITS_VALUE(CONFIG_DST, dst)); + + reg::Write(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_ENUM (CRYPTO_CONFIG_MEMIF, AHB), + SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_CORE_SEL, ENCRYPT), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, RANDOM), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + + /* Configure the RNG to use Entropy as source. */ + reg::Write(SE->SE_RNG_CONFIG, SE_REG_BITS_ENUM(RNG_CONFIG_SRC, ENTROPY), SE_REG_BITS_VALUE(RNG_CONFIG_MODE, mode)); + } + + } + + void InitializeRandom() { + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Lock the entropy source. */ + reg::Write(SE->SE_RNG_SRC_CONFIG, SE_REG_BITS_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE, ENABLE), + SE_REG_BITS_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE_LOCK, ENABLE)); + + /* Set the reseed interval to force a reseed every 70000 blocks. */ + SE->SE_RNG_RESEED_INTERVAL = RngReseedInterval; + + /* Initialize the DRBG. */ + { + u8 dummy_buf[AesBlockSize]; + + /* Configure the engine to force drbg instantiation by writing random to memory. */ + ConfigRng(SE, SE_CONFIG_DST_MEMORY, SE_RNG_CONFIG_MODE_FORCE_INSTANTIATION); + + /* Configure to do a single RNG block operation to trigger DRBG init. */ + SE->SE_CRYPTO_LAST_BLOCK = 0; + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, dummy_buf, sizeof(dummy_buf), nullptr, 0); + } + } + + void GenerateRandomBytes(void *dst, size_t size) { + /* If we're not generating any bytes, there's nothing to do. */ + if (size == 0) { + return; + } + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Determine how many blocks to generate. */ + const size_t num_blocks = size / AesBlockSize; + const size_t aligned_size = num_blocks * AesBlockSize; + const size_t fractional = size - aligned_size; + + /* Configure the RNG to generate random to memory. */ + ConfigRng(SE, SE_CONFIG_DST_MEMORY, SE_RNG_CONFIG_MODE_NORMAL); + + /* Generate as many aligned blocks as we can. */ + if (aligned_size > 0) { + /* Configure the engine to generate the right number of blocks. */ + SE->SE_CRYPTO_LAST_BLOCK = num_blocks - 1; + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, dst, aligned_size, nullptr, 0); + } + + /* Generate a single block to output. */ + if (fractional > 0) { + ExecuteOperationSingleBlock(SE, static_cast(dst) + aligned_size, fractional, nullptr, 0); + } + } + + void SetRandomKey(int slot) { + /* NOTE: Nintendo does not validate the destination keyslot here. */ + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure the RNG to output to the keytable. */ + ConfigRng(SE, SE_CONFIG_DST_KEYTABLE, SE_RNG_CONFIG_MODE_NORMAL); + + /* Configure the keytable destination to be the low part of the key. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_DST, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_DST_KEY_INDEX, slot), SE_REG_BITS_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, KEYS_0_3)); + + /* Configure a single block operation. */ + SE->SE_CRYPTO_LAST_BLOCK = 0; + + /* Execute the operation to generate a random low-part of the key. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0); + + /* Configure the keytable destination to be the high part of the key. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_DST, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_DST_KEY_INDEX, slot), SE_REG_BITS_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, KEYS_4_7)); + + /* Execute the operation to generate a random high-part of the key. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0); + } + + void GenerateSrk() { + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure the RNG to output to SRK and force a reseed. */ + ConfigRng(SE, SE_CONFIG_DST_SRK, SE_RNG_CONFIG_MODE_FORCE_RESEED); + + /* Configure a single block operation. */ + SE->SE_CRYPTO_LAST_BLOCK = 0; + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0); + } + +} diff --git a/libraries/libexosphere/source/se/se_rsa.cpp b/libraries/libexosphere/source/se/se_rsa.cpp new file mode 100644 index 000000000..d39a040dc --- /dev/null +++ b/libraries/libexosphere/source/se/se_rsa.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + struct RsaKeyInfo { + int modulus_size_val; + int exponent_size_val; + }; + + constinit RsaKeyInfo g_rsa_key_infos[RsaKeySlotCount] = {}; + + void ClearRsaKeySlot(int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod) { + /* Get the engine. */ + auto *SE = GetRegisters(); + + constexpr int NumWords = se::RsaSize / sizeof(u32); + for (int i = 0; i < NumWords; ++i) { + /* Select the keyslot word. */ + reg::Write(SE->SE_RSA_KEYTABLE_ADDR, SE_REG_BITS_ENUM (RSA_KEYTABLE_ADDR_INPUT_MODE, REGISTER), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_KEY_SLOT, slot), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_EXPMOD_SEL, expmod), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_WORD_ADDR, i)); + + /* Clear the keyslot word. */ + SE->SE_RSA_KEYTABLE_DATA = 0; + } + } + + void SetRsaKey(int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod, const void *key, size_t key_size) { + /* Get the engine. */ + auto *SE = GetRegisters(); + + const int num_words = key_size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + /* Select the keyslot word. */ + reg::Write(SE->SE_RSA_KEYTABLE_ADDR, SE_REG_BITS_ENUM (RSA_KEYTABLE_ADDR_INPUT_MODE, REGISTER), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_KEY_SLOT, slot), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_EXPMOD_SEL, expmod), + SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_WORD_ADDR, i)); + + /* Get the word. */ + const u32 word = util::LoadBigEndian(static_cast(key) + (num_words - 1 - i)); + + /* Write the keyslot word. */ + SE->SE_RSA_KEYTABLE_DATA = word; + } + } + + } + + void ClearRsaKeySlot(int slot) { + /* Validate the key slot. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + + /* Clear the info. */ + g_rsa_key_infos[slot] = {}; + + /* Clear the modulus. */ + ClearRsaKeySlot(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS); + + /* Clear the exponent. */ + ClearRsaKeySlot(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT); + } + + void LockRsaKeySlot(int slot, u32 flags) { + /* Validate the key slot. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Set non per-key flags. */ + if ((flags & ~KeySlotLockFlags_PerKey) != 0) { + /* Pack the flags into the expected format. */ + u32 value = 0; + value |= ((flags & KeySlotLockFlags_KeyRead) == 0) ? (1u << 0) : 0; + value |= ((flags & KeySlotLockFlags_KeyRead) == 0) ? (1u << 1) : 0; + value |= ((flags & KeySlotLockFlags_KeyRead) == 0) ? (1u << 2) : 0; + + reg::Write(SE->SE_RSA_KEYTABLE_ACCESS[slot], SE_REG_BITS_ENUM_SEL(RSA_KEYTABLE_ACCESS_KEYREAD, (flags & KeySlotLockFlags_KeyRead) != 0, DISABLE, ENABLE), + SE_REG_BITS_ENUM_SEL(RSA_KEYTABLE_ACCESS_KEYUPDATE, (flags & KeySlotLockFlags_KeyWrite) != 0, DISABLE, ENABLE), + SE_REG_BITS_ENUM_SEL(RSA_KEYTABLE_ACCESS_KEYUSE, (flags & KeySlotLockFlags_KeyUse) != 0, DISABLE, ENABLE)); + } + + /* Set per-key flag. */ + if ((flags & KeySlotLockFlags_PerKey) != 0) { + reg::ReadWrite(SE->SE_RSA_SECURITY_PERKEY, REG_BITS_VALUE(slot, 1, 0)); + } + } + + void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size) { + /* Validate the key slot and sizes. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + AMS_ABORT_UNLESS(mod_size <= RsaSize); + AMS_ABORT_UNLESS(exp_size <= RsaSize); + + /* Set the sizes in the info. */ + auto &info = g_rsa_key_infos[slot]; + info.modulus_size_val = (mod_size / 64) - 1; + info.exponent_size_val = (exp_size / 4); + + /* Set the modulus and exponent. */ + SetRsaKey(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS, mod, mod_size); + SetRsaKey(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT, exp, exp_size); + } + +} diff --git a/libraries/libexosphere/source/se/se_suspend.cpp b/libraries/libexosphere/source/se/se_suspend.cpp new file mode 100644 index 000000000..03473deb4 --- /dev/null +++ b/libraries/libexosphere/source/se/se_suspend.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "se_execute.hpp" + +namespace ams::se { + + namespace { + + bool TestRegister(volatile u32 &r, u16 v) { + return (static_cast(reg::Read(r))) == v; + } + + } + + bool ValidateStickyBits(const StickyBits &bits) { + /* Get the registers. */ + auto *SE = GetRegisters(); + + /* Check SE_SECURITY. */ + if (!TestRegister(SE->SE_SE_SECURITY, bits.se_security)) { return false; } + + /* Check TZRAM_SECURITY. */ + if (!TestRegister(SE->SE_TZRAM_SECURITY, bits.tzram_security)) { return false; } + + /* Check CRYPTO_SECURITY_PERKEY. */ + if (!TestRegister(SE->SE_CRYPTO_SECURITY_PERKEY, bits.crypto_security_perkey)) { return false; } + + /* Check CRYPTO_KEYTABLE_ACCESS. */ + for (int i = 0; i < AesKeySlotCount; ++i) { + if (!TestRegister(SE->SE_CRYPTO_KEYTABLE_ACCESS[i], bits.crypto_keytable_access[i])) { return false; } + } + + /* Test RSA_SCEURITY_PERKEY */ + if (!TestRegister(SE->SE_CRYPTO_SECURITY_PERKEY, bits.rsa_security_perkey)) { return false; } + + /* Check RSA_KEYTABLE_ACCESS. */ + for (int i = 0; i < RsaKeySlotCount; ++i) { + if (!TestRegister(SE->SE_RSA_KEYTABLE_ACCESS[i], bits.rsa_keytable_access[i])) { return false; } + } + + /* All sticky bits are valid. */ + return true; + } + +} diff --git a/libraries/libexosphere/source/tsec/tsec_api.cpp b/libraries/libexosphere/source/tsec/tsec_api.cpp new file mode 100644 index 000000000..b8994ebc4 --- /dev/null +++ b/libraries/libexosphere/source/tsec/tsec_api.cpp @@ -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 . + */ +#include + +namespace ams::tsec { + + namespace { + + + + } + + void Lock() { + /* Set the tsec host1x syncpoint (160) to be secure. */ + /* TODO: constexpr value. */ + reg::ReadWrite(0x500038F8, REG_BITS_VALUE(0, 1, 0)); + + /* Clear the tsec host1x syncpoint. */ + reg::Write(0x50003300, 0); + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/uart/uart_api.cpp b/libraries/libexosphere/source/uart/uart_api.cpp new file mode 100644 index 000000000..533748106 --- /dev/null +++ b/libraries/libexosphere/source/uart/uart_api.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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 "uart_registers.hpp" + +namespace ams::uart { + + namespace { + + constexpr inline const u16 UartRegisterOffsets[Port_Count] = { + secmon::MemoryRegionPhysicalDeviceUartA.GetAddress() - secmon::MemoryRegionPhysicalDeviceUart.GetAddress(), + secmon::MemoryRegionPhysicalDeviceUartB.GetAddress() - secmon::MemoryRegionPhysicalDeviceUart.GetAddress(), + secmon::MemoryRegionPhysicalDeviceUartC.GetAddress() - secmon::MemoryRegionPhysicalDeviceUart.GetAddress(), + }; + + constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceUart.GetAddress(); + + volatile UartRegisters *GetRegisters(Port port) { + return reinterpret_cast(g_register_address + UartRegisterOffsets[port]); + } + + void WaitSymbols(int baud, u32 num) { + util::WaitMicroSeconds(util::DivideUp(1'000'000, baud) * num); + } + + void WaitCycles(int baud, u32 num) { + util::WaitMicroSeconds(util::DivideUp(1'000'000, 16 * baud) * num); + } + + ALWAYS_INLINE void WaitFifoNotFull(volatile UartRegisters *uart) { + while ((uart->lsr & UART_LSR_TX_FIFO_FULL) != 0) { /* ... */ } + } + + ALWAYS_INLINE void WaitFifoNotEmpty(volatile UartRegisters *uart) { + while ((uart->lsr & UART_LSR_RX_FIFO_EMPTY) != 0) { /* ... */ } + } + + void WaitIdle(volatile UartRegisters *uart, u32 vendor_state) { + if (vendor_state & UART_VENDOR_STATE_TX_IDLE) { + while ((uart->lsr & UART_LSR_TMTY) == 0) { /* ... */ } + } + + if (vendor_state & UART_VENDOR_STATE_RX_IDLE) { + while ((uart->lsr & UART_LSR_RDR) != 0) { /* ... */ } + } + } + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + void Initialize(Port port, int baud_rate, u32 flags) { + /* Get the registers. */ + auto *uart = GetRegisters(port); + + /* Parse flags. */ + const bool inverted = (flags & Flag_Inverted) != 0; + + /* Calculate the baud rate divisor. */ + constexpr u32 UartClock = 408000000; + const u32 divisor = (UartClock + (baud_rate * 16) / 2) / (baud_rate * 16); + + /* Disable DLAB and all interrupts. */ + uart->lcr = uart->lcr & ~UART_LCR_DLAB; + uart->ier = 0; + uart->mcr = 0; + + /* Setup the uart in FIFO mode. */ + uart->lcr = UART_LCR_DLAB | UART_LCR_WD_LENGTH_8; + uart->dll = static_cast(divisor); + uart->dlh = static_cast(divisor >> 8); + uart->lcr = uart->lcr & ~UART_LCR_DLAB; + reg::Read(std::addressof(uart->spr)); + + /* Wait three symbols. */ + WaitSymbols(baud_rate, 3); + + /* Enable FIFO with default settings. */ + uart->fcr = UART_FCR_FCR_EN_FIFO; + uart->irda_csr = inverted ? UART_IRDA_CSR_INVERT_TXD : 0; + reg::Read(std::addressof(uart->spr)); + + /* Wait three cycles. */ + WaitCycles(baud_rate, 3); + + /* Flush the FIFO. */ + WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE); + uart->fcr = uart->fcr | UART_FCR_RX_CLR | UART_FCR_TX_CLR; + WaitCycles(baud_rate, 32); + + /* Wait for idle state. */ + WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE | UART_VENDOR_STATE_RX_IDLE); + + /* Set scratch register to 0. */ + uart->spr = 0; + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/uart/uart_registers.hpp b/libraries/libexosphere/source/uart/uart_registers.hpp new file mode 100644 index 000000000..509de989f --- /dev/null +++ b/libraries/libexosphere/source/uart/uart_registers.hpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::uart { + + struct UartRegisters { + union { + u32 thr; + u32 rbr; + u32 dll; + }; + union { + u32 ier; + u32 dlh; + }; + union { + u32 iir; + u32 fcr; + }; + u32 lcr; + u32 mcr; + u32 lsr; + u32 msr; + u32 spr; + u32 irda_csr; + u32 rx_fifo_cfg; + u32 mie; + u32 vendor_status; + u32 reserved_30; + u32 reserved_34; + u32 reserved_38; + u32 asr; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(UartRegisters) == 0x40); + + /* 36.3.12 UART_VENDOR_STATUS_0_0 */ + enum UartVendorStatus { + UART_VENDOR_STATE_TX_IDLE = 1 << 0, + UART_VENDOR_STATE_RX_IDLE = 1 << 1, + + /* This bit is set to 1 when a read is issued to an empty FIFO and gets cleared on register read (sticky bit until read) + 0 = NO_UNDERRUN + 1 = UNDERRUN + */ + UART_VENDOR_STATE_RX_UNDERRUN = 1 << 2, + + /* This bit is set to 1 when write data is issued to the TX FIFO when it is already full and gets cleared on register read (sticky bit until read) + 0 = NO_OVERRUN + 1 = OVERRUN + */ + UART_VENDOR_STATE_TX_OVERRUN = 1 << 3, + + UART_VENDOR_STATE_RX_FIFO_COUNTER = 0b111111 << 16, /* reflects number of current entries in RX FIFO */ + UART_VENDOR_STATE_TX_FIFO_COUNTER = 0b111111 << 24 /* reflects number of current entries in TX FIFO */ + }; + + /* 36.3.6 UART_LSR_0 */ + enum UartLineStatus { + UART_LSR_RDR = 1 << 0, /* Receiver Data Ready */ + UART_LSR_OVRF = 1 << 1, /* Receiver Overrun Error */ + UART_LSR_PERR = 1 << 2, /* Parity Error */ + UART_LSR_FERR = 1 << 3, /* Framing Error */ + UART_LSR_BRK = 1 << 4, /* BREAK condition detected on line */ + UART_LSR_THRE = 1 << 5, /* Transmit Holding Register is Empty -- OK to write data */ + UART_LSR_TMTY = 1 << 6, /* Transmit Shift Register empty status */ + UART_LSR_FIFOE = 1 << 7, /* Receive FIFO Error */ + UART_LSR_TX_FIFO_FULL = 1 << 8, /* Transmitter FIFO full status */ + UART_LSR_RX_FIFO_EMPTY = 1 << 9, /* Receiver FIFO empty status */ + }; + + /* 36.3.4 UART_LCR_0 */ + enum UartLineControl { + UART_LCR_WD_LENGTH_5 = 0, /* word length 5 */ + UART_LCR_WD_LENGTH_6 = 1, /* word length 6 */ + UART_LCR_WD_LENGTH_7 = 2, /* word length 7 */ + UART_LCR_WD_LENGTH_8 = 3, /* word length 8 */ + + /* STOP: + 0 = Transmit 1 stop bit + 1 = Transmit 2 stop bits (receiver always checks for 1 stop bit) + */ + UART_LCR_STOP = 1 << 2, + UART_LCR_PAR = 1 << 3, /* Parity enabled */ + UART_LCR_EVEN = 1 << 4, /* Even parity format. There will always be an even number of 1s in the binary representation (PAR = 1) */ + UART_LCR_SET_P = 1 << 5, /* Set (force) parity to value in LCR[4] */ + UART_LCR_SET_B = 1 << 6, /* Set BREAK condition -- Transmitter sends all zeroes to indicate BREAK */ + UART_LCR_DLAB = 1 << 7, /* Divisor Latch Access Bit (set to allow programming of the DLH, DLM Divisors) */ + }; + + /* 36.3.3 UART_IIR_FCR_0 */ + enum UartFifoControl { + UART_FCR_FCR_EN_FIFO = 1 << 0, /* Enable the transmit and receive FIFOs. This bit should be enabled */ + UART_FCR_RX_CLR = 1 << 1, /* Clears the contents of the receive FIFO and resets its counter logic to 0 (the receive shift register is not cleared or altered). This bit returns to 0 after clearing the FIFOs */ + UART_FCR_TX_CLR = 1 << 2, /* Clears the contents of the transmit FIFO and resets its counter logic to 0 (the transmit shift register is not cleared or altered). This bit returns to 0 after clearing the FIFOs */ + + /* DMA: + 0 = DMA_MODE_0 + 1 = DMA_MODE_1 + */ + UART_FCR_DMA = 1 << 3, + + /* TX_TRIG + 0 = FIFO_COUNT_GREATER_16 + 1 = FIFO_COUNT_GREATER_8 + 2 = FIFO_COUNT_GREATER_4 + 3 = FIFO_COUNT_GREATER_1 + */ + UART_FCR_TX_TRIG = 3 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_16 = 0 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_8 = 1 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_4 = 2 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_1 = 3 << 4, + + /* RX_TRIG + 0 = FIFO_COUNT_GREATER_1 + 1 = FIFO_COUNT_GREATER_4 + 2 = FIFO_COUNT_GREATER_8 + 3 = FIFO_COUNT_GREATER_16 + */ + UART_FCR_RX_TRIG = 3 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_1 = 0 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_4 = 1 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_8 = 2 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_16 = 3 << 6, + }; + + /* 36.3.2 UART_IER_DLAB_0_0 */ + enum UartInterruptEnable { + UART_IER_IE_RHR = 1 << 0, /* Interrupt enable for Received Data Interrupt */ + UART_IER_IE_THR = 1 << 1, /* Interrupt enable for Transmitter Holding Register Empty interrupt */ + UART_IER_IE_RXS = 1 << 2, /* Interrupt enable for Receiver Line Status Interrupt */ + UART_IER_IE_MSI = 1 << 3, /* Interrupt enable for Modem Status Interrupt */ + UART_IER_IE_RX_TIMEOUT = 1 << 4, /* Interrupt enable for RX FIFO timeout */ + UART_IER_IE_EORD = 1 << 5, /* Interrupt enable for Interrupt Enable for End of Received Data */ + }; + + /* 36.3.3 UART_IIR_FCR_0 */ + enum UartInterruptIdentification { + UART_IIR_IS_STA = 1 << 0, /* Interrupt Pending if ZERO */ + UART_IIR_IS_PRI0 = 1 << 1, /* Encoded Interrupt ID Refer to IIR[3:0] table [36.3.3] */ + UART_IIR_IS_PRI1 = 1 << 2, /* Encoded Interrupt ID Refer to IIR[3:0] table */ + UART_IIR_IS_PRI2 = 1 << 3, /* Encoded Interrupt ID Refer to IIR[3:0] table */ + + /* FIFO Mode Status + 0 = 16450 mode (no FIFO) + 1 = 16550 mode (FIFO) + */ + UART_IIR_EN_FIFO = 3 << 6, + UART_IIR_MODE_16450 = 0 << 6, + UART_IIR_MODE_16550 = 1 << 6, + }; + + /* 36.3.9 UART_IRDA_CSR_0 */ + enum UartIrDAPulseCodingCSR { + UART_IRDA_CSR_INVERT_RXD = 1 << 0, + UART_IRDA_CSR_INVERT_TXD = 1 << 1, + UART_IRDA_CSR_INVERT_CTS = 1 << 2, + UART_IRDA_CSR_INVERT_RTS = 1 << 3, + + UART_IRDA_CSR_PWT_A_BAUD_PULSE_3_14 = 0 << 6, + UART_IRDA_CSR_PWT_A_BAUD_PULSE_4_14 = 1 << 6, + UART_IRDA_CSR_SIR_A = 1 << 7, + }; + +} diff --git a/libraries/libexosphere/source/util/util_api.cpp b/libraries/libexosphere/source/util/util_api.cpp new file mode 100644 index 000000000..c8d46f554 --- /dev/null +++ b/libraries/libexosphere/source/util/util_api.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 + +namespace ams::util { + + namespace { + + constinit uintptr_t g_timer_register_address = secmon::MemoryRegionPhysicalDeviceTimer.GetAddress(); + + ALWAYS_INLINE uintptr_t GetCurrentTimeRegisterAddress() { + return g_timer_register_address + 0x10; + } + + } + + void SetRegisterAddress(uintptr_t address) { + g_timer_register_address = address; + } + + u32 GetMicroSeconds() { + return reg::Read(GetCurrentTimeRegisterAddress()); + } + + void WaitMicroSeconds(int us) { + const u32 start = reg::Read(GetCurrentTimeRegisterAddress()); + u32 cur = start; + while ((cur - start) <= static_cast(us)) { + cur = reg::Read(GetCurrentTimeRegisterAddress()); + } + } + + void ClearMemory(void *ptr, size_t size) { + std::memset(ptr, 0, size); + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/wdt/wdt_api.cpp b/libraries/libexosphere/source/wdt/wdt_api.cpp new file mode 100644 index 000000000..cb26f5e05 --- /dev/null +++ b/libraries/libexosphere/source/wdt/wdt_api.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::wdt { + + namespace { + + volatile uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceTimer.GetAddress(); + + #if defined(ATMOSPHERE_ARCH_ARM64) + NOINLINE void Reboot(uintptr_t registers) { + __asm__ __volatile__( + /* Get the current core. */ + "mrs x12, mpidr_el1\n" + "and x12, x12, #0xFF\n" + + /* Get the offsets of the registers we want to write */ + "mov x10, #0x8\n" + "mov x11, #0x20\n" + "madd x10, x10, x12, %[registers]\n" + "madd x11, x11, x12, %[registers]\n" + "add x10, x10, #0x60\n" + "add x11, x11, #0x100\n" + + /* Write the magic unlock pattern. */ + "mov w9, #0xC45A\n" + "str w9, [x11, #0xC]\n" + + /* Disable the counters. */ + "mov w9, #0x2\n" + "str w9, [x11, #0x8]\n" + + /* Start periodic timer. */ + "mov w9, #0xC0000000\n" + "str w9, [x10]\n" + + /* Set reboot source to the timer we started. */ + "mov w9, #0x8015\n" + "add w9, w9, w12\n" + "str w9, [x11]\n" + + /* Enable the counters. */ + "mov w9, #0x1\n" + "str w9, [x11, #0x8]\n" + + /* Wait forever. */ + "1: b 1b" + : [registers]"=&r"(registers) + : + : "x9", "x10", "x11", "x12", "memory" + ); + } + #elif defined(ATMOSPHERE_ARCH_ARM) + NOINLINE void Reboot(uintptr_t registers) { + /* Write the magic unlock pattern. */ + reg::Write(registers + 0x18C, 0xC45A); + + /* Disable the counters. */ + reg::Write(registers + 0x188, 0x2); + + /* Start periodic timer. */ + reg::Write(registers + 0x080, 0xC0000000); + + /* Set reboot source to the timer we started. */ + reg::Write(registers + 0x180, 0x8019); + + /* Enable the counters. */ + reg::Write(registers + 0x188, 0x1); + + while (true) { /* ... */ } + } + #endif + + } + + void SetRegisterAddress(uintptr_t address) { + g_register_address = address; + } + + NOINLINE void Reboot() { + const uintptr_t registers = g_register_address; + Reboot(registers); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp index 905d479a3..4108343ab 100644 --- a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp +++ b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp @@ -132,4 +132,8 @@ namespace ams::diag { AbortWithValue(value); } + NORETURN WEAK_SYMBOL void AbortImpl() { + AbortWithValue(0); + } + } diff --git a/libraries/libvapours/include/vapours/assert.hpp b/libraries/libvapours/include/vapours/assert.hpp index 8402cbc52..8cd61bd61 100644 --- a/libraries/libvapours/include/vapours/assert.hpp +++ b/libraries/libvapours/include/vapours/assert.hpp @@ -32,6 +32,7 @@ namespace ams::diag { NORETURN NOINLINE void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) __attribute__((format(printf, 6, 7))); NORETURN NOINLINE void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value); + NORETURN NOINLINE void AbortImpl(); } @@ -42,7 +43,7 @@ namespace ams::diag { #define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, 0, ## __VA_ARGS__) #else #define AMS_CALL_ASSERT_FAIL_IMPL(cond, ...) ::ams::diag::AssertionFailureImpl("", 0, "", "", 0) -#define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl("", 0, "", "", 0) +#define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl() #endif #ifdef AMS_ENABLE_ASSERTIONS diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_ctr_mode_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_ctr_mode_impl.hpp index b588bc2c3..df5955d68 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_ctr_mode_impl.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_ctr_mode_impl.hpp @@ -142,7 +142,7 @@ namespace ams::crypto::impl { u64 _block[IvSize / sizeof(u64)] = {}; util::StoreBigEndian(std::addressof(_block[(IvSize / sizeof(u64)) - 1]), count); - u16 acc; + u16 acc = 0; const u8 *block = reinterpret_cast(_block); for (s32 i = IvSize - 1; i >= 0; --i) { acc += (this->counter[i] + block[i]); diff --git a/libraries/libvapours/include/vapours/defines.hpp b/libraries/libvapours/include/vapours/defines.hpp index 391640806..61c733237 100644 --- a/libraries/libvapours/include/vapours/defines.hpp +++ b/libraries/libvapours/include/vapours/defines.hpp @@ -59,4 +59,6 @@ #define AMS_LIKELY(expr) AMS_PREDICT_TRUE(expr, 1.0) #define AMS_UNLIKELY(expr) AMS_PREDICT_FALSE(expr, 1.0) +#define AMS_ASSUME(expr) do { if (!static_cast((expr))) { __builtin_unreachable(); } } while (0) + #define AMS_CURRENT_FUNCTION_NAME __FUNCTION__ diff --git a/libraries/libvapours/include/vapours/literals.hpp b/libraries/libvapours/include/vapours/literals.hpp index ddd22a6b6..7f741a916 100644 --- a/libraries/libvapours/include/vapours/literals.hpp +++ b/libraries/libvapours/include/vapours/literals.hpp @@ -19,16 +19,16 @@ namespace ams { inline namespace literals { - constexpr ALWAYS_INLINE size_t operator ""_KB(unsigned long long n) { - return static_cast(n) * size_t(1024); + constexpr ALWAYS_INLINE u64 operator ""_KB(unsigned long long n) { + return static_cast(n) * UINT64_C(1024); } - constexpr ALWAYS_INLINE size_t operator ""_MB(unsigned long long n) { - return operator ""_KB(n) * size_t(1024); + constexpr ALWAYS_INLINE u64 operator ""_MB(unsigned long long n) { + return operator ""_KB(n) * UINT64_C(1024); } - constexpr ALWAYS_INLINE size_t operator ""_GB(unsigned long long n) { - return operator ""_MB(n) * size_t(1024); + constexpr ALWAYS_INLINE u64 operator ""_GB(unsigned long long n) { + return operator ""_MB(n) * UINT64_C(1024); } } } diff --git a/libraries/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp b/libraries/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp new file mode 100644 index 000000000..391dad000 --- /dev/null +++ b/libraries/libvapours/include/vapours/svc/arch/arm/svc_thread_local_region.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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::svc::arch::arm { + + constexpr inline size_t NumTlsSlots = 16; + constexpr inline size_t MessageBufferSize = 0x100; + + struct ThreadLocalRegion { + u32 message_buffer[MessageBufferSize / sizeof(u32)]; + volatile u16 disable_count; + volatile u16 interrupt_flag; + /* TODO: Should we bother adding the Nintendo aarch32 thread local context here? */ + uintptr_t TODO[(0x200 - 0x104) / sizeof(uintptr_t)]; + }; + + ALWAYS_INLINE ThreadLocalRegion *GetThreadLocalRegion() { + ThreadLocalRegion *tlr; + __asm__ __volatile__("mrc p15, 0, %[tlr], c13, c0, 3" : [tlr]"=&r"(tlr) :: "memory"); + return tlr; + } + +} diff --git a/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp b/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp index 3f3393661..e7b3e6f60 100644 --- a/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_select_thread_local_region.hpp @@ -25,6 +25,14 @@ using ams::svc::arch::arm64::GetThreadLocalRegion; } +#elif defined(ATMOSPHERE_ARCH_ARM) + + #include + namespace ams::svc { + using ams::svc::arch::arm::ThreadLocalRegion; + using ams::svc::arch::arm::GetThreadLocalRegion; + } + #else #error "Unknown architecture for svc::ThreadLocalRegion" diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp index 4567e5d0b..8e7a5049e 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp @@ -225,7 +225,7 @@ namespace ams::svc { /* Thread types. */ using ThreadFunc = ams::svc::Address; -#ifdef ATMOSPHERE_ARCH_ARM64 +#if defined(ATMOSPHERE_ARCH_ARM64) struct ThreadContext { u64 r[29]; @@ -242,8 +242,23 @@ namespace ams::svc { }; static_assert(sizeof(ThreadContext) == 0x320); +#elif defined(ATMOSPHERE_ARCH_ARM) + + struct ThreadContext { + u32 r[13]; + u32 sp; + u32 lr; + u32 pc; + u32 cpsr; + u32 padding; + u64 fpu_registers[32]; + u32 fpscr; + u32 fpexc; + u32 tpidr; + }; + #else - #error >Unknown Architecture for ams::svc::ThreadContext> + #error "Unknown Architecture for ams::svc::ThreadContext" #endif enum ThreadSuspend : u32 { diff --git a/libraries/libvapours/include/vapours/types.hpp b/libraries/libvapours/include/vapours/types.hpp index d77ea21fe..6807e9df2 100644 --- a/libraries/libvapours/include/vapours/types.hpp +++ b/libraries/libvapours/include/vapours/types.hpp @@ -25,25 +25,28 @@ typedef uint8_t u8; ///< 8-bit unsigned integer. typedef uint16_t u16; ///< 16-bit unsigned integer. typedef uint32_t u32; ///< 32-bit unsigned integer. typedef uint64_t u64; ///< 64-bit unsigned integer. -typedef __uint128_t u128; ///< 128-bit unsigned integer. typedef int8_t s8; ///< 8-bit signed integer. typedef int16_t s16; ///< 16-bit signed integer. typedef int32_t s32; ///< 32-bit signed integer. typedef int64_t s64; ///< 64-bit signed integer. -typedef __int128_t s128; ///< 128-bit unsigned integer. typedef volatile u8 vu8; ///< 8-bit volatile unsigned integer. typedef volatile u16 vu16; ///< 16-bit volatile unsigned integer. typedef volatile u32 vu32; ///< 32-bit volatile unsigned integer. typedef volatile u64 vu64; ///< 64-bit volatile unsigned integer. -typedef volatile u128 vu128; ///< 128-bit volatile unsigned integer. typedef volatile s8 vs8; ///< 8-bit volatile signed integer. typedef volatile s16 vs16; ///< 16-bit volatile signed integer. typedef volatile s32 vs32; ///< 32-bit volatile signed integer. typedef volatile s64 vs64; ///< 64-bit volatile signed integer. + +#ifdef ATMOSPHERE_ARCH_ARM64 +typedef __uint128_t u128; ///< 128-bit unsigned integer. +typedef __int128_t s128; ///< 128-bit unsigned integer. +typedef volatile u128 vu128; ///< 128-bit volatile unsigned integer. typedef volatile s128 vs128; ///< 128-bit volatile signed integer. +#endif typedef u32 Result; ///< Function error code result type. diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index 43e2806e1..c10fa5d37 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/util/util_aligned_buffer.hpp b/libraries/libvapours/include/vapours/util/util_aligned_buffer.hpp new file mode 100644 index 000000000..fc405490e --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_aligned_buffer.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include + +namespace ams::util { + + template + class AlignedBuffer { + private: + static constexpr size_t AlignedSize = ((Size + Alignment - 1) / Alignment) * Alignment; + static_assert(AlignedSize % Alignment == 0); + private: + u8 buffer[Alignment + AlignedSize]; + public: + ALWAYS_INLINE operator u8 *() { return reinterpret_cast(util::AlignUp(reinterpret_cast(this->buffer), Alignment)); } + }; + +} \ No newline at end of file diff --git a/libraries/libvapours/include/vapours/util/util_bitpack.hpp b/libraries/libvapours/include/vapours/util/util_bitpack.hpp index c89058d61..4b5c1e863 100644 --- a/libraries/libvapours/include/vapours/util/util_bitpack.hpp +++ b/libraries/libvapours/include/vapours/util/util_bitpack.hpp @@ -42,7 +42,7 @@ namespace ams::util { } }(); public: - template + template struct Field { using Type = T; static constexpr size_t Index = _Index; diff --git a/libraries/libvapours/include/vapours/util/util_endian.hpp b/libraries/libvapours/include/vapours/util/util_endian.hpp index 19844f202..f269796d2 100644 --- a/libraries/libvapours/include/vapours/util/util_endian.hpp +++ b/libraries/libvapours/include/vapours/util/util_endian.hpp @@ -135,21 +135,21 @@ namespace ams::util { } template requires std::integral - constexpr ALWAYS_INLINE T LoadBigEndian(T *ptr) { + constexpr ALWAYS_INLINE T LoadBigEndian(const T *ptr) { return ConvertToBigEndian(*ptr); } template requires std::integral - constexpr ALWAYS_INLINE T LoadLittleEndian(T *ptr) { + constexpr ALWAYS_INLINE T LoadLittleEndian(const T *ptr) { return ConvertToLittleEndian(*ptr); } - template + template requires std::integral constexpr ALWAYS_INLINE void StoreBigEndian(T *ptr, T val) { *ptr = ConvertToBigEndian(val); } - template + template requires std::integral constexpr ALWAYS_INLINE void StoreLittleEndian(T *ptr, T val) { *ptr = ConvertToLittleEndian(val); } diff --git a/libraries/libvapours/include/vapours/util/util_tinymt.hpp b/libraries/libvapours/include/vapours/util/util_tinymt.hpp index 7611c9b7c..a200fc4b2 100644 --- a/libraries/libvapours/include/vapours/util/util_tinymt.hpp +++ b/libraries/libvapours/include/vapours/util/util_tinymt.hpp @@ -242,7 +242,7 @@ namespace ams::util { const u32 first = (this->GenerateRandomU32() >> Shift1st); const u32 second = (this->GenerateRandomU32() >> Shift2nd); - return (1.0 * first * (1ul << (32 - Shift2nd)) + second) * (1.0 / (1ul << MantissaBits)); + return (1.0 * first * (static_cast(1) << (32 - Shift2nd)) + second) * (1.0 / (static_cast(1) << MantissaBits)); } };