diff --git a/exosphere/Makefile b/exosphere/Makefile new file mode 100644 index 0000000..3bdaf72 --- /dev/null +++ b/exosphere/Makefile @@ -0,0 +1,71 @@ +rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITPRO)/devkitA64/base_tools + +name := exosphere + +dir_source := src +dir_build := build +dir_out := out +dir_bpmpfw := bpmpfw + +ARCH := -march=armv8-a -mtune=cortex-a57 + +ASFLAGS := -g $(ARCH) + +CFLAGS = \ + $(ARCH) \ + -g \ + -O2 \ + -ffunction-sections \ + -fdata-sections \ + -fomit-frame-pointer \ + -std=gnu11 \ + -Werror \ + -Wall + +LDFLAGS = -g $(ARCH) -nostartfiles -Wl,--nmagic,--gc-sections + +objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ + $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.s *.c))) + +.PHONY: all +all: $(dir_out)/$(name).bin + +.PHONY: clean +clean: + @$(MAKE) -C $(dir_bpmpfw) clean + @rm -rf $(dir_build) + @rm -rf $(dir_out) + +.PHONY: $(dir_bpmpfw) + +$(dir_out)/$(name).bin: $(dir_build)/$(name).elf + @mkdir -p "$(@D)" + $(OBJCOPY) -S -O binary $< $@ + +$(dir_build)/$(name).elf: $(objects) + $(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^ + +$(dir_bpmpfw)/out/bpmpfw.bin: $(dir_bpmpfw) + @$(MAKE) -C $< + +$(dir_build)/bpmpfw.bin: $(dir_bpmpfw)/out/bpmpfw.bin + @mkdir -p "$(@D)" + @cp $< $(@D) + +$(dir_build)/%.bin.o: $(dir_build)/%.bin + @$(bin2o) + +$(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/bpmpfw.bin.o + @mkdir -p "$(@D)" + $(COMPILE.c) $(OUTPUT_OPTION) $< + +$(dir_build)/%.o: $(dir_source)/%.s + @mkdir -p "$(@D)" + $(COMPILE.c) -x assembler-with-cpp $(OUTPUT_OPTION) $< diff --git a/exosphere/bootconfig.h b/exosphere/bootconfig.h deleted file mode 100644 index 5fda115..0000000 --- a/exosphere/bootconfig.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef EXOSPHERE_BOOTCONFIG_H -#define EXOSPHERE_BOOTCONFIG_H - -#include -#include - -/* This provides management for Switch BootConfig. */ - -typedef struct { - uint8_t unsigned_config[0x200]; - uint8_t signature[0x100]; - uint8_t signed_config[0x100]; - uint8_t unknown_config[0x240]; -} bootconfig_t; - -void bootconfig_load_and_verify(const bootconfig_t *bootconfig); -void bootconfig_clear(void); - -/* Actual configuration getters. */ -bool bootconfig_is_package2_plaintext(void); -bool bootconfig_is_package2_unsigned(void); -bool bootconfig_disable_program_verification(void); -bool bootconfig_is_debug_mode(void); - -uint64_t bootconfig_get_memory_arrangement(void); -uint64_t bootconfig_get_kernel_memory_configuration(void); - -#endif \ No newline at end of file diff --git a/exosphere/bpmpfw/Makefile b/exosphere/bpmpfw/Makefile index 87df99b..74ca355 100644 --- a/exosphere/bpmpfw/Makefile +++ b/exosphere/bpmpfw/Makefile @@ -6,7 +6,7 @@ include $(DEVKITARM)/base_tools -name := $(shell basename $(CURDIR)) +name := bpmpfw dir_source := src dir_build := build diff --git a/exosphere/build/bpmpfw.bin b/exosphere/build/bpmpfw.bin new file mode 100644 index 0000000..a362f8e --- /dev/null +++ b/exosphere/build/bpmpfw.bin Binary files differ diff --git a/exosphere/cache.h b/exosphere/cache.h deleted file mode 100644 index 03fa1a9..0000000 --- a/exosphere/cache.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef EXOSPHERE_CACHE_H -#define EXOSPHERE_CACHE_H - -#include - -void tlb_invalidate_all(void); -void tlb_invalidate_all_inner_shareable(void); - -void tlb_invalidate_page(const void *page); -void tlb_invalidate_page_inner_shareable(const void *page); - -void flush_dcache_all(void); -void invalidate_dcache_all(void); - -void flush_dcache_range(const void *start, const void *end); -void invalidate_dcache_range(const void *start, const void *end); - -void invalidate_icache_inner_shareable(void); - -#endif diff --git a/exosphere/cache.s b/exosphere/cache.s deleted file mode 100644 index 128afe9..0000000 --- a/exosphere/cache.s +++ /dev/null @@ -1,229 +0,0 @@ -.section .text.tlb_invalidate_all, "ax", %progbits -.type tlb_invalidate_all, %function -.global tlb_invalidate_all -tlb_invalidate_all: - dsb sy - tlbi alle3 - dsb sy - isb - ret - -.section .text.tlb_invalidate_all_inner_shareable, "ax", %progbits -.type tlb_invalidate_all_inner_shareable, %function -.global tlb_invalidate_all_inner_shareable -tlb_invalidate_all_inner_shareable: - dsb ish - tlbi alle3is - dsb ish - isb - ret - -.section .text.tlb_invalidate_page, "ax", %progbits -.type tlb_invalidate_page, %function -.global tlb_invalidate_page -tlb_invalidate_page: - lsr x8, x0, #12 - dsb sy - tlbi vale3, x8 - dsb sy - isb - ret - -.section .text.tlb_invalidate_page_inner_shareable, "ax", %progbits -.type tlb_invalidate_page_inner_shareable, %function -.global tlb_invalidate_page_inner_shareable -tlb_invalidate_page_inner_shareable: - lsr x8, x0, #12 - dsb ish - tlbi vale3is, x8 - dsb ish - isb - ret - -/* The following functions are taken/adapted from https://github.com/u-boot/u-boot/blob/master/arch/arm/cpu/armv8/cache.S */ - -/* - * (C) Copyright 2013 - * David Feng - * - * This file is based on sample code from ARMv8 ARM. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * void __asm_dcache_level(level) - * - * flush or invalidate one level cache. - * - * x0: cache level - * x1: 0 clean & invalidate, 1 invalidate only - * x2~x9: clobbered - */ -.section .text.__asm_dcache_level, "ax", %progbits -.type __asm_dcache_level, %function -__asm_dcache_level: - lsl x12, x0, #1 - msr csselr_el1, x12 /* select cache level */ - isb /* sync change of cssidr_el1 */ - mrs x6, ccsidr_el1 /* read the new cssidr_el1 */ - and x2, x6, #7 /* x2 <- log2(cache line size)-4 */ - add x2, x2, #4 /* x2 <- log2(cache line size) */ - mov x3, #0x3ff - and x3, x3, x6, lsr #3 /* x3 <- max number of #ways */ - clz w5, w3 /* bit position of #ways */ - mov x4, #0x7fff - and x4, x4, x6, lsr #13 /* x4 <- max number of #sets */ - /* x12 <- cache level << 1 */ - /* x2 <- line length offset */ - /* x3 <- number of cache ways - 1 */ - /* x4 <- number of cache sets - 1 */ - /* x5 <- bit position of #ways */ - -loop_set: - mov x6, x3 /* x6 <- working copy of #ways */ -loop_way: - lsl x7, x6, x5 - orr x9, x12, x7 /* map way and level to cisw value */ - lsl x7, x4, x2 - orr x9, x9, x7 /* map set number to cisw value */ - tbz w1, #0, 1f - dc isw, x9 - b 2f -1: dc cisw, x9 /* clean & invalidate by set/way */ -2: subs x6, x6, #1 /* decrement the way */ - b.ge loop_way - subs x4, x4, #1 /* decrement the set */ - b.ge loop_set - - ret - -/* - * void __asm_flush_dcache_all(int invalidate_only) - * - * x0: 0 clean & invalidate, 1 invalidate only - * - * flush or invalidate all data cache by SET/WAY. - */ -.section .text.__asm_dcache_all, "ax", %progbits -.type __asm_dcache_all, %function -__asm_dcache_all: - mov x1, x0 - dsb sy - mrs x10, clidr_el1 /* read clidr_el1 */ - lsr x11, x10, #24 - and x11, x11, #0x7 /* x11 <- loc */ - cbz x11, finished /* if loc is 0, exit */ - mov x15, lr - mov x0, #0 /* start flush at cache level 0 */ - /* x0 <- cache level */ - /* x10 <- clidr_el1 */ - /* x11 <- loc */ - /* x15 <- return address */ - -loop_level: - lsl x12, x0, #1 - add x12, x12, x0 /* x0 <- tripled cache level */ - lsr x12, x10, x12 - and x12, x12, #7 /* x12 <- cache type */ - cmp x12, #2 - b.lt skip /* skip if no cache or icache */ - bl __asm_dcache_level /* x1 = 0 flush, 1 invalidate */ -skip: - add x0, x0, #1 /* increment cache level */ - cmp x11, x0 - b.gt loop_level - - mov x0, #0 - msr csselr_el1, x0 /* restore csselr_el1 */ - dsb sy - isb - mov lr, x15 - -finished: - ret - -.section .text.flush_dcache_all, "ax", %progbits -.type flush_dcache_all, %function -.global flush_dcache_all -flush_dcache_all: - mov x0, #0 - b __asm_dcache_all - -.section .text.invalidate_dcache_all, "ax", %progbits -.type invalidate_dcache_all, %function -.global invalidate_dcache_all -invalidate_dcache_all: - mov x0, #1 - b __asm_dcache_all - -/* - * void __asm_flush_dcache_range(start, end) (renamed -> flush_dcache_range) - * - * clean & invalidate data cache in the range - * - * x0: start address - * x1: end address - */ -.section .text.flush_dcache_range, "ax", %progbits -.type flush_dcache_range, %function -.global flush_dcache_range -flush_dcache_range: - mrs x3, ctr_el0 - lsr x3, x3, #16 - and x3, x3, #0xf - mov x2, #4 - lsl x2, x2, x3 /* cache line size */ - - /* x2 <- minimal cache line size in cache system */ - sub x3, x2, #1 - bic x0, x0, x3 -1: dc civac, x0 /* clean & invalidate data or unified cache */ - add x0, x0, x2 - cmp x0, x1 - b.lo 1b - dsb sy - ret - -/* - * void __asm_invalidate_dcache_range(start, end) (-> invalidate_dcache_range) - * - * invalidate data cache in the range - * - * x0: start address - * x1: end address - */ -.section .text.invalidate_dcache_range, "ax", %progbits -.type invalidate_dcache_range, %function -.global invalidate_dcache_range -invalidate_dcache_range: - mrs x3, ctr_el0 - ubfm x3, x3, #16, #19 - mov x2, #4 - lsl x2, x2, x3 /* cache line size */ - - /* x2 <- minimal cache line size in cache system */ - sub x3, x2, #1 - bic x0, x0, x3 -1: dc ivac, x0 /* invalidate data or unified cache */ - add x0, x0, x2 - cmp x0, x1 - b.lo 1b - dsb sy - ret - -/* - * void __asm_invalidate_icache_all(void) (-> invalidate_icache_inner_shareable) - * - * invalidate all icache entries. - */ -.section .text.invalidate_icache_inner_shareable, "ax", %progbits -.type invalidate_icache_inner_shareable, %function -.global invalidate_icache_inner_shareable -invalidate_icache_inner_shareable: - dsb ish - isb - ic ialluis - dsb ish - isb - ret diff --git a/exosphere/coldboot_init.c b/exosphere/coldboot_init.c deleted file mode 100644 index 0992a10..0000000 --- a/exosphere/coldboot_init.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "utils.h" -#include "mmu.h" -#include "memory_map.h" - -extern void (*__preinit_array_start[])(void); -extern void (*__preinit_array_end[])(void); -extern void (*__init_array_start[])(void); -extern void (*__init_array_end[])(void); -extern void _init(void); - -extern uint8_t __warmboot_crt0_start__[], __warmboot_crt0_end__[], __warmboot_crt0_lma__[]; -extern uint8_t __main_start__[], __main_end__[], __main_lma__[]; -extern uint8_t __pk2ldr_start__[], __pk2ldr_end__[], __pk2ldr_lma__[]; -extern uint8_t __vectors_start__[], __vectors_end__[], __vectors_lma__[]; -extern void flush_dcache_all_tzram_pa(void); -extern void invalidate_icache_all_tzram_pa(void); - -uintptr_t get_coldboot_crt0_stack_address(void); - -static void configure_ttbls(void) { - uintptr_t *mmu_l1_tbl = (uintptr_t *)(tzram_get_segment_pa(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64); - uintptr_t *mmu_l2_tbl = (uintptr_t *)tzram_get_segment_pa(TZRAM_SEGMENT_ID_L2_TRANSLATION_TABLE); - uintptr_t *mmu_l3_tbl = (uintptr_t *)tzram_get_segment_pa(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - - mmu_init_table(mmu_l1_tbl, 64); /* 33-bit address space */ - mmu_init_table(mmu_l2_tbl, 4096); - /* - Nintendo uses the same L3 table for everything, but they make sure - nothing clashes. - */ - mmu_init_table(mmu_l3_tbl, 4096); - - mmu_map_table(1, mmu_l1_tbl, 0x40000000, mmu_l2_tbl, 0); - mmu_map_table(1, mmu_l1_tbl, 0x1C0000000, mmu_l2_tbl, 0); - - mmu_map_table(2, mmu_l2_tbl, 0x40000000, mmu_l3_tbl, 0); - mmu_map_table(2, mmu_l2_tbl, 0x7C000000, mmu_l3_tbl, 0); - mmu_map_table(2, mmu_l2_tbl, 0x1F0000000ull, mmu_l3_tbl, 0); - - identity_map_all_mappings(mmu_l1_tbl, mmu_l3_tbl); - mmio_map_all_devices(mmu_l3_tbl); - lp0_map_all_plaintext_ram_segments(mmu_l3_tbl); - lp0_map_all_ciphertext_ram_segments(mmu_l3_tbl); - tzram_map_all_segments(mmu_l3_tbl); -} - -static void copy_lma_to_vma(unsigned int segment_id, void *lma, size_t size, bool vma_is_pa) { - uintptr_t vma = vma_is_pa ? tzram_get_segment_pa(segment_id) : tzram_get_segment_address(segment_id); - uintptr_t vma_offset = (uintptr_t)lma & 0xFFF; - uint64_t *p_vma = (uint64_t *)vma; - uint64_t *p_lma = (uint64_t *)lma; - for (size_t i = 0; i < size / 8; i++) { - p_vma[vma_offset / 8 + i] = p_lma[i]; - } -} - -static void __libc_init_array(void) { - for (size_t i = 0; i < __preinit_array_end - __preinit_array_start; i++) - __preinit_array_start[i](); - _init(); /* FIXME: do we have this gcc-provided symbol if we build with -nostartfiles? */ - for (size_t i = 0; i < __init_array_end - __init_array_start; i++) - __init_array_start[i](); -} - -uintptr_t get_coldboot_crt0_stack_address(void) { - return tzram_get_segment_pa(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x800; -} - -void coldboot_init(void) { - /* TODO: Set NX BOOTLOADER clock time field */ - copy_lma_to_vma(TZRAM_SEGMENT_ID_WARMBOOT_CRT0_AND_MAIN, __warmboot_crt0_lma__, __warmboot_crt0_end__ - __warmboot_crt0_start__, true); - /* TODO: set some mmio regs, etc. */ - /* TODO: initialize DMA controllers */ - configure_ttbls(); - copy_lma_to_vma(TZRAM_SEGMENT_ID_WARMBOOT_CRT0_AND_MAIN, __main_lma__, __main_end__ - __main_start__, false); - copy_lma_to_vma(TZRAM_SEGMENT_ID_PK2LDR, __pk2ldr_lma__, __pk2ldr_end__ - __pk2ldr_start__, false); - copy_lma_to_vma(TZRAM_SEGEMENT_ID_SECMON_EVT, __vectors_lma__, __vectors_end__ - __vectors_start__, false); - /* TODO: set the MMU regs & tlbi & enable MMU */ - - flush_dcache_all_tzram_pa(); - invalidate_icache_all_tzram_pa(); - /* TODO: zero-initialize the cpu context */ - /* Nintendo clears the (emtpy) pk2ldr's BSS section, but we embed it 0-filled in the binary */ - __libc_init_array(); /* construct global objects */ -} diff --git a/exosphere/coldboot_main.c b/exosphere/coldboot_main.c deleted file mode 100644 index bb76ce8..0000000 --- a/exosphere/coldboot_main.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include "utils.h" -#include "mmu.h" -#include "memory_map.h" - -extern void (*__fini_array_start[])(void); -extern void (*__fini_array_end[])(void); -extern void _fini(void); - -extern uint8_t __pk2ldr_start__[], __pk2ldr_end__[]; - -extern void __jump_to_lower_el(uint64_t arg, uintptr_t ep, unsigned int el); - -void coldboot_main(void); - -/* Needs to be called for EL3->EL3 chainloading (and only in that case). TODO: use it */ -static void __libc_fini_array(void) __attribute__((used)) { - for (size_t i = __fini_array_end - __fini_array_start; i > 0; i--) - __fini_array_start[i - 1](); - _fini(); /* FIXME: do we have this gcc-provided symbol if we build with -nostartfiles? */ -} - -void coldboot_main(void) { - uintptr_t *mmu_l3_table = (uintptr_t *)tzram_get_segment_address(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); - void *pk2ldr = (void *)tzram_get_segment_address(TZRAM_SEGMENT_ID_PK2LDR); - - /* Clear and unmap pk2ldr (which is reused as exception entry stacks) */ - memset(pk2ldr, 0, __pk2ldr_end__ - __pk2ldr_start__); - mmu_unmap_range(mmu_l3_table, pk2ldr, __pk2ldr_end__ - __pk2ldr_start__); - tlb_invalidate_all_inner_shareable(); - - /* TODO: stuff & jump to lower EL */ -} diff --git a/exosphere/configitem.c b/exosphere/configitem.c deleted file mode 100644 index 2e21a4d..0000000 --- a/exosphere/configitem.c +++ /dev/null @@ -1,88 +0,0 @@ -#include - -#include "bootconfig.h" -#include "configitem.h" -#include "interrupt.h" -#include "package2.h" -#include "se.h" -#include "utils.h" - -int g_battery_profile = 0; - -uint32_t configitem_set(enum ConfigItem item, uint64_t value) { - if (item != CONFIGITEM_BATTERYPROFILE) { - return 2; - } - - g_battery_profile = ((int)(value != 0)) & 1; -} - -bool configitem_is_recovery_boot(void) { - uint64_t is_recovery_boot; - if (configitem_get(CONFIGITEM_ISRECOVERYBOOT, &is_recovery_boot) != 0) { - generic_panic(); - } - - return is_recovery_boot != 0; -} - -bool configitem_is_retail(void) { - uint64_t is_retail; - if (configitem_get(CONFIGITEM_ISRETAIL, &is_retail) != 0) { - generic_panic(); - } - - return is_retail != 0; -} - -uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue) { - uint32_t result = 0; - switch (item) { - case CONFIGITEM_DISABLEPROGRAMVERIFICATION: - *p_outvalue = (int)(bootconfig_disable_program_verification()); - break; - case CONFIGITEM_DRAMID: - *p_outvalue = fuse_get_dram_id(); - break; - case CONFIGITEM_SECURITYENGINEIRQ: - /* SE is interrupt #0x2C. */ - *p_outvalue = INTERRUPT_ID_USER_SECURITY_ENGINE; - break; - case CONFIGITEM_VERSION: - /* Always returns maxver - 1 on hardware. */ - *p_outvalue = PACKAGE2_MAXVER_400_CURRENT - 1; - break; - case CONFIGITEM_HARDWARETYPE: - *p_outvalue = fuse_get_hardware_type(); - break; - case CONFIGITEM_ISRETAIL: - *p_outvalue = fuse_get_retail_type(); - break; - case CONFIGITEM_ISRECOVERYBOOT: - /* TODO: This requires reading values passed to crt0 via NX_Bootloader. TBD pending crt0 implementation. */ - *p_outvalue = 0; - break; - case CONFIGITEM_DEVICEID: - *p_outvalue = fuse_get_device_id(); - break; - case CONFIGITEM_BOOTREASON: - /* TODO: This requires reading values passed to crt0 via NX_Bootloader. TBD pending crt0 implementation. */ - break; - case CONFIGITEM_MEMORYARRANGE: - *p_outvalue = bootconfig_get_memory_arrangement(); - break; - case CONFIGITEM_ISDEBUGMODE: - *p_outvalue = (int)(bootconfig_is_debug_mode()); - break; - case CONFIGITEM_KERNELMEMORYCONFIGURATION: - *p_outvalue = bootconfig_get_kernel_memory_configuration(); - break; - case CONFIGITEM_BATTERYPROFILE: - *p_outvalue = g_battery_profile; - break; - default: - result = 2; - break; - } - return result; -} diff --git a/exosphere/configitem.h b/exosphere/configitem.h deleted file mode 100644 index f176d9b..0000000 --- a/exosphere/configitem.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef EXOSPHERE_CFG_ITEM_H -#define EXOSPHERE_CFG_ITEM_H - -#include -#include - -enum ConfigItem { - CONFIGITEM_DISABLEPROGRAMVERIFICATION = 1, - CONFIGITEM_DRAMID = 2, - CONFIGITEM_SECURITYENGINEIRQ = 3, - CONFIGITEM_VERSION = 4, - CONFIGITEM_HARDWARETYPE = 5, - CONFIGITEM_ISRETAIL = 6, - CONFIGITEM_ISRECOVERYBOOT = 7, - CONFIGITEM_DEVICEID = 8, - CONFIGITEM_BOOTREASON = 9, - CONFIGITEM_MEMORYARRANGE = 10, - CONFIGITEM_ISDEBUGMODE = 11, - CONFIGITEM_KERNELMEMORYCONFIGURATION = 12, - CONFIGITEM_BATTERYPROFILE = 13 -}; - -uint32_t configitem_set(enum ConfigItem item, uint64_t value); -uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue); - -bool configitem_is_recovery_boot(void); -bool configitem_is_retail(void); - -#endif \ No newline at end of file diff --git a/exosphere/cpu_context.c b/exosphere/cpu_context.c deleted file mode 100644 index b80d26c..0000000 --- a/exosphere/cpu_context.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include "cpu_context.h" -#include "pmc.h" -#include "timers.h" -#include "utils.h" - -saved_cpu_context_t g_cpu_contexts[NUM_CPU_CORES] = {0}; - -void set_core_entrypoint_and_context_id(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id) { - g_cpu_contexts[core].ELR_EL3 = entrypoint_addr; - g_cpu_contexts[core].context_id = context_id; -} - -uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id) { - /* Is core valid? */ - if (core >= NUM_CPU_CORES) { - return 0xFFFFFFFE; - } - - /* Is core already on? */ - if (g_cpu_contexts[core].is_active) { - return 0xFFFFFFFC; - } - - set_core_entrypoint_and_context_id(core, entrypoint_addr, context_id); - - const uint32_t status_masks[NUM_CPU_CORES] = {0x4000, 0x200, 0x400, 0x800}; - const uint32_t toggle_vals[NUM_CPU_CORES] = {0xE, 0x9, 0xA, 0xB}; - - /* Check if we're already in the correct state. */ - if ((APBDEV_PMC_PWRGATE_STATUS_0 & status_masks[core]) != status_masks[core]) { - uint32_t counter = 5001; - - /* Poll the start bit until 0 */ - while (APBDEV_PMC_PWRGATE_TOGGLE_0 & 0x100) { - wait(1); - counter--; - if (counter < 1) { - return 0; - } - } - - /* Program PWRGATE_TOGGLE with the START bit set to 1, selecting CE[N] */ - APBDEV_PMC_PWRGATE_TOGGLE_0 = toggle_vals[core] | 0x100; - - /* Poll until we're in the correct state. */ - counter = 5001; - while (counter > 0) { - if ((APBDEV_PMC_PWRGATE_STATUS_0 & status_masks[core]) == status_masks[core]) { - break; - } - wait(1); - counter--; - } - } - return 0; -} - diff --git a/exosphere/cpu_context.h b/exosphere/cpu_context.h deleted file mode 100644 index dbf94d1..0000000 --- a/exosphere/cpu_context.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef EXOSPHERE_CPU_CTX_H -#define EXOSPHERE_CPU_CTX_H - -#include - -/* Exosphere CPU Management functionality. */ - -typedef struct { - uint64_t context_id; - uint64_t ELR_EL3; - int is_active; - int is_saved; - uint32_t OSDTRRX_EL1; - uint32_t OSDTRTX_EL1; - uint32_t MDSCR_EL1; - uint32_t OSECCR_EL1; - uint32_t MDCCINT_EL1; - uint32_t DBGCLAIMCLR_EL1; - uint32_t DBGVCR32_EL2; - uint32_t SDER32_EL3; - uint32_t MDCR_EL2; - uint32_t MDCR_EL3; - uint64_t DBGBVR0_EL1; - uint64_t DBGBCR0_EL1; - uint64_t DBGBVR1_EL1; - uint64_t DBGBCR1_EL1; - uint64_t DBGBVR2_EL1; - uint64_t DBGBCR2_EL1; - uint64_t DBGBVR3_EL1; - uint64_t DBGBCR3_EL1; - uint64_t DBGBVR4_EL1; - uint64_t DBGBCR4_EL1; - uint64_t DBGBVR5_EL1; - uint64_t DBGBCR5_EL1; - uint64_t DBGWVR0_EL1; - uint64_t DBGWCR0_EL1; - uint64_t DBGWVR1_EL1; - uint64_t DBGWCR1_EL1; - uint64_t DBGWVR2_EL1; - uint64_t DBGWCR2_EL1; - uint64_t DBGWVR3_EL1; - uint64_t DBGWCR3_EL1; -} saved_cpu_context_t; - -#define NUM_CPU_CORES 4 - -void set_core_entrypoint_and_context_id(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id); - -uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id); -uint32_t cpu_off(void); -uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint_addr, uint64_t context_id); - - -#endif \ No newline at end of file diff --git a/exosphere/exceptions.s b/exosphere/exceptions.s deleted file mode 100644 index 14ce6e3..0000000 --- a/exosphere/exceptions.s +++ /dev/null @@ -1,265 +0,0 @@ -/* 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 Exosphere. */ -.global exosphere_vectors -vector_base exosphere_vectors - -/* Current EL, SP0 */ -.global unknown_exception -unknown_exception: -vector_entry synch_sp0 - /* Panic with color FF7700, code 10. */ - mov x0, #0x10 - movk x0, #0x07F0,lsl#16 - b panic - check_vector_size synch_sp0 - -vector_entry irq_sp0 - b unknown_exception - check_vector irq_sp0 - -vector_entry fiq_sp0 - b unknown_exception - check_vector fiq_sp0 - -vector_entry serror_sp0 - b unknown_exception - check_vector serror_sp0 - -/* Current EL, SPx */ -vector_entry synch_spx - b unknown_exception - check_vector synch_spx - -vector_entry irq_spx - b unknown_exception - check_vector irq_spx - -vector_entry fiq_spx - b unknown_exception - check_vector fiq_spx - -vector_entry serror_spx - b unknown_exception - check_vector serror_spx - -/* Lower EL, A64 */ -vector_entry synch_a64 - stp x29, x30, [sp, #-0x10]! - /* Verify SMC. */ - mrs x30, esr_el3 - lsr w29, w30, #0x1A - cmp w29, #0x17 - ldp x29, x30, [sp],#0x10 - b.ne unknown_exception - /* Call appropriate handler. */ - stp x29, x30, [sp, #-0x10]! - mrs x29, mpidr_el1 - and x29, x39, #0x3 - cmp x29, #0x3 - b.ne handle_core012_smc_exception - bl handle_core3_smc_exception - ldp x29, x30, [sp],#0x10 - eret - check_vector synch_a64 - -vector_entry irq_a64 - b unknown_exception - check_vector irq_a64 - -vector_entry fiq_a64 - stp x29, x30, [sp, #-0x10]! - mrs x29, mpidr_el1 - and x29, x39, #0x3 - cmp x29, #0x3 - b.ne unknown_exception - stp x28, x29, [sp, #-0x10]! - stp x26, x27, [sp, #-0x10]! - bl handle_fiq_exception - ldp x26, x27, [sp],#0x10 - ldp x28, x29, [sp],#0x10 - ldp x29, x30, [sp],#0x10 - eret - check_vector fiq_a64 - -vector_entry serror_a64 - b unknown_exception - .endfunc - .cfi_endproc -/* To save space, insert in an unused vector segment. */ -.global handle_core012_smc_exception -.type handle_core012_smc_exception, %function -handle_core012_smc_exception: - stp x6, x7, [sp, #-0x10]! - stp x4, x5, [sp, #-0x10]! - stp x2, x3, [sp, #-0x10]! - stp x0, x1, [sp, #-0x10]! - bl set_priv_smc_in_progress - bl get_smc_core012_stack_address - mov x29, x0 - ldp x0, x1, [sp],#0x10 - ldp x2, x3, [sp],#0x10 - ldp x4, x5, [sp],#0x10 - ldp x6, x7, [sp],#0x10 - mov x30, sp - mov sp, x29 - stp x29, x30, [sp, #-0x10]! - bl handle_core3_smc_exception - ldp x29, x30, [sp],#0x10 - mov sp, x30 - stp x6, x7, [sp, #-0x10]! - stp x4, x5, [sp, #-0x10]! - stp x2, x3, [sp, #-0x10]! - stp x0, x1, [sp, #-0x10]! - bl clear_priv_smc_in_progress - ldp x0, x1, [sp],#0x10 - ldp x2, x3, [sp],#0x10 - ldp x4, x5, [sp],#0x10 - ldp x6, x7, [sp],#0x10 - ldp x29, x30, [sp],#0x10 - eret - .if (. - serror_a64) > (32 * 4) - .error "Vector exceeds 32 instructions" - .endif - -/* Lower EL, A32 */ -vector_entry synch_a32 - b unknown_exception - check_vector synch_a32 - -vector_entry irq_a32 - b unknown_exception - check_vector irq_a32 - -vector_entry fiq_a32 - b fiq_a64 - .endfunc - .cfi_endproc -/* To save space, insert in an unused vector segment. */ -.global handle_fiq_exception -.type handle_fiq_exception, %function -handle_fiq_exception: - 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]! - bl handle_registered_interrupt - 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 - .if (. - fiq_a32) > (32 * 4) - .error "Vector exceeds 32 instructions" - .endif - -vector_entry serror_a32 - b unknown_exception - .endfunc - .cfi_endproc -/* To save space, insert in an unused vector segment. */ -.global handle_core3_smc_exception -.type handle_core3_smc_exception, %function -handle_core3_smc_exception: - 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]! - mrs x0, esr_el3 - and x0, x0, #0xFFFF - mov x1, sp - bl call_smc_handler - 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 - ret - .if (. - serror_a32) > (32 * 4) - .error "Vector exceeds 32 instructions" - .endif diff --git a/exosphere/fuse.c b/exosphere/fuse.c deleted file mode 100644 index c034c14..0000000 --- a/exosphere/fuse.c +++ /dev/null @@ -1,231 +0,0 @@ -#include -#include -#include - -#include "fuse.h" -#include "utils.h" -#include "timers.h" - -/* Prototypes for internal commands. */ -void fuse_make_regs_visible(void); - -void fuse_enable_power(void); -void fuse_disable_power(void); -void fuse_wait_idle(void); - -/* Initialize the FUSE driver */ -void fuse_init(void) -{ - fuse_make_regs_visible(); - - /* TODO: Overrides (iROM patches) and various reads happen here */ -} - -/* Make all fuse registers visible */ -void fuse_make_regs_visible(void) -{ - /* TODO: Replace this with a proper CLKRST driver */ - uint32_t* misc_clk_reg = (volatile uint32_t *)mmio_get_device_address(MMIO_DEVID_CLKRST) + 0x48; - uint32_t misc_clk_val = *misc_clk_reg; - *misc_clk_reg = (misc_clk_val | (1 << 28)); -} - -/* Enable power to the fuse hardware array */ -void fuse_enable_power(void) -{ - FUSE_REGS->FUSE_PWR_GOOD_SW = 1; - wait(1); -} - -/* Disable power to the fuse hardware array */ -void fuse_disable_power(void) -{ - FUSE_REGS->FUSE_PWR_GOOD_SW = 0; - wait(1); -} - -/* Wait for the fuse driver to go idle */ -void fuse_wait_idle(void) -{ - uint32_t ctrl_val = 0; - - /* Wait for STATE_IDLE */ - while ((ctrl_val & (0xF0000)) != 0x40000) - { - wait(1); - ctrl_val = FUSE_REGS->FUSE_CTRL; - } -} - -/* Read a fuse from the hardware array */ -uint32_t fuse_hw_read(uint32_t addr) -{ - fuse_wait_idle(); - - /* Program the target address */ - FUSE_REGS->FUSE_REG_ADDR = addr; - - /* Enable read operation in control register */ - uint32_t ctrl_val = FUSE_REGS->FUSE_CTRL; - ctrl_val &= ~0x3; - ctrl_val |= 0x1; /* Set FUSE_READ command */ - FUSE_REGS->FUSE_CTRL = ctrl_val; - - fuse_wait_idle(); - - return FUSE_REGS->FUSE_REG_READ; -} - -/* Write a fuse in the hardware array */ -void fuse_hw_write(uint32_t, value, uint32_t addr) -{ - fuse_wait_idle(); - - /* Program the target address and value */ - FUSE_REGS->FUSE_REG_ADDR = addr; - FUSE_REGS->FUSE_REG_WRITE = value; - - /* Enable write operation in control register */ - uint32_t ctrl_val = FUSE_REGS->FUSE_CTRL; - ctrl_val &= ~0x3; - ctrl_val |= 0x2; /* Set FUSE_WRITE command */ - FUSE_REGS->FUSE_CTRL = ctrl_val; - - fuse_wait_idle(); -} - -/* Sense the fuse hardware array into the shadow cache */ -void fuse_hw_sense(void) -{ - fuse_wait_idle(); - - /* Enable sense operation in control register */ - uint32_t ctrl_val = FUSE_REGS->FUSE_CTRL; - ctrl_val &= ~0x3; - ctrl_val |= 0x3; /* Set FUSE_SENSE command */ - FUSE_REGS->FUSE_CTRL = ctrl_val; - - fuse_wait_idle(); -} - -/* Disables all fuse programming. */ -void fuse_disable_programming(void) { - FUSE_REGS->FUSE_DIS_PGM = 1; -} - -/* Unknown exactly what this does, but it alters the contents read from the fuse cache. */ -void fuse_secondary_private_key_disable(void) { - FUSE_REGS->FUSE_PRIVATEKEYDISABLE = 0x10; -} - - -/* Read the SKU info register from the shadow cache */ -uint32_t fuse_get_sku_info(void) -{ - return FUSE_CHIP_REGS->FUSE_SKU_INFO; -} - -/* Read the bootrom patch version from a register in the shadow cache */ -uint32_t fuse_get_bootrom_patch_version(void) -{ - return FUSE_CHIP_REGS->FUSE_SOC_SPEEDO_1; -} - -/* Read a spare bit register from the shadow cache */ -uint32_t fuse_get_spare_bit(uint32_t idx) -{ - uint32_t spare_bit_val = 0; - - if ((idx >= 0) && (idx < 32)) - spare_bit_val = FUSE_CHIP_REGS->FUSE_SPARE_BIT[idx]; - - return spare_bit_val; -} - -/* Read a reserved ODM register from the shadow cache */ -uint32_t fuse_get_reserved_odm(uint32_t idx) -{ - uint32_t reserved_odm_val = 0; - - if ((idx >= 0) && (idx < 8)) - reserved_odm_val = FUSE_CHIP_REGS->FUSE_RESERVED_ODM[idx]; - - return reserved_odm_val; -} - -/* Derive the Device ID using values in the shadow cache */ -uint64_t fuse_get_device_id(void) { - uint64_t device_id = 0; - uint64_t y_coord = FUSE_CHIP_REGS->FUSE_Y_COORDINATE & 0x1FF; - uint64_t x_coord = FUSE_CHIP_REGS->FUSE_X_COORDINATE & 0x1FF; - uint64_t wafer_id = FUSE_CHIP_REGS->FUSE_WAFER_ID & 0x3F; - uint32_t lot_code = FUSE_CHIP_REGS->FUSE_LOT_CODE_0; - uint64_t fab_code = FUSE_CHIP_REGS->FUSE_FAB_CODE & 0x3F; - uint64_t derived_lot_code = 0; - for (unsigned int i = 0; i < 5; i++) { - derived_lot_code = (derived_lot_code * 0x24) + ((lot_code >> (24 - 6*i)) & 0x3F); - } - derived_lot_code &= 0x03FFFFFF; - - device_id |= y_coord << 0; - device_id |= x_coord << 9; - device_id |= wafer_id << 18; - device_id |= derived_lot_code << 24; - device_id |= fab_code << 50; - return device_id; -} - -/* Get the DRAM ID using values in the shadow cache */ -uint32_t fuse_get_dram_id(void) { - return (FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 3) & 0x7; -} - -/* Derive the Hardware Type using values in the shadow cache */ -uint32_t fuse_get_hardware_type(void) { - uint32_t hardware_type = ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 7) & 2) | ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 2) & 1); - if (hardware_type) { - if (hardware_type == 1) { - return 0; - } - if (hardware_type == 2) { - return 1; - } - } else if ((FUSE_CHIP_REGS->FUSE_SPARE_BIT[9] & 1) == 0) { - return 0; - } - return 3; -} - -/* Derive the Retail Type using values in the shadow cache */ -uint32_t fuse_get_retail_type(void) { - /* Retail type = IS_RETAIL | UNIT_TYPE */ - uint32_t retail_type = ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 7) & 4) | (FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] & 3); - if (retail_type == 4) { /* Standard retail unit, IS_RETAIL | 0. */ - return 1; - } else if (retail_type == 3) { /* Standard dev unit, 0 | DEV_UNIT. */ - return 0; - } - return 2; /* IS_RETAIL | DEV_UNIT */ -} - -/* Derive the 16-byte Hardware Info using values in the shadow cache, and copy to output buffer. */ -void fuse_get_hardware_info(void *dst) { - uint32_t hw_info[0x4]; - - uint32_t unk_hw_fuse = FUSE_CHIP_REGS->_0x120 & 0x3F; - uint32_t y_coord = FUSE_CHIP_REGS->FUSE_Y_COORDINATE & 0x1FF; - uint32_t x_coord = FUSE_CHIP_REGS->FUSE_X_COORDINATE & 0x1FF; - uint32_t wafer_id = FUSE_CHIP_REGS->FUSE_WAFER_ID & 0x3F; - uint32_t lot_code_0 = FUSE_CHIP_REGS->FUSE_LOT_CODE_0; - uint32_t lot_code_1 = FUSE_CHIP_REGS->FUSE_LOT_CODE_1 & 0x0FFFFFFF; - uint32_t fab_code = FUSE_CHIP_REGS->FUSE_FAB_CODE & 0x3F; - uint32_t vendor_code = FUSE_CHIP_REGS->FUSE_VENDOR_CODE & 0xF; - - /* Hardware Info = unk_hw_fuse || Y_COORD || X_COORD || WAFER_ID || LOT_CODE || FAB_CODE || VENDOR_ID */ - hw_info[0] = (uint32_t)((lot_code_1 << 30) | (wafer_id << 24) | (x_coord << 15) | (y_coord << 6) | (unk_hw_fuse)); - hw_info[1] = (uint32_t)((lot_code_0 << 26) | (lot_code_1 >> 2)); - hw_info[2] = (uint32_t)((fab_code << 26) | (lot_code_0 >> 6)); - hw_info[3] = (uint32_t)(vendor_code); - - memcpy(dst, hw_info, 0x10); -} \ No newline at end of file diff --git a/exosphere/fuse.h b/exosphere/fuse.h deleted file mode 100644 index 9a68725..0000000 --- a/exosphere/fuse.h +++ /dev/null @@ -1,193 +0,0 @@ -#ifndef EXOSPHERE_FUSE_H -#define EXOSPHERE_FUSE_H - -#include -#include -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 FUSE registers. */ - -typedef struct { - uint32_t FUSE_CTRL; - uint32_t FUSE_REG_ADDR; - uint32_t FUSE_REG_READ; - uint32_t FUSE_REG_WRITE; - uint32_t FUSE_TIME_RD1; - uint32_t FUSE_TIME_RD2; - uint32_t FUSE_TIME_PGM1; - uint32_t FUSE_TIME_PGM2; - uint32_t FUSE_PRIV2INTFC; - uint32_t FUSE_FUSEBYPASS; - uint32_t FUSE_PRIVATEKEYDISABLE; - uint32_t FUSE_DIS_PGM; - uint32_t FUSE_WRITE_ACCESS; - uint32_t FUSE_PWR_GOOD_SW; - uint32_t _0x38[0x32]; -} fuse_registers_t; - -typedef struct { - uint32_t FUSE_PRODUCTION_MODE; - uint32_t _0x4; - uint32_t _0x8; - uint32_t _0xC; - uint32_t FUSE_SKU_INFO; - uint32_t FUSE_CPU_SPEEDO_0; - uint32_t FUSE_CPU_IDDQ; - uint32_t _0x1C; - uint32_t _0x20; - uint32_t _0x24; - uint32_t FUSE_FT_REV; - uint32_t FUSE_CPU_SPEEDO_1; - uint32_t FUSE_CPU_SPEEDO_2; - uint32_t FUSE_SOC_SPEEDO_0; - uint32_t FUSE_SOC_SPEEDO_1; - uint32_t FUSE_SOC_SPEEDO_2; - uint32_t FUSE_SOC_IDDQ; - uint32_t _0x44; - uint32_t FUSE_FA; - uint32_t _0x4C; - uint32_t _0x50; - uint32_t _0x54; - uint32_t _0x58; - uint32_t _0x5C; - uint32_t _0x60; - uint32_t FUSE_PUBLIC_KEY[0x8]; - uint32_t FUSE_TSENSOR_1; - uint32_t FUSE_TSENSOR_2; - uint32_t _0x8C; - uint32_t FUSE_CP_REV; - uint32_t _0x94; - uint32_t FUSE_TSENSOR_0; - uint32_t FUSE_FIRST_BOOTROM_PATCH_SIZE_REG; - uint32_t FUSE_SECURITY_MODE; - uint32_t FUSE_PRIVATE_KEY[0x4]; - uint32_t FUSE_DEVICE_KEY; - uint32_t _0xB8; - uint32_t _0xBC; - uint32_t FUSE_RESERVED_SW; - uint32_t FUSE_VP8_ENABLE; - uint32_t FUSE_RESERVED_ODM[0x8]; - uint32_t _0xE8; - uint32_t _0xEC; - uint32_t FUSE_SKU_USB_CALIB; - uint32_t FUSE_SKU_DIRECT_CONFIG; - uint32_t _0xF8; - uint32_t _0xFC; - uint32_t FUSE_VENDOR_CODE; - uint32_t FUSE_FAB_CODE; - uint32_t FUSE_LOT_CODE_0; - uint32_t FUSE_LOT_CODE_1; - uint32_t FUSE_WAFER_ID; - uint32_t FUSE_X_COORDINATE; - uint32_t FUSE_Y_COORDINATE; - uint32_t _0x11C; - uint32_t _0x120; - uint32_t FUSE_SATA_CALIB; - uint32_t FUSE_GPU_IDDQ; - uint32_t FUSE_TSENSOR_3; - uint32_t _0x130; - uint32_t _0x134; - uint32_t _0x138; - uint32_t _0x13C; - uint32_t _0x140; - uint32_t _0x144; - uint32_t FUSE_OPT_SUBREVISION; - uint32_t _0x14C; - uint32_t _0x150; - uint32_t FUSE_TSENSOR_4; - uint32_t FUSE_TSENSOR_5; - uint32_t FUSE_TSENSOR_6; - uint32_t FUSE_TSENSOR_7; - uint32_t FUSE_OPT_PRIV_SEC_DIS; - uint32_t FUSE_PKC_DISABLE; - uint32_t _0x16C; - uint32_t _0x170; - uint32_t _0x174; - uint32_t _0x178; - uint32_t _0x17C; - uint32_t FUSE_TSENSOR_COMMON; - uint32_t _0x184; - uint32_t _0x188; - uint32_t _0x18C; - uint32_t _0x190; - uint32_t _0x194; - uint32_t _0x198; - uint32_t FUSE_DEBUG_AUTH_OVERRIDE; - uint32_t _0x1A0; - uint32_t _0x1A4; - uint32_t _0x1A8; - uint32_t _0x1AC; - uint32_t _0x1B0; - uint32_t _0x1B4; - uint32_t _0x1B8; - uint32_t _0x1BC; - uint32_t _0x1D0; - uint32_t FUSE_TSENSOR_8; - uint32_t _0x1D8; - uint32_t _0x1DC; - uint32_t _0x1E0; - uint32_t _0x1E4; - uint32_t _0x1E8; - uint32_t _0x1EC; - uint32_t _0x1F0; - uint32_t _0x1F4; - uint32_t _0x1F8; - uint32_t _0x1FC; - uint32_t _0x200; - uint32_t FUSE_RESERVED_CALIB; - uint32_t _0x208; - uint32_t _0x20C; - uint32_t _0x210; - uint32_t _0x214; - uint32_t _0x218; - uint32_t FUSE_TSENSOR_9; - uint32_t _0x220; - uint32_t _0x224; - uint32_t _0x228; - uint32_t _0x22C; - uint32_t _0x230; - uint32_t _0x234; - uint32_t _0x238; - uint32_t _0x23C; - uint32_t _0x240; - uint32_t _0x244; - uint32_t _0x248; - uint32_t _0x24C; - uint32_t FUSE_USB_CALIB_EXT; - uint32_t _0x254; - uint32_t _0x258; - uint32_t _0x25C; - uint32_t _0x260; - uint32_t _0x264; - uint32_t _0x268; - uint32_t _0x26C; - uint32_t _0x270; - uint32_t _0x274; - uint32_t _0x278; - uint32_t _0x27C; - uint32_t FUSE_SPARE_BIT[0x20]; -} fuse_chip_registers_t; - -#define FUSE_REGS ((volatile fuse_registers_t *)(mmio_get_device_address(MMIO_DEVID_FUSE) + 0x800)) -#define FUSE_CHIP_REGS ((volatile fuse_chip_registers_t *)(mmio_get_device_address(MMIO_DEVID_FUSE) + 0x900)) - -void fuse_init(void); - -uint32_t fuse_hw_read(uint32_t addr); -void fuse_hw_write(uint32_t, value, uint32_t addr); -void fuse_hw_sense(void); -void fuse_disable_programming(void); -void fuse_secondary_private_key_disable(void); - -uint32_t fuse_get_sku_info(void); -uint32_t fuse_get_spare_bit(uint32_t idx); -uint32_t fuse_get_reserved_odm(uint32_t idx); - -uint32_t fuse_get_bootrom_patch_version(void); -uint64_t fuse_get_device_id(void); -uint32_t fuse_get_dram_id(void); -uint32_t fuse_get_hardware_type(void); -uint32_t fuse_get_retail_type(void); -void fuse_get_hardware_info(void *dst); - -#endif diff --git a/exosphere/gcm.c b/exosphere/gcm.c deleted file mode 100644 index f09ae2d..0000000 --- a/exosphere/gcm.c +++ /dev/null @@ -1,167 +0,0 @@ -#include -#include - -#include "utils.h" -#include "fuse.h" -#include "gcm.h" - -#include "sealedkeys.h" -#include "se.h" - -/* Shifts right a little endian 128-bit value. */ -static void shr_128(uint64_t *val) { - val[0] >>= 1; - val[0] |= (val[1] & 1) << 63; - val[1] >>= 1; -} - -/* Shifts left a little endian 128-bit value. */ -static void shl_128(uint64_t *val) { - val[1] <<= 1; - val[1] |= (val[0] & (1ULL << 63)) >> 63; - val[0] <<= 1; -} - - -/* Multiplies two 128-bit numbers X,Y in the GF(128) Galois Field. */ -static void gf128_mul(uint8_t *dst, const uint8_t *x, const uint8_t *y) { - uint8_t x_work[0x10]; - uint8_t y_work[0x10]; - uint8_t dst_work[0x10]; - - uint64_t *p_x = (uint64_t *)(&x_work[0]); - uint64_t *p_y = (uint64_t *)(&y_work[0]); - uint64_t *p_dst = (uint64_t *)(&dst_work[0]); - - /* Initialize buffers. */ - for (unsigned int i = 0; i < 0x10; i++) { - x_work[i] = x[0xF-i]; - y_work[i] = y[0xF-i]; - dst_work[i] = 0; - } - - /* Perform operation for each bit in y. */ - for (unsigned int round = 0; round < 0x80; round++) { - p_dst[0] ^= p_x[0] * ((y_work[0xF] & 0x80) >> 7); - p_dst[1] ^= p_x[1] * ((y_work[0xF] & 0x80) >> 7); - shl_128(p_y); - uint8_t xval = 0xE1 * (x_work[0] & 1); - shr_128(p_x); - x_work[0xF] ^= xval; - } - - for (unsigned int i = 0; i < 0x10; i++) { - dst[i] = dst_work[0xF-i]; - } -} - - - -/* Performs an AES-GCM GHASH operation over the data into dst. */ -static void ghash(void *dst, const void *data, size_t data_size, const void *j_block, bool encrypt) { - uint8_t x[0x10]; - uint8_t h[0x10]; - - uint64_t *p_x = (uint64_t *)(&x[0]); - uint64_t *p_data = (uint64_t *)data; - - memset(x, 0, 0x10); - - /* H = aes_ecb_encrypt(zeroes) */ - se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, h, 0x10, x, 0x10); - - size_t total_size = data_size; - - while (data_size >= 0x10) { - /* X = (X ^ current_block) * H */ - p_x[0] ^= p_data[0]; - p_x[1] ^= p_data[1]; - - gf128_mul(x, x, h); - - /* Increment p_data by 0x10 bytes. */ - p_data += 2; - data_size -= 0x10; - } - - /* Nintendo's code *discards all data in the last block* if unaligned. */ - /* And treats that block as though it were all-zero. */ - /* This is a bug, they just forget to XOR with the copy of the last block they save. */ - if (data_size & 0xF) { - gf128_mul(x, x, h); - } - - /* Due to a Nintendo bug, the wrong QWORD gets XOR'd in the "final output block" case. */ - if (encrypt) { - p_x[1] ^= (uint64_t)(total_size << 3); - } else { - p_x[0] ^= (uint64_t)(total_size << 3); - } - - gf128_mul(x, x, h); - - /* If final output block, XOR with encrypted J block. */ - if (encrypt) { - se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, h, 0x10, j_block, 0x10); - for (unsigned int i = 0; i < 0x10; i++) { - x[i] ^= h[i]; - } - } - - /* Copy output. */ - memcpy(dst, x, 0x10); -} - - -/* This function is a doozy. It decrypts and validates a (non-standard) AES-GCM wrapped keypair. */ -size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_size, const void *sealed_kek, size_t kek_size, const void *wrapped_key, size_t key_size, unsigned int usecase, bool is_personalized) { - if (is_personalized == 0) { - /* Devkit keys use a different keyformat without a MAC/Device ID. */ - if (src_size <= 0x10 || src_size - 0x10 > dst_size) { - generic_panic(); - } - } else { - if (src_size <= 0x30 || src_size - 0x20 > dst_size) { - generic_panic(); - } - } - - /* Unwrap the key */ - unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, kek_size, usecase); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, key_size); - - /* Decrypt the GCM keypair, AES-CTR with CTR = blob[:0x10]. */ - se_aes_ctr_crypt(KEYSLOT_SWITCH_TEMPKEY, dst, dst_size, src + 0x10, src_size - 0x10, src, 0x10); - - - if (!is_personalized) { - /* Devkit non-personalized keys have no further authentication. */ - return src_size - 0x10; - } - - /* J = GHASH(CTR); */ - uint8_t j_block[0x10]; - ghash(j_block, src, 0x10, NULL, false); - - /* MAC = GHASH(PLAINTEXT) ^ ENCRYPT(J) */ - /* Note: That MAC is calculated over plaintext is non-standard. */ - /* It is supposed to be over the ciphertext. */ - uint8_t calc_mac[0x10]; - ghash(calc_mac, dst, src_size - 0x20, j_block, true); - - /* Const-time memcmp. */ - const uint8_t *src_bytes = src; - int different = 0; - for (unsigned int i = 0; i < 0x10; i++) { - different |= src_bytes[src_size - 0x10 + i] ^ calc_mac[i]; - } - if (different) { - return 0; - } - - if (read64le(src_bytes, src_size - 0x28) != fuse_get_device_id()) { - return 0; - } - - return src_size - 0x30; -} diff --git a/exosphere/gcm.h b/exosphere/gcm.h deleted file mode 100644 index 15caa11..0000000 --- a/exosphere/gcm.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef EXOSPHERE_GCM_H -#define EXOSPHERE_GCM_H - -#include -#include - -size_t gcm_decrypt_key(void *dst, size_t dst_size, - const void *src, size_t src_size, - const void *sealed_kek, size_t kek_size, - const void *wrapped_key, size_t key_size, - unsigned int usecase, bool is_personalized); - -#endif diff --git a/exosphere/i2c.c b/exosphere/i2c.c deleted file mode 100644 index 72565db..0000000 --- a/exosphere/i2c.c +++ /dev/null @@ -1,203 +0,0 @@ -#include -#include -#include - -#include "i2c.h" -#include "utils.h" -#include "timers.h" - -/* Prototypes for internal commands. */ -volatile i2c_registers_t *i2c_get_registers_from_id(unsigned int id); -void i2c_load_config(volatile i2c_registers_t *regs); - -bool i2c_query(unsigned int id, uint8_t device, uint8_t r, void *dst, size_t dst_size); -bool i2c_send(unsigned int id, uint8_t device, uint8_t r, void *src, size_t src_size); - -bool i2c_write(volatile i2c_registers_t *regs, uint8_t device, void *src, size_t src_size); -bool i2c_read(volatile i2c_registers_t *regs, uint8_t device, void *dst, size_t dst_size); - -/* Initialize I2C based on registers. */ -void i2c_init(unsigned int id) { - volatile i2c_registers_t *regs = i2c_get_registers_from_id(id); - - /* Setup divisor, and clear the bus. */ - regs->I2C_I2C_CLK_DIVISOR_REGISTER_0 = 0x50001; - regs->I2C_I2C_BUS_CLEAR_CONFIG_0 = 0x90003; - - /* Load hardware configuration. */ - i2c_load_config(regs); - - /* Wait a while until BUS_CLEAR_DONE is set. */ - for (unsigned int i = 0; i < 10; i++) { - wait(20000); - if (regs->I2C_INTERRUPT_STATUS_REGISTER_0 & 0x800) { - break; - } - } - - /* Read the BUS_CLEAR_STATUS. Result doesn't matter. */ - regs->I2C_I2C_BUS_CLEAR_STATUS_0; - - /* Read and set the Interrupt Status. */ - uint32_t int_status = regs->I2C_INTERRUPT_STATUS_REGISTER_0; - regs->I2C_INTERRUPT_STATUS_REGISTER_0 = int_status; -} - -/* Sets a bit in a PMIC register over I2C during CPU shutdown. */ -void i2c_send_pmic_cpu_shutdown_cmd(void) { - uint32_t val = 0; - /* PMIC == Device 4:3C. */ - i2c_query(4, 0x3C, 0x41, &val, 1); - val |= 4; - i2c_send(4, 0x3C, 0x41, &val, 1); -} - -/* Queries the value of TI charger bit over I2C. */ -bool i2c_query_ti_charger_bit_7(void) { - uint32_t val = 0; - /* TI Charger = Device 0:6B. */ - i2c_query(0, 0x6B, 0, &val, 1); - return (val & 0x80) != 0; -} - -/* Clears TI charger bit over I2C. */ -void i2c_clear_ti_charger_bit_7(void) { - uint32_t val = 0; - /* TI Charger = Device 0:6B. */ - i2c_query(0, 0x6B, 0, &val, 1); - val &= 0x7F; - i2c_send(0, 0x6B, 0, &val, 1); -} - -/* Sets TI charger bit over I2C. */ -void i2c_set_ti_charger_bit_7(void) { - uint32_t val = 0; - /* TI Charger = Device 0:6B. */ - i2c_query(0, 0x6B, 0, &val, 1); - val |= 0x80; - i2c_send(0, 0x6B, 0, &val, 1); -} - -/* Get registers pointer based on I2C ID. */ -volatile i2c_registers_t *i2c_get_registers_from_id(unsigned int id) { - switch (id) { - case 0: - return I2C1_REGS; - case 1: - return I2C2_REGS; - case 2: - return I2C3_REGS; - case 3: - return I2C4_REGS; - case 4: - return I2C5_REGS; - case 5: - return I2C6_REGS; - default: - generic_panic(); - } - return NULL; -} - -/* Load hardware config for I2C4. */ -void i2c_load_config(volatile i2c_registers_t *regs) { - /* Set MSTR_CONFIG_LOAD, TIMEOUT_CONFIG_LOAD, undocumented bit. */ - regs->I2C_I2C_CONFIG_LOAD_0 = 0x25; - - /* Wait a bit for master config to be loaded. */ - for (unsigned int i = 0; i < 20; i++) { - wait(1); - if (!(regs->I2C_I2C_CONFIG_LOAD_0 & 1)) { - break; - } - } -} - -/* Reads a register from a device over I2C, writes result to output. */ -bool i2c_query(unsigned int id, uint8_t device, uint8_t r, void *dst, size_t dst_size) { - volatile i2c_registers_t *regs = i2c_get_registers_from_id(id); - uint32_t val = r; - - /* Write single byte register ID to device. */ - if (!i2c_write(regs, device, &val, 1)) { - return false; - } - /* Limit output size to 32-bits. */ - if (dst_size > 4) { - return false; - } - - return i2c_read(regs, device, dst, dst_size); -} - -/* Writes a value to a register over I2C. */ -bool i2c_send(unsigned int id, uint8_t device, uint8_t r, void *src, size_t src_size) { - uint32_t val = r; - if (src_size <= 3) { - memcpy(((uint8_t *)&val) + 1, src, src_size); - return i2c_write(i2c_get_registers_from_id(id), &val, src_size + 1, device); - } else { - return false; - } -} - -/* Writes bytes to device over I2C. */ -bool i2c_write(volatile i2c_registers_t *regs, uint8_t device, void *src, size_t src_size) { - if (src_size > 4) { - return false; - } - - /* Set device for 7-bit write mode. */ - regs->I2C_I2C_CMD_ADDR0_0 = device << 1; - - /* Load in data to write. */ - regs->I2C_I2C_CMD_DATA1_0 = read32le(src, 0); - - /* Set config with LENGTH = src_size, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */ - regs->I2C_I2C_CNFG_0 = ((src_size << 1) - 2) | 0x2800; - - i2c_load_config(regs); - - /* Config |= SEND; */ - regs->I2C_I2C_CNFG_0 |= 0x200; - - - while (regs->I2C_I2C_STATUS_0 & 0x100) { - /* Wait until not busy. */ - } - - /* Return CMD1_STAT == SL1_XFER_SUCCESSFUL. */ - return (regs->I2C_I2C_STATUS_0 & 0xF) == 0; -} - -/* Reads bytes from device over I2C. */ -bool i2c_read(volatile i2c_registers_t *regs, uint8_t device, void *dst, size_t dst_size) { - if (dst_size > 4) { - return false; - } - - /* Set device for 7-bit read mode. */ - regs->I2C_I2C_CMD_ADDR0_0 = (device << 1) | 1; - - /* Set config with LENGTH = dst_size, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */ - regs->I2C_I2C_CNFG_0 = ((dst_size << 1) - 2) | 0x2840; - - i2c_load_config(regs); - - /* Config |= SEND; */ - regs->I2C_I2C_CNFG_0 |= 0x200; - - - while (regs->I2C_I2C_STATUS_0 & 0x100) { - /* Wait until not busy. */ - } - - /* Ensure success. */ - if ((regs->I2C_I2C_STATUS_0 & 0xF) != 0) { - return false; - } - - uint32_t val = regs->I2C_I2C_CMD_DATA1_0; - memcpy(dst, &val, dst_size); - return true; -} diff --git a/exosphere/i2c.h b/exosphere/i2c.h deleted file mode 100644 index 1890b59..0000000 --- a/exosphere/i2c.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef EXOSPHERE_I2C_H -#define EXOSPHERE_I2C_H - -#include -#include -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 I2C registers. */ - -typedef struct { - uint32_t I2C_I2C_CNFG_0; - uint32_t I2C_I2C_CMD_ADDR0_0; - uint32_t I2C_I2C_CMD_ADDR1_0; - uint32_t I2C_I2C_CMD_DATA1_0; - uint32_t I2C_I2C_CMD_DATA2_0; - uint32_t _0x14; - uint32_t _0x18; - uint32_t I2C_I2C_STATUS_0; - uint32_t I2C_I2C_SL_CNFG_0; - uint32_t I2C_I2C_SL_RCVD_0; - uint32_t I2C_I2C_SL_STATUS_0; - uint32_t I2C_I2C_SL_ADDR1_0; - uint32_t I2C_I2C_SL_ADDR2_0; - uint32_t I2C_I2C_TLOW_SEXT_0; - uint32_t _0x38; - uint32_t I2C_I2C_SL_DELAY_COUNT_0; - uint32_t I2C_I2C_SL_INT_MASK_0; - uint32_t I2C_I2C_SL_INT_SOURCE_0; - uint32_t I2C_I2C_SL_INT_SET_0; - uint32_t _0x4C; - uint32_t I2C_I2C_TX_PACKET_FIFO_0; - uint32_t I2C_I2C_RX_FIFO_0; - uint32_t I2C_PACKET_TRANSFER_STATUS_0; - uint32_t I2C_FIFO_CONTROL_0; - uint32_t I2C_FIFO_STATUS_0; - uint32_t I2C_INTERRUPT_MASK_REGISTER_0; - uint32_t I2C_INTERRUPT_STATUS_REGISTER_0; - uint32_t I2C_I2C_CLK_DIVISOR_REGISTER_0; - uint32_t I2C_I2C_INTERRUPT_SOURCE_REGISTER_0; - uint32_t I2C_I2C_INTERRUPT_SET_REGISTER_0; - uint32_t I2C_I2C_SLV_TX_PACKET_FIFO_0; - uint32_t I2C_I2C_SLV_RX_FIFO_0; - uint32_t I2C_I2C_SLV_PACKET_STATUS_0; - uint32_t I2C_I2C_BUS_CLEAR_CONFIG_0; - uint32_t I2C_I2C_BUS_CLEAR_STATUS_0; - uint32_t I2C_I2C_CONFIG_LOAD_0; - uint32_t _0x90; - uint32_t I2C_I2C_INTERFACE_TIMING_0_0; - uint32_t I2C_I2C_INTERFACE_TIMING_1_0; - uint32_t I2C_I2C_HS_INTERFACE_TIMING_0_0; - uint32_t I2C_I2C_HS_INTERFACE_TIMING_1_0; -} i2c_registers_t; - - -#define I2C1_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_DTV_I2C234) + 0x000)) -#define I2C2_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_DTV_I2C234) + 0x400)) -#define I2C3_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_DTV_I2C234) + 0x500)) -#define I2C4_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_DTV_I2C234) + 0x700)) -#define I2C5_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_I2C56_SPI2B) + 0x000)) -#define I2C6_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_I2C56_SPI2B) + 0x100)) - -void i2c_init(unsigned int id); - -void i2c_send_pmic_cpu_shutdown_cmd(void); - -bool i2c_query_ti_charger_bit_7(void); -void i2c_clear_ti_charger_bit_7(void); -void i2c_set_ti_charger_bit_7(void); - -#endif diff --git a/exosphere/interrupt.c b/exosphere/interrupt.c deleted file mode 100644 index 2bec984..0000000 --- a/exosphere/interrupt.c +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include - -#include "utils.h" -#include "interrupt.h" - -/* Global of registered handlers. */ -struct { - unsigned int id; - void (*handler)(void); -} g_registered_interrupts[MAX_REGISTERED_INTERRUPTS] = { {0, NULL}, {0, NULL}, {0, NULL}, {0, NULL} }; - -/* Prototypes for internal (private) functions. */ -unsigned int get_interrupt_id(void); - - -/* Initializes the GIC. TODO: This must be called during wakeup. */ -void intr_initialize_gic(void) { - /* Setup interrupts 0-0x1F as nonsecure with highest non-secure priority. */ - GICD_IGROUPR[0] = 0xFFFFFFFF; - for (unsigned int i = 0; i < 0x20; i++) { - GICD_IPRIORITYR[i] = GIC_PRI_HIGHEST; - } - - /* Setup the GICC. */ - GICC_CTLR = 0x1D9; - GICC_PMR = GIC_PRI_HIGHEST; - GICC_BPR = 7; -} - -/* Sets an interrupt's group in the GICD. */ -void intr_set_group(unsigned int id, int group) { - GICD_IGROUPR[id >> 5] = (GICD_IGROUPR[id >> 5] & (~(1 << (id & 0x1F)))) | ((group & 1) << (id & 0x1F)); -} - -/* Sets an interrupt id as pending in the GICD. */ -void intr_set_pending(unsigned int id) { - GICD_ISPENDR[id >> 5] = 1 << (id & 0x1F); -} - -/* Sets an interrupt's priority in the GICD. */ -void intr_set_priority(unsigned int id, uint8_t priority) { - GICD_IPRIORITYR[id] = priority; -} - -/* Sets an interrupt's target CPU mask in the GICD. */ -void intr_set_cpu_mask(unsigned int id, uint8_t mask) { - GICD_ITARGETSR[id] = mask; -} - -/* Sets an interrupt's edge/level bits in the GICD. */ -void intr_set_edge_level(unsigned int id, int edge_level) { - GICD_ICFGR[id >> 4] = (GICD_ICFGR[id >> 4] & (~(3 << ((id & 0xF) << 1))) | (((edge_level & 1) << 1) << ((id & 0xF) << 1)); -} - -/* Sets an interrupt's enabled status in the GICD. */ -void intr_set_enabled(unsigned int id, int enabled) { - GICD_ISENABLER[id >> 5] = (enabled & 1) << (id & 0x1F); -} - -/* To be called by FIQ handler. */ -void handle_registered_interrupt(void) { - unsigned int interrupt_id = get_interrupt_id(); - if (interrupt_id <= 0xDF) { - bool found_handler = false; - for (unsigned int i = 0; i < MAX_REGISTERED_INTERRUPTS; i++) { - if (g_registered_interrupts[i].id == interrupt_id) { - found_handler = true; - g_registered_interrupts[i].handler(); - /* Mark that interrupt is done. */ - GICC_EOIR = interrupt_id; - break; - } - } - /* We must have found a handler, or something went wrong. */ - if (!found_handler) { - generic_panic(); - } - } -} - -/* Registers an interrupt into the global. */ -void intr_register_handler(unsigned int id, void (*handler)(void)) { - bool registered_handler = false; - for (unsigned int i = 0; i < MAX_REGISTERED_INTERRUPTS; i++) { - if (g_registered_interrupts[i].id == 0) { - g_registered_interrupts[i].handler = handler; - g_registered_interrupts[i].id = id; - registered_handler = true; - break; - } - } - /* Failure to register is an error condition. */ - if (!registered_handler) { - generic_panic(); - } -} - diff --git a/exosphere/interrupt.h b/exosphere/interrupt.h deleted file mode 100644 index f46d778..0000000 --- a/exosphere/interrupt.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef EXOSPHERE_INTERRUPT_H -#define EXOSPHERE_INTERRUPT_H - -#include -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 GIC-400 registers. */ - - -#define MAX_REGISTERED_INTERRUPTS 4 -#define INTERRUPT_ID_SECURITY_ENGINE 0x5A -#define INTERRUPT_ID_USER_SECURITY_ENGINE 0x2C - -#define GICD_BASE (mmio_get_device_address(MMIO_DEVID_GICD)) -#define GICC_BASE (mmio_get_device_address(MMIO_DEVID_GICC)) - -#define GICD_IGROUPR ((volatile uint32_t *)(GICD_BASE + 0x080ULL)) -#define GICD_ISENABLER ((volatile uint32_t *)(GICD_BASE + 0x100ULL)) -#define GICD_ISPENDR ((volatile uint32_t *)(GICD_BASE + 0x200ULL)) -#define GICD_IPRIORITYR ((volatile uint8_t *)(GICD_BASE + 0x400ULL)) -#define GICD_ITARGETSR ((volatile uint8_t *)(GICD_BASE + 0x800ULL)) -#define GICD_ICFGR ((volatile uint32_t *)(GICD_BASE + 0xC00ULL)) - -#define GICC_CTLR (*((volatile uint32_t *)(GICC_BASE + 0x0000ULL))) -#define GICC_PMR (*((volatile uint32_t *)(GICC_BASE + 0x0004ULL))) -#define GICC_BPR (*((volatile uint32_t *)(GICC_BASE + 0x0008ULL))) -#define GICC_IAR (*((volatile uint32_t *)(GICC_BASE + 0x000CULL))) -#define GICC_EOIR (*((volatile uint32_t *)(GICC_BASE + 0x0010ULL))) - -#define GIC_PRI_HIGHEST_SECURE 0x00 -#define GIC_PRI_HIGHEST_NONSECURE 0x80 - -#define GIC_GROUP_SECURE 0 -#define GIC_GROUP_NONSECURE 1 - -/* To be called by FIQ handler. */ -void handle_registered_interrupt(void); - -/* Initializes the GIC. TODO: This must be called during wakeup. */ -void intr_initialize_gic(void); - - -void intr_register_handler(unsigned int id, void (*handler)(void)); -void intr_set_group(unsigned int id, int group); -void intr_set_pending(unsigned int id); -void intr_set_priority(unsigned int id, uint8_t priority); -void intr_set_cpu_mask(unsigned int id, uint8_t mask); -void intr_set_edge_level(unsigned int id, int edge_level); -void intr_set_enabled(unsigned int id, int enabled); -#endif diff --git a/exosphere/linker.ld b/exosphere/linker.ld new file mode 100644 index 0000000..af74fd1 --- /dev/null +++ b/exosphere/linker.ld @@ -0,0 +1,16 @@ +ENTRY(__start_cold) +SECTIONS +{ + /* DUMMY LINKER SCRIPT, REPLACE ASAP */ + . = 0xF0000000; + + __start__ = ABSOLUTE(.); + + .text : ALIGN(4) { *(.text*); . = ALIGN(4); } + .rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); } + .data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); } + + . = ALIGN(4); + + __end__ = ABSOLUTE(.); +} diff --git a/exosphere/lock.h b/exosphere/lock.h deleted file mode 100644 index 5a4ca4a..0000000 --- a/exosphere/lock.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef EXOSPHERE_LOCK_H -#define EXOSPHERE_LOCK_H - -#include - -/* Simple atomics driver for Exosphere. */ - -/* Acquire a lock. */ -static inline void lock_acquire(bool *l) { - while (__atomic_test_and_set(l, __ATOMIC_ACQUIRE)) { - /* Wait to acquire lock. */ - } -} - -/* Release a lock. */ -static inline void lock_release(bool *l) { - __atomic_clear(l, __ATOMIC_RELEASE); -} - -/* Try to acquire a lock. */ -static inline bool lock_try_acquire(bool *l) { - return __atomic_test_and_set(l, __ATOMIC_ACQUIRE); -} - -#endif \ No newline at end of file diff --git a/exosphere/masterkey.c b/exosphere/masterkey.c deleted file mode 100644 index 5288b94..0000000 --- a/exosphere/masterkey.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include - -#include "utils.h" -#include "masterkey.h" -#include "se.h" - -unsigned int g_mkey_revision = 0; -bool g_determined_mkey_revision = false; - -uint8_t g_old_masterkeys[MASTERKEY_REVISION_MAX][0x10]; - -/* TODO: Dev keys. */ - -/* TODO: Extend with new vectors, as needed. */ -const uint8_t mkey_vectors[MASTERKEY_REVISION_MAX][0x10] = -{ - {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. */ -}; - -bool check_mkey_revision(unsigned int revision) { - uint8_t final_vector[0x10]; - - unsigned int check_keyslot = KEYSLOT_SWITCH_MASTERKEY; - if (revision > 0) { - /* Generate old master key array. */ - for (unsigned int i = revision; i > 0; i--) { - se_aes_ecb_decrypt_block(check_keyslot, g_old_masterkeys[i-1], 0x10, mkey_vectors[i], 0x10); - set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, g_old_masterkeys[i-1], 0x10); - check_keyslot = KEYSLOT_SWITCH_TEMPKEY; - } - } - - se_aes_ecb_decrypt_block(check_keyslot, final_vector, 0x10, mkey_vectors[0], 0x10); - for (unsigned int i = 0; i < 0x10; i++) { - if (final_vector[i] != 0) { - return false; - } - } - return true; -} - -void mkey_detect_revision(void) { - if (g_determined_mkey_revision) { - generic_panic(); - } - - for (unsigned int rev = 0; rev < MASTERKEY_REVISION_MAX; rev++) { - if (check_mkey_revision(rev)) { - g_determined_mkey_revision = true; - g_mkey_revision = rev; - break; - } - } - - /* We must have determined the master key, or we're not running on a Switch. */ - /* TODO: When panic is implemented, make this a really distinctive color. */ - /* Maybe bright red? */ - if (!g_determined_mkey_revision) { - generic_panic(); - } -} - -unsigned int mkey_get_revision(void) { - if (!g_determined_mkey_revision) { - generic_panic(); - } - - return g_mkey_revision; -} - -unsigned int mkey_get_keyslot(unsigned int revision) { - if (!g_determined_mkey_revision || revision >= MASTERKEY_REVISION_MAX) { - generic_panic(); - } - - if (revision > g_mkey_revision) { - generic_panic(); - } - - if (revision == g_mkey_revision) { - return KEYSLOT_SWITCH_MASTERKEY; - } else { - /* Load into a temp keyslot. */ - set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, g_old_masterkeys[revision], 0x10); - return KEYSLOT_SWITCH_TEMPKEY; - } -} diff --git a/exosphere/masterkey.h b/exosphere/masterkey.h deleted file mode 100644 index b6bd59e..0000000 --- a/exosphere/masterkey.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef EXOSPHERE_MASTERKEY_H -#define EXOSPHERE_MASTERKEY_H - -/* This is glue code to enable master key support across versions. */ - -/* TODO: Update to 0x5 on release of new master key. */ -#define MASTERKEY_REVISION_MAX 0x4 - -#define MASTERKEY_REVISION_100_230 0x00 -#define MASTERKEY_REVISION_300 0x01 -#define MASTERKEY_REVISION_301_302 0x02 -#define MASTERKEY_REVISION_400_CURRENT 0x03 - -/* This should be called early on in initialization. */ -void mkey_detect_revision(void); - -unsigned int mkey_get_revision(void); - -unsigned int mkey_get_keyslot(unsigned int revision); - -#endif \ No newline at end of file diff --git a/exosphere/mc.c b/exosphere/mc.c deleted file mode 100644 index f76f8d8..0000000 --- a/exosphere/mc.c +++ /dev/null @@ -1,104 +0,0 @@ -#include - -#include "memory_map.h" -#include "mc.h" - -volatile security_carveout_t *get_carveout_by_id(unsigned int carveout) { - if (CARVEOUT_ID_MIN <= carveout && carveout <= CARVEOUT_ID_MAX) { - return (volatile security_carveout_t *)(MC_BASE + 0xC08ULL + 0x50 * (carveout - CARVEOUT_ID_MIN)); - } - generic_panic(); - return NULL; - switch (carveout) { - case 4: /* Kernel carveout */ - return (volatile security_carveout_t *)(MC_BASE + 0xCF8ULL); - case 5: /* Unused Kernel carveout */ - return (volatile security_carveout_t *)(MC_BASE + 0xD48ULL); - default: - generic_panic(); - return NULL; - } -} - -void configure_default_carveouts(void) { - /* Configure Carveout 1 (UNUSED) */ - volatile security_carveout_t *carveout = get_carveout_by_id(1); - carveout->paddr_low = 0; - carveout->paddr_high = 0; - carveout->size_big_pages = 0; - carveout->flags_0 = 0; - carveout->flags_1 = 0; - carveout->flags_2 = 0; - carveout->flags_3 = 0; - carveout->flags_4 = 0; - carveout->flags_5 = 0; - carveout->flags_6 = 0; - carveout->flags_7 = 0; - carveout->flags_8 = 0; - carveout->flags_9 = 0; - carveout->allowed_clients = 0x04000006; - - /* Configure Carveout 2 (GPU UCODE) */ - carveout = get_carveout_by_id(2); - carveout->paddr_low = 0x80020000; - carveout->paddr_high = 0; - carveout->size_big_pages = 2; /* 0x40000 */ - carveout->flags_0 = 0; - carveout->flags_1 = 0; - carveout->flags_2 = 0x3000000; - carveout->flags_3 = 0; - carveout->flags_4 = 0x300; - carveout->flags_5 = 0; - carveout->flags_6 = 0; - carveout->flags_7 = 0; - carveout->flags_8 = 0; - carveout->flags_9 = 0; - carveout->allowed_clients = 0x440167E; - - /* Configure Carveout 3 (UNUSED GPU) */ - carveout = get_carveout_by_id(3); - carveout->paddr_low = 0; - carveout->paddr_high = 0; - carveout->size_big_pages = 0; - carveout->flags_0 = 0; - carveout->flags_1 = 0; - carveout->flags_2 = 0x3000000; - carveout->flags_3 = 0; - carveout->flags_4 = 0x300; - carveout->flags_5 = 0; - carveout->flags_6 = 0; - carveout->flags_7 = 0; - carveout->flags_8 = 0; - carveout->flags_9 = 0; - carveout->allowed_clients = 0x4401E7E; - - /* Configure default Kernel carveouts based on 2.0.0+. */ - - /* Configure Carveout 4 (KERNEL_BUILTINS) */ - configure_kernel_carveout(5, 0x80060000, KERNEL_CARVEOUT_SIZE_MAX); - - /* Configure Carveout 5 (KERNEL_UNUSED) */ - configure_kernel_carveout(5, 0, 0); -} - -void configure_kernel_carveout(unsigned int carveout_id, uint64_t address, uint64_t size) { - if (carveout_id != 4 && carveout_id != 5) { - generic_panic(); - } - - volatile security_carveout_t *carveout = get_carveout_by_id(carveout_id); - carveout->paddr_low = (uint32_t)(address & 0xFFFFFFFF); - carveout->paddr_high = (uint32_t)(address >> 32); - carveout->size_big_pages = (uint32_t)(size >> 17); - carveout->flags_0 = 0x70E3407F; - carveout->flags_1 = 0x1A620880; - carveout->flags_2 = 0x303C00; - carveout->flags_3 = 0xCF0830BB; - carveout->flags_4 = 0x3; - carveout->flags_5 = 0; - carveout->flags_6 = 0; - carveout->flags_7 = 0; - carveout->flags_8 = 0; - carveout->flags_9 = 0; - carveout->allowed_clients = 0x8B; -} diff --git a/exosphere/mc.h b/exosphere/mc.h deleted file mode 100644 index b26911d..0000000 --- a/exosphere/mc.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef EXOSPHERE_MC_H -#define EXOSPHERE_MC_H - -#include -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 Memory Controller. */ - -#define MC_BASE (mmio_get_device_address(MMIO_DEVID_MC)) - -#define CARVEOUT_ID_MIN 1 -#define CARVEOUT_ID_MAX 5 - -#define KERNEL_CARVEOUT_SIZE_MAX 0x1FFE0000 - -typedef struct { - uint32_t allowed_clients; - uint32_t paddr_low; - uint32_t paddr_high; - uint32_t size_big_pages; - uint32_t flags_0; - uint32_t flags_1; - uint32_t flags_2; - uint32_t flags_3; - uint32_t flags_4; - uint32_t flags_5; - uint32_t flags_6; - uint32_t flags_7; - uint32_t flags_8; - uint32_t flags_9; - uint8_t padding[0x28]; -} security_carveout_t; - - -volatile security_carveout_t *get_carveout_by_id(unsigned int carveout); -void configure_default_carveouts(void); -void configure_kernel_carveout(unsigned int carveout_id, uint64_t address, uint64_t size); - - -#endif diff --git a/exosphere/memory_map.h b/exosphere/memory_map.h deleted file mode 100644 index a0a05a8..0000000 --- a/exosphere/memory_map.h +++ /dev/null @@ -1,310 +0,0 @@ -#ifndef EXOSPHERE_MEMORY_MAP_H -#define EXOSPHERE_MEMORY_MAP_H - -#include "mmu.h" - -#define ATTRIB_MEMTYPE_NORMAL MMU_PTE_BLOCK_MEMTYPE(0) -#define ATTRIB_MEMTYPE_DEVICE MMU_PTE_BLOCK_MEMTYPE(1) - -static const struct { - uintptr_t address; - uint64_t size; - uint64_t attributes; - bool is_block_range; -} g_identity_mappings[] = { - { 0x40020000, 0x20000, 0, false }, /* iRAM-C+D (contains the secmon's coldboot crt0) */ - { 0x7C010000, 0x10000, 0, false }, /* TZRAM (contains the secmon's warmboot crt0) */ - { 0x80000000, 4u << 30, MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_NS, true }, /* DRAM (4GB) */ -}; - -static const struct { - uintptr_t pa; - size_t size; - bool is_secure; -} g_devices[] = { - { 0x50041000, 0x1000, true }, /* ARM Interrupt Distributor */ - { 0x50042000, 0x2000, true }, /* Interrupt Controller Physical CPU interface */ - { 0x70006000, 0x1000, false }, /* UART-A */ - { 0x60006000, 0x1000, false }, /* Clock and Reset */ - { 0x7000E000, 0x1000, true }, /* RTC, PMC */ - { 0x60005000, 0x1000, true }, /* TMRs, WDTs */ - { 0x6000C000, 0x1000, true }, /* System Registers */ - { 0x70012000, 0x2000, true }, /* SE */ - { 0x700F0000, 0x1000, true }, /* SYSCTR0 */ - { 0x70019000, 0x1000, true }, /* MC */ - { 0x7000F000, 0x1000, true }, /* FUSE (0x7000F800) */ - { 0x70000000, 0x4000, true }, /* MISC */ - { 0x60007000, 0x1000, true }, /* Flow Controller */ - { 0x40002000, 0x1000, true }, /* NX bootloader mailbox page */ - { 0x7000D000, 0x1000, true }, /* I2C-5,6 - SPI 2B-1 to 4 */ - { 0x6000D000, 0x1000, true }, /* GPIO-1 - GPIO-8 */ - { 0x7000C000, 0x1000, true }, /* I2C-I2C4 */ - { 0x6000F000, 0x1000, true }, /* Exception vectors */ -}; - -static const struct { - uintptr_t pa; - size_t size; - uint64_t attributes; -} g_lp0_entry_ram_segments[] = { - { 0x40020000, 0x10000, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE }, /* Encrypted TZRAM */ - { 0x40003000, 0x01000, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE }, /* LP0 entry code */ - { 0x7C010000, 0x10000, MMU_AP_PRIV_RO | ATTRIB_MEMTYPE_NORMAL }, /* TZRAM to encrypt */ -}; - -static const struct { - uintptr_t pa; - size_t size; - uint64_t attributes; -} g_warmboot_ram_segments[] = { - { 0x8000F000, 0x01000, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE }, /* Encrypted SE state for bootROM */ - { 0x80010000, 0x10000, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE }, /* Encrypted TZRAM for warmboot.bin */ -}; - -static const struct { - size_t tzram_offset; - size_t map_size; - size_t increment; /* for alignment, guard pages, etc. */ - bool is_code_segment; /* note: code is RWX */ -} g_tzram_segments[] = { - { 0x3000, 0x10000 - 0x2000 - 0x3000, 0x10000, true }, /* Warmboot crt0 sections and main code segment */ - { 0x10000 - 0x2000, 0x2000, 0x04000, true }, /* pk2ldr segment */ - { 0, 0, 0x02000, false }, /* SPL .bss buffer, NOT mapped at startup */ - { 0x10000 - 0x2000, 0x1000, 0x02000, false }, /* Core 0,1,2 stack */ - { 0x10000 - 0x1000, 0x1000, 0x02000, false }, /* Core 3 stack */ - { 0, 0x1000, 0x02000, true }, /* Secure Monitor exception vectors, some init stacks */ - { 0x1000, 0x1000, 0x02000, false }, /* L2 translation table */ - { 0x2000, 0x1000, 0x02000, false }, /* L3 translation table */ -}; - -#define MMIO_BASE 0x1F0080000ull -#define LP0_ENTRY_RAM_SEGMENT_BASE (MMIO_BASE + 0x000100000) -#define WARMBOOT_RAM_SEGMENT_BASE (LP0_ENTRY_RAM_SEGMENT_BASE + 0x000047000) /* increment seems to be arbitrary ? */ -#define TZRAM_SEGMENT_BASE (MMIO_BASE + 0x0001E0000) - -#define MMIO_DEVID_GICD 0 -#define MMIO_DEVID_GICC 1 -#define MMIO_DEVID_UART_A 2 -#define MMIO_DEVID_CLKRST 3 -#define MMIO_DEVID_RTC_PMC 4 -#define MMIO_DEVID_TMRs_WDTs 5 -#define MMIO_DEVID_SYSREGS 6 -#define MMIO_DEVID_SE 7 -#define MMIO_DEVID_SYSCTR0 8 -#define MMIO_DEVID_MC 9 -#define MMIO_DEVID_FUSE 10 -#define MMIO_DEVID_MISC 11 -#define MMIO_DEVID_FLOWCTRL 12 -#define MMIO_DEVID_NXBOOTLOADER_MAILBOX 13 -#define MMIO_DEVID_I2C56_SPI2B 14 -#define MMIO_DEVID_GPIO 15 -#define MMIO_DEVID_DTV_I2C234 16 -#define MMIO_DEVID_EXCEPTION_VECTORS 17 - -#define LP0_ENTRY_RAM_SEGMENT_ID_DECRYPTED_TZRAM 0 -#define LP0_ENTRY_RAM_SEGMENT_ID_LP0_ENTRY_CODE 1 -#define LP0_ENTRY_RAM_SEGMENT_ID_CURRENT_TZRAM 2 - -#define WARMBOOT_RAM_SEGMENT_ID_SE_STATE 0 -#define WARMBOOT_RAM_SEGMENT_ID_TZRAM 1 - -#define TZRAM_SEGMENT_ID_WARMBOOT_CRT0_MAIN_CODE 0 -#define TZRAM_SEGMENT_ID_PK2LDR 1 -#define TZRAM_SEGMENT_ID_USERPAGE 2 -#define TZRAM_SEGMENT_ID_CORE012_STACK 3 -#define TZRAM_SEGMENT_ID_CORE3_STACK 4 -#define TZRAM_SEGEMENT_ID_SECMON_EVT 5 -#define TZRAM_SEGMENT_ID_L2_TRANSLATION_TABLE 6 -#define TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE 7 - -/**********************************************************************************************/ - -static inline uintptr_t identity_map_all_mappings(uintptr_t *mmu_l1_tbl, uintptr_t *mmu_l3_tbl) { - static uint64_t base_attributes = MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_NORMAL; - for(size_t i = 0; i < sizeof(g_identity_mappings) / sizeof(g_identity_mappings[0]); i++) { - uint64_t attributes = base_attributes | g_identity_mappings[i].attributes; - if(g_identity_mappings[i].is_block_range) { - mmu_map_block_range(mmu_l1_tbl, g_identity_mappings[i].address, g_identity_mappings[i].address, - g_identity_mappings[i].size, attributes); - } - else { - mmu_map_page_range(mmu_l3_tbl, g_identity_mappings[i].address, g_identity_mappings[i].address, - g_identity_mappings[i].size, attributes); - } - } -} - -static inline uintptr_t identity_unmap_all_mappings(uintptr_t *mmu_l1_tbl, uintptr_t *mmu_l3_tbl) { - for(size_t i = 0; i < sizeof(g_identity_mappings) / sizeof(g_identity_mappings[0]); i++) { - if(g_identity_mappings[i].is_block_range) { - mmu_unmap_block_range(mmu_l1_tbl, g_identity_mappings[i].address, g_identity_mappings[i].size); - } - else { - mmu_unmap_page_range(mmu_l3_tbl, g_identity_mappings[i].address, g_identity_mappings[i].size); - } - } -} - -/**********************************************************************************************/ - -static inline uintptr_t mmio_get_device_pa(unsigned int device_id) { - return g_devices[device_id].pa; -} - -#ifndef MEMORY_MAP_USE_IDENTIY_MAPPING -static inline uintptr_t mmio_get_device_address(unsigned int device_id) { - size_t offset = 0; - for(unsigned int i = 0; i < device_id; i++) { - offset += g_devices[i].size; - offset += 0x1000; /* guard page */ - } - - return MMIO_BASE + offset; -} - -#else -static inline uintptr_t mmio_get_device_address(unsigned int device_id) { - return mmio_get_device_pa(device_id); -} -#endif - -static inline void mmio_map_all_devices(uintptr_t *mmu_l3_tbl) { - static const uint64_t secure_device_attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_DEVICE; - static const uint64_t device_attributes = MMU_PTE_BLOCK_NS | secure_device_attributes; - - for(size_t i = 0, offset = 0; i < sizeof(g_devices) / sizeof(g_devices[0]); i++) { - uint64_t attributes = g_devices[i].is_secure ? secure_device_attributes : device_attributes; - mmu_map_page_range(mmu_l3_tbl, MMIO_BASE + offset, g_devices[i].pa, g_devices[i].size, attributes); - - offset += g_devices[i].size; - offset += 0x1000; /* insert guard page */ - } -} - -static inline void mmio_unmap_all_devices(uintptr_t *mmu_l3_tbl) { - for(size_t i = 0, offset = 0; i < sizeof(g_devices) / sizeof(g_devices[0]); i++) { - mmu_unmap_range(3, mmu_l3_tbl, MMIO_BASE + offset, g_devices[i].size); - - offset += g_devices[i].size; - offset += 0x1000; /* insert guard page */ - } -} - -/**********************************************************************************************/ - -static inline uintptr_t lp0_get_plaintext_ram_segment_pa(unsigned int segment_id) { - return g_lp0_entry_ram_segments[segment_id].pa; -} - -#ifndef MEMORY_MAP_USE_IDENTIY_MAPPING -static inline uintptr_t lp0_get_plaintext_ram_segment_address(unsigned int segment_id) { - return LP0_PLAINTEXT_RAM_SEGMENT_BASE + 0x10000 * segment_id; -} -#else -static inline uintptr_t lp0_get_plaintext_ram_segment_address(unsigned int segment_id) { - return lp0_get_plaintext_ram_segment_pa(segment_id); -} -#endif - -static inline void lp0_map_all_plaintext_ram_segments(uintptr_t *mmu_l3_tbl) { - for(size_t i = 0, offset = 0; i < sizeof(g_lp0_entry_ram_segments) / sizeof(g_lp0_entry_ram_segments[0]); i++) { - uint64_t attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | g_lp0_entry_ram_segments[i].attributes; - mmu_map_page_range(mmu_l3_tbl, LP0_PLAINTEXT_RAM_SEGMENT_BASE + offset, g_lp0_entry_ram_segments[i].pa, - g_lp0_entry_ram_segments[i].size, attributes); - offset += 0x10000; - } -} - -static inline void lp0_unmap_all_plaintext_ram_segments(uintptr_t *mmu_l3_tbl) { - for(size_t i = 0, offset = 0; i < sizeof(g_lp0_entry_ram_segments) / sizeof(g_lp0_entry_ram_segments[0]); i++) { - mmu_unmap_range(3, mmu_l3_tbl, LP0_PLAINTEXT_RAM_SEGMENT_BASE + offset, g_lp0_entry_ram_segments[i].size); - - offset += 0x10000; - } -} - -/**********************************************************************************************/ - -static inline uintptr_t lp0_get_ciphertext_ram_segment_pa(unsigned int segment_id) { - return g_warmboot_ram_segments[segment_id].pa; -} - -#ifndef MEMORY_MAP_USE_IDENTIY_MAPPING -static inline uintptr_t lp0_get_ciphertext_ram_segment_address(unsigned int segment_id) { - size_t offset = 0; - for(unsigned int i = 0; i < segment_id; i++) { - offset += g_warmboot_ram_segments[i].size; - } - - return LP0_CIPHERTEXT_RAM_SEGMENT_BASE + offset; -} -#else -static inline uintptr_t lp0_get_ciphertext_ram_segment_address(unsigned int segment_id) { - return lp0_get_ciphertext_ram_segment_pa(segment_id); -} -#endif - -static inline void lp0_map_all_ciphertext_ram_segments(uintptr_t *mmu_l3_tbl) { - for(size_t i = 0, offset = 0; i < sizeof(g_warmboot_ram_segments) / sizeof(g_warmboot_ram_segments[0]); i++) { - uint64_t attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | g_warmboot_ram_segments[i].attributes; - mmu_map_page_range(mmu_l3_tbl, LP0_CIPHERTEXT_RAM_SEGMENT_BASE + offset, g_warmboot_ram_segments[i].pa, - g_warmboot_ram_segments[i].size, attributes); - offset += g_warmboot_ram_segments[i].size; - } -} - -static inline void lp0_unmap_all_ciphertext_ram_segments(uintptr_t *mmu_l3_tbl) { - for(size_t i = 0, offset = 0; i < sizeof(g_warmboot_ram_segments) / sizeof(g_warmboot_ram_segments[0]); i++) { - mmu_unmap_range(3, mmu_l3_tbl, LP0_CIPHERTEXT_RAM_SEGMENT_BASE + offset, g_warmboot_ram_segments[i].size); - - offset += g_warmboot_ram_segments[i].size; - } -} - -/**********************************************************************************************/ - -static inline uintptr_t tzram_get_segment_pa(unsigned int segment_id) { - return 0x7C010000 + g_tzram_segments[segment_id].tzram_offset; -} - -#ifndef MEMORY_MAP_USE_IDENTIY_MAPPING -static inline uintptr_t tzram_get_segment_address(unsigned int segment_id) { - size_t offset = 0; - for(unsigned int i = 0; i < segment_id; i++) { - offset += g_tzram_segments[i].increment; - } - - return TZRAM_SEGMENT_BASE + offset; -} -#else -static inline uintptr_t tzram_get_segment_address(unsigned int segment_id) { - return tzram_get_segment_pa(segment_id); -} -#endif - -static inline void tzram_map_all_segments(uintptr_t *mmu_l3_tbl) { - /* Except the SPL userpage */ - for(size_t i = 0, offset = 0; i < sizeof(g_tzram_segments) / sizeof(g_tzram_segments[0]); i++) { - uint64_t attributes = (g_tzram_segments[i].is_code_segment ? 0 : MMU_PTE_BLOCK_XN) | MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_NORMAL; - if(g_tzram_segments[i].map_size == 0) { - continue; - } - mmu_map_page_range(mmu_l3_tbl, TZRAM_SEGMENT_BASE + offset, 0x7C010000 + g_tzram_segments[i].tzram_offset, - g_tzram_segments[i].map_size, attributes); - offset += g_tzram_segments[i].increment; - } -} - -static inline void tzram_unmap_all_segments(uintptr_t *mmu_l3_tbl) { - /* Except the SPL userpage */ - for(size_t i = 0, offset = 0; i < sizeof(g_warmboot_ram_segments) / sizeof(g_warmboot_ram_segments[0]); i++) { - if(g_tzram_segments[i].map_size == 0) { - continue; - } - mmu_unmap_range(3, mmu_l3_tbl, TZRAM_SEGMENT_BASE + offset, g_tzram_segments[i].map_size); - - offset += g_tzram_segments[i].increment; - } -} - -#endif diff --git a/exosphere/mmu.h b/exosphere/mmu.h deleted file mode 100644 index 391e0b9..0000000 --- a/exosphere/mmu.h +++ /dev/null @@ -1,173 +0,0 @@ -#ifndef EXOSPHERE_MMU_H -#define EXOSPHERE_MMU_H - -#include -#include -#include -#include "utils.h" - - -#ifndef MMU_GRANULE_TYPE -#define MMU_GRANULE_TYPE 0 /* 0: 4KB, 1: 64KB, 2: 16KB. The Switch always uses a 4KB granule size. */ -#endif - -#if MMU_GRANULE_TYPE == 0 -#define MMU_Lx_SHIFT(x) (12 + 9 * (3 - (x))) -#define MMU_Lx_MASK(x) (BIT(9) - 1) -#elif MMU_GRANULE_TYPE == 1 -/* 64 KB, no L0 here */ -#define MMU_Lx_SHIFT(x) (16 + 13 * (3 - (x))) -#define MMU_Lx_MASK(x) ((x) == 1 ? (BIT(5) - 1) : (BIT(13) - 1)) -#elif MMU_GRANULE_TYPE == 2 -#define MMU_Lx_SHIFT(x) (14 + 11 * (3 - (x))) -#define MMU_Lx_MASK(x) ((x) == 0 ? 1 : (BIT(11) - 1)) -#endif - -/* - * The following defines are adapted from uboot: - * - * (C) Copyright 2013 - * David Feng - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#define MMU_MT_DEVICE_NGNRNE 0 -#define MMU_MT_DEVICE_NGNRE 1 -#define MMU_MT_DEVICE_GRE 2 -#define MMU_MT_NORMAL_NC 3 -#define MMU_MT_NORMAL 4 - -/* - * Hardware page table definitions. - * - */ - -#define MMU_PTE_TYPE_MASK 3 -#define MMU_PTE_TYPE_FAULT 0 -#define MMU_PTE_TYPE_TABLE 3 -#define MMU_PTE_TYPE_BLOCK 1 - -/* L3 only */ -#define MMU_PTE_TYPE_PAGE 3 - -#define MMU_PTE_TABLE_PXN BITL(59) -#define MMU_PTE_TABLE_XN BITL(60) -#define MMU_PTE_TABLE_AP BITL(61) -#define MMU_PTE_TABLE_NS BITL(63) - -/* - * Block - */ -#define MMU_PTE_BLOCK_MEMTYPE(x) ((x) << 2) -#define MMU_PTE_BLOCK_NS BIT(5) -#define MMU_PTE_BLOCK_NON_SHAREABLE (0 << 8) -#define MMU_PTE_BLOCK_OUTER_SHAREABLE (2 << 8) -#define MMU_PTE_BLOCK_INNER_SHAREBLE (3 << 8) -#define MMU_PTE_BLOCK_AF BIT(10) -#define MMU_PTE_BLOCK_NG BIT(11) -#define MMU_PTE_BLOCK_PXN BITL(53) -#define MMU_PTE_BLOCK_UXN BITL(54) -#define MMU_PTE_BLOCK_XN MMU_PTE_BLOCK_UXN - -/* - * AP[2:1] - */ -#define MMU_AP_PRIV_RW (0 << 6) -#define MMU_AP_RW (1 << 6) -#define MMU_AP_PRIV_RO (2 << 6) -#define MMU_AP_RO (3 << 6) - -/* - * S2AP[2:1] (for stage2 translations; secmon doesn't use it) - */ -#define MMU_S2AP_NONE (0 << 6) -#define MMU_S2AP_RO (1 << 6) -#define MMU_S2AP_WO (2 << 6) -#define MMU_S2AP_RW (3 << 6) - -/* - * AttrIndx[2:0] - */ -#define MMU_PMD_ATTRINDX(t) ((t) << 2) -#define MMU_PMD_ATTRINDX_MASK (7 << 2) - -/* - * TCR flags. - */ -#define TCR_T0SZ(x) ((64 - (x)) << 0) -#define TCR_IRGN_NC (0 << 8) -#define TCR_IRGN_WBWA (1 << 8) -#define TCR_IRGN_WT (2 << 8) -#define TCR_IRGN_WBNWA (3 << 8) -#define TCR_IRGN_MASK (3 << 8) -#define TCR_ORGN_NC (0 << 10) -#define TCR_ORGN_WBWA (1 << 10) -#define TCR_ORGN_WT (2 << 10) -#define TCR_ORGN_WBNWA (3 << 10) -#define TCR_ORGN_MASK (3 << 10) -#define TCR_NOT_SHARED (0 << 12) -#define TCR_SHARED_OUTER (2 << 12) -#define TCR_SHARED_INNER (3 << 12) -#define TCR_TG0_4K (0 << 14) -#define TCR_TG0_64K (1 << 14) -#define TCR_TG0_16K (2 << 14) -#define TCR_EPD1_DISABLE BIT(23) - -#define TCR_EL1_RSVD BIT(31) -#define TCR_EL2_RSVD (BIT(31) | BIT(23)) -#define TCR_EL3_RSVD (BIT(31) | BIT(23)) - -static inline void mmu_init_table(uintptr_t *tbl, size_t num_entries) { - for(size_t i = 0; i < num_entries; i++) { - tbl[i] = MMU_PTE_TYPE_FAULT; - } -} - -/* - All the functions below assume base_addr is valid. - They do not invalidate the TLB, which must be done separately. -*/ - -static inline unsigned int mmu_compute_index(unsigned int level, uintptr_t base_addr) { - return (base_addr >> MMU_Lx_SHIFT(level)) & MMU_Lx_MASK(level); -} - -static inline void mmu_map_table(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, uintptr_t *next_lvl_tbl_pa, uint64_t attrs) { - tbl[mmu_compute_index(level, base_addr)] = (uintptr_t)next_lvl_tbl_pa | attrs | MMU_PTE_TYPE_TABLE; -} - -static inline void mmu_map_block(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, uint64_t attrs) { - tbl[mmu_compute_index(level, base_addr)] = phys_addr | attrs | MMU_PTE_BLOCK_AF | MMU_PTE_TYPE_BLOCK; -} - -static inline void mmu_map_page(uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, uint64_t attrs) { - tbl[mmu_compute_index(3, base_addr)] = phys_addr | attrs | MMU_PTE_BLOCK_AF | MMU_PTE_TYPE_PAGE; -} - -static inline void mmu_unmap(unsigned int level, uintptr_t *tbl, uintptr_t base_addr) { - tbl[mmu_compute_index(level, base_addr)] = MMU_PTE_TYPE_FAULT; -} - -static inline void mmu_map_block_range(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, size_t size, uint64_t attrs) { - size = ((size + (BITL(MMU_Lx_SHIFT(level)) - 1)) >> MMU_Lx_SHIFT(level)) << MMU_Lx_SHIFT(level); - for(size_t offset = 0; offset < size; offset += MMU_Lx_SHIFT(level)) { - mmu_map_block(level, tbl, base_addr + offset, phys_addr + offset, attrs); - } -} - -static inline void mmu_map_page_range(uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, size_t size, uint64_t attrs) { - size = ((size + (BITL(MMU_Lx_SHIFT(3)) - 1)) >> MMU_Lx_SHIFT(3)) << MMU_Lx_SHIFT(3); - for(size_t offset = 0; offset < size; offset += MMU_Lx_SHIFT(3)) { - mmu_map_page(tbl, base_addr + offset, phys_addr + offset, attrs); - } -} - -static inline void mmu_unmap_range(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, size_t size) { - size = ((size + (BITL(MMU_Lx_SHIFT(level)) - 1)) >> MMU_Lx_SHIFT(level)) << MMU_Lx_SHIFT(level); - for(size_t offset = 0; offset < size; offset += MMU_Lx_SHIFT(level)) { - mmu_unmap(level, tbl, base_addr + offset); - } -} - -#endif diff --git a/exosphere/out/exosphere.bin b/exosphere/out/exosphere.bin new file mode 100644 index 0000000..d3b67d2 --- /dev/null +++ b/exosphere/out/exosphere.bin Binary files differ diff --git a/exosphere/package2.c b/exosphere/package2.c deleted file mode 100644 index a96cfb2..0000000 --- a/exosphere/package2.c +++ /dev/null @@ -1,452 +0,0 @@ -#include -#include -#include - -#include "utils.h" -#include "memory_map.h" - -#include "package2.h" -#include "configitem.h" -#include "se.h" -#include "masterkey.h" -#include "cache.h" -#include "randomcache.h" -#include "timers.h" - -/* Hardware init, sets up the RNG and SESSION keyslots, derives new DEVICE key. */ -static void setup_se(void) { - uint8_t work_buffer[0x10]; - - /* Sanity check the Security Engine. */ - se_verify_flags_cleared(); - se_clear_interrupts(); - - /* Perform some sanity initialization. */ - security_engine_t *p_security_engine = get_security_engine_address(); - p_security_engine->_0x4 = 0; - p_security_engine->AES_KEY_READ_DISABLE_REG = 0; - p_security_engine->RSA_KEY_READ_DISABLE_REG = 0; - p_security_engine->_0x0 &= 0xFFFFFFFB; - - /* Currently unknown what each flag does. */ - for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) { - set_aes_keyslot_flags(i, 0x15); - } - - for (unsigned int i = 4; i < KEYSLOT_AES_MAX; i++) { - set_aes_keyslot_flags(i, 0x40); - } - - for (unsigned int i = 0; i < KEYSLOT_RSA_MAX; i++) { - set_rsa_keyslot_flags(i, 0x41); - } - - /* Detect Master Key revision. */ - mkey_detect_revision(); - - /* Setup new device key, if necessary. */ - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { - const uint8_t new_devicekey_source_4x[0x10] = {0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}; - se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY, work_buffer, 0x10, new_devicekey_source_4x, 0x10); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_DEVICEKEY, KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY, work_buffer, 0x10); - clear_aes_keyslot(KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY); - set_aes_keyslot_flags(KEYSLOT_SWITCH_DEVICEKEY, 0xFF); - } - - se_initialize_rng(KEYSLOT_SWITCH_DEVICEKEY); - - /* Generate random data, transform with device key to get RNG key. */ - se_generate_random(KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_RNGKEY, KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); - set_aes_keyslot_flags(KEYSLOT_SWITCH_RNGKEY, 0xFF); - - /* Repeat for Session key. */ - se_generate_random(KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_SESSIONKEY, KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); - set_aes_keyslot_flags(KEYSLOT_SWITCH_SESSIONKEY, 0xFF); - - /* TODO: Create Test Vector, to validate keyslot data is unchanged post warmboot. */ -} - -static void setup_boot_config(void) { - /* Load boot config only if dev unit. */ - if (configitem_is_retail()) { - bootconfig_clear(); - } else { - flush_dcache_range((uint8_t *)NX_BOOTLOADER_BOOTCONFIG_POINTER, (uint8_t *)NX_BOOTLOADER_BOOTCONFIG_POINTER + sizeof(bootconfig_t)); - bootconfig_load_and_verify((bootconfig_t *)NX_BOOTLOADER_BOOTCONFIG_POINTER); - } -} - -static bool rsa2048_pss_verify(const void *signature, size_t signature_size, const void *modulus, size_t modulus_size, const void *data, size_t data_size) { - uint8_t message[RSA_2048_BYTES]; - uint8_t h_buf[0x24]; - - /* Hardcode RSA with keyslot 0. */ - const uint8_t public_exponent[4] = {0x00, 0x01, 0x00, 0x01}; - set_rsa_keyslot(0, modulus, modulus_size, public_exponent, sizeof(public_exponent)); - se_synchronous_exp_mod(0, message, sizeof(message), signature, signature_size); - - /* Validate sanity byte. */ - if (message[RSA_2048_BYTES - 1] != 0xBC) { - return false; - } - - /* Copy Salt into MGF1 Hash Buffer. */ - memset(h_buf, 0, sizeof(h_buf)); - memcpy(h_buf, message + RSA_2048_BYTES - 0x20 - 0x1, 0x20); - - /* Decrypt maskedDB (via inline MGF1). */ - uint8_t seed = 0; - uint8_t mgf1_buf[0x20]; - for (unsigned int ofs = 0; ofs < RSA_2048_BYTES - 0x20 - 1; ofs += 0x20) { - h_buf[sizeof(h_buf) - 1] = seed++; - flush_dcache_range(h_buf, h_buf + sizeof(h_buf)); - se_calculate_sha256(mgf1_buf, h_buf, sizeof(h_buf)); - for (unsigned int i = ofs; i < ofs + 0x20 && i < RSA_2048_BYTES - 0x20 - 1; i++) { - message[i] ^= mgf1_buf[i - ofs]; - } - } - - /* Constant lmask for rsa-2048-pss. */ - message[0] &= 0x7F; - - /* Validate DB is of the form 0000...0001. */ - for (unsigned int i = 0; i < RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1; i++) { - if (message[i] != 0) { - return false; - } - } - if (message[RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1] != 1) { - return false; - } - - /* Check hash correctness. */ - uint8_t validate_buf[8 + 0x20 + 0x20]; - uint8_t validate_hash[0x20]; - - memset(validate_buf, 0, sizeof(validate_buf)); - flush_dcache_range((uint8_t *)data, (uint8_t *)data + data_size); - se_calculate_sha256(&validate_buf[8], data, data_size); - memcpy(&validate_buf[0x28], &message[RSA_2048_BYTES - 0x20 - 0x20 - 1], 0x20); - flush_dcache_range(validate_buf, validate_buf + sizeof(validate_buf)); - se_calculate_sha256(validate_hash, validate_buf, sizeof(validate_buf)); - return memcmp(h_buf, validate_hash, 0x20) == 0; -} - -static void package2_crypt_ctr(unsigned int master_key_rev, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size) { - /* Derive package2 key. */ - const uint8_t package2_key_source[0x10] = {0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7}; - flush_dcache_range((uint8_t *)dst, (uint8_t *)dst + dst_size); - flush_dcache_range((uint8_t *)src, (uint8_t *)src + src_size); - unsigned int keyslot = mkey_get_keyslot(master_key_rev); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_PACKAGE2KEY, keyslot, package2_key_source, 0x10); - - /* Perform Encryption. */ - se_aes_ctr_crypt(KEYSLOT_SWITCH_PACKAGE2KEY, dst, dst_size, src, src_size, ctr, ctr_size); -} - - -static void verify_header_signature(package2_header_t *header) { - const uint8_t *modulus; - - if (configitem_is_retail()) { - const uint8_t package2_modulus_retail[0x100] = { - 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59, - 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, - 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, - 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74, - 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48, - 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE, - 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32, - 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A, - 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B, - 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3, - 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9, - 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47, - 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D, - 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2, - 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF, - 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F - }; - modulus = package2_modulus_retail; - } else { - const uint8_t package2_modulus_dev[0x100] = { - 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99, - 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, - 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, - 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38, - 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83, - 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0, - 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6, - 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50, - 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7, - 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42, - 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E, - 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8, - 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7, - 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4, - 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46, - 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B - }; - modulus = package2_modulus_dev; - } - - /* This is normally only allowed on dev units, but we'll allow it anywhere. */ - if (bootconfig_is_package2_unsigned() == 0 && rsa2048_pss_verify(header->signature, 0x100, modulus, 0x100, header->encrypted_header, 0x100) == 0) { - generic_panic(); - } -} - -static bool validate_package2_metadata(package2_meta_t *metadata) { - if (metadata->magic != MAGIC_PK21) { - return false; - } - - /* Package2 size, version number is stored XORed in header CTR. */ - /* Nintendo, what the fuck? */ - uint32_t package_size = metadata->ctr_dwords[0] ^ metadata->ctr_dwords[2] ^ metadata->ctr_dwords[3]; - uint8_t header_version = (uint8_t)((metadata->ctr_dwords[1] ^ (metadata->ctr_dwords[1] >> 16) ^ (metadata->ctr_dwords[1] >> 24)) & 0xFF); - - /* Ensure package isn't too big or too small. */ - if (package_size <= sizeof(package2_header_t) || package_size > PACKAGE2_SIZE_MAX - sizeof(package2_header_t)) { - return false; - } - - /* Validate that we're working with a header we know how to handle. */ - if (header_version > MASTERKEY_REVISION_MAX) { - return false; - } - - /* Require aligned entrypoint. */ - if (metadata->entrypoint & 3) { - return false; - } - - /* Validate section size sanity. */ - if (metadata->section_sizes[0] + metadata->section_sizes[1] + metadata->section_sizes[2] + sizeof(package2_header_t) != package_size) { - return false; - } - - bool entrypoint_found = false; - - /* Header has space for 4 sections, but only 3 are validated/potentially loaded on hardware. */ - for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { - /* Validate section size alignment. */ - if (metadata->section_sizes[section] & 3) { - return false; - } - - /* Validate section does not overflow. */ - if (check_32bit_additive_overflow(metadata->section_offsets[section], metadata->section_sizes[section])) { - return false; - } - - /* Check for entrypoint presence. */ - uint32_t section_end = metadata->section_offsets[section] + metadata->section_sizes[section]; - if (metadata->section_offsets[section] <= metadata->entrypoint && metadata->entrypoint < section_end) { - entrypoint_found = true; - } - - /* Ensure no overlap with later sections. */ - for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) { - uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section]; - if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) { - return false; - } - } - - /* Validate section hashes. */ - void *section_data = (void *)((uint8_t *)NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS + sizeof(package2_header_t) + metadata->section_offsets[section]); - uint8_t calculated_hash[0x20]; - se_calculate_sha256(calculated_hash, section_data, metadata->section_sizes[section]); - if (memcmp(calculated_hash, metadata->section_hashes[section], sizeof(metadata->section_hashes[section])) != 0) { - return false; - } - } - - /* Ensure that entrypoint is present in one of our sections. */ - if (!entrypoint_found) { - return false; - } - - /* Perform version checks. */ - /* We will be compatible with all package2s released before current, but not newer ones. */ - if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_400_CURRENT) { - return false; - } - - return true; -} - -/* Decrypts package2 header, and returns the master key revision required. */ -static uint32_t decrypt_and_validate_header(package2_header_t *header) { - package2_meta_t metadata; - - if (bootconfig_is_package2_plaintext() == 0) { - uint32_t mkey_rev; - - /* Try to decrypt for all possible master keys. */ - for (mkey_rev = 0; mkey_rev < MASTERKEY_REVISION_MAX; mkey_rev++) { - package2_crypt_ctr(mkey_rev, &metadata, sizeof(package2_meta_t), &header->metadata, sizeof(package2_meta_t), header->metadata.ctr, sizeof(header->metadata.ctr)); - /* Copy the ctr (which stores information) into the decrypted metadata. */ - memcpy(metadata.ctr, header->metadata.ctr, sizeof(header->metadata.ctr)); - /* See if this is the correct key. */ - if (validate_package2_metadata(&metadata)) { - header->metadata = metadata; - break; - } - } - - /* Ensure we successfully decrypted the header. */ - generic_panic(); - } - return 0; -} - -static void load_package2_sections(package2_meta_t *metadata, uint32_t master_key_rev) { - /* By default, copy data directly from where NX_BOOTLOADER puts it. */ - void *load_buf = NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS; - - /* Check whether any of our sections overlap this region. If they do, we must relocate and copy from elsewhere. */ - bool needs_relocation = false; - for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { - uint64_t section_start = DRAM_BASE_PHYSICAL + (uint64_t)metadata->section_offsets[section]; - uint64_t section_end = section_start + (uint64_t)metadata->section_sizes[section]; - if (overlaps(section_start, section_end, (uint64_t)(NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS), (uint64_t)(NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS) + PACKAGE2_SIZE_MAX)) { - needs_relocation = true; - } - } - if (needs_relocation) { - /* This code should *always* succeed in finding a carveout within seven loops, */ - /* due to the section size limit, and section number limit. */ - /* However, Nintendo tries panics after 8 loops if a safe section is not found. */ - /* This should never be the case, mathematically. */ - /* We will replicate this behavior. */ - bool found_safe_carveout = false; - uint64_t potential_base_start = DRAM_BASE_PHYSICAL; - uint64_t potential_base_end = potential_base_start + PACKAGE2_SIZE_MAX; - for (unsigned int i = 0; i < 8; i++) { - int is_safe = 1; - for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { - uint64_t section_start = DRAM_BASE_PHYSICAL + (uint64_t)metadata->section_offsets[section]; - uint64_t section_end = section_start + (uint64_t)metadata->section_sizes[section]; - if (overlaps(section_start, section_end, potential_base_start, potential_base_end)) { - is_safe = 0; - } - } - found_safe_carveout |= is_safe; - if (found_safe_carveout) { - break; - } - potential_base_start += PACKAGE2_SIZE_MAX; - potential_base_end += PACKAGE2_SIZE_MAX; - } - if (!found_safe_carveout) { - generic_panic(); - } - /* Relocate to new carveout. */ - memcpy((void *)potential_base_start, load_buf, PACKAGE2_SIZE_MAX); - memset(load_buf, 0, PACKAGE2_SIZE_MAX); - load_buf = (void *)potential_base_start; - } - - /* Copy each section to its appropriate location, decrypting if necessary. */ - for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { - if (metadata->section_sizes[section] == 0) { - continue; - } - - void *dst_start = (void *)(DRAM_BASE_PHYSICAL + (uint64_t)metadata->section_offsets[section]); - void *src_start = load_buf + sizeof(package2_header_t) + metadata->section_offsets[section]; - size_t size = (size_t)metadata->section_sizes[section]; - - if (bootconfig_is_package2_plaintext()) { - memcpy(dst_start, src_start, size); - } else { - package2_crypt_ctr(master_key_rev, dst_start, size, src_start, size, metadata->section_ctrs[section], 0x10); - } - } - - /* Clear the encrypted package2 from memory. */ - memset(load_buf, 0, PACKAGE2_SIZE_MAX); -} - -uintptr_t get_pk2ldr_stack_address(void) { - return tzram_get_segment_address(TZRAM_SEGMENT_ID_PK2LDR) + 0x2000; -} - -/* This function is called during coldboot init, and validates a package2. */ -/* This package2 is read into memory by a concurrent BPMP bootloader. */ -void load_package2(void) { - /* Setup the Security Engine. */ - setup_se(); - - /* TODO: bootup_misc_mmio(). */ - /* This func will also be called on warmboot. */ - /* And will verify stored SE Test Vector, clear keyslots, */ - /* Generate an SRK, set the warmboot firmware location, */ - /* Configure the GPU uCode carveout, configure the Kernel default carveouts, */ - /* Initialize the PMC secure scratch registers, initialize MISC registers, */ - /* And assign "se_operation_completed" to Interrupt 0x5A. */ - - /* TODO: Read and save BOOTREASON stored by NX_BOOTLOADER at 0x1F009FE00 */ - - /* Initialize cache'd random bytes for kernel. */ - randomcache_init(); - - /* TODO: memclear the initial copy of Exosphere running in IRAM (relocated to TZRAM by earlier code). */ - - /* Let NX Bootloader know that we're running. */ - MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE = 1; - - /* Synchronize with NX BOOTLOADER. */ - if (MAILBOX_NX_BOOTLOADER_SETUP_STATE == NX_BOOTLOADER_STATE_INIT) { - while (MAILBOX_NX_BOOTLOADER_SETUP_STATE < NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG) { - wait(1); - } - } - - /* Load Boot Config into global. */ - setup_boot_config(); - - /* Synchronize with NX BOOTLOADER. */ - if (MAILBOX_NX_BOOTLOADER_SETUP_STATE == NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG) { - while (MAILBOX_NX_BOOTLOADER_SETUP_STATE < NX_BOOTLOADER_STATE_LOADED_PACKAGE2) { - wait(1); - } - } - - /* Load header from NX_BOOTLOADER-initialized DRAM. */ - package2_header_t header; - flush_dcache_range((uint8_t *)NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, (uint8_t *)NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS + sizeof(header)); - memcpy(&header, NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, sizeof(header)); - flush_dcache_range((uint8_t *)&header, (uint8_t *)&header + sizeof(header)); - - /* Perform signature checks. */ - verify_header_signature(&header); - - /* Decrypt header, get key revision required. */ - uint32_t package2_mkey_rev = decrypt_and_validate_header(&header); - - /* Load Package2 Sections. */ - load_package2_sections(&header.metadata, package2_mkey_rev); - - /* Clean up cache. */ - flush_dcache_all(); - invalidate_icache_inner_shareable(); - - /* Set CORE0 entrypoint for Package2. */ - set_core_entrypoint_and_context_id(0, DRAM_BASE_PHYSICAL + header.metadata.entrypoint, 0); - - /* Synchronize with NX BOOTLOADER. */ - if (MAILBOX_NX_BOOTLOADER_SETUP_STATE == NX_BOOTLOADER_STATE_LOADED_PACKAGE2) { - while (MAILBOX_NX_BOOTLOADER_SETUP_STATE < NX_BOOTLOADER_STATE_FINISHED) { - wait(1); - } - } - - /* TODO: MISC register 0x1F0098C00 |= 0x2000; */ - - /* TODO: Update SCR_EL3 depending on value in Bootconfig. */ -} diff --git a/exosphere/package2.h b/exosphere/package2.h deleted file mode 100644 index f32fa8d..0000000 --- a/exosphere/package2.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef EXOSPHERE_PACKAGE2_H -#define EXOSPHERE_PACKAGE2_H - -/* This is code responsible for validating a package2. Actual file reading is done by bootloader. */ - -#include -#include "bootconfig.h" -#include "memory_map.h" - -/* Physaddr 0x40002EF8 */ -#define MAILBOX_NX_BOOTLOADER_BASE (mmio_get_device_address(MMIO_DEVID_NXBOOTLOADER_MAILBOX)) - -#define MAILBOX_NX_BOOTLOADER_SETUP_STATE (*((volatile uint32_t *)(MAILBOX_NX_BOOTLOADER_BASE + 0xEF8ULL))) - -#define NX_BOOTLOADER_STATE_INIT 0 -#define NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG 1 -#define NX_BOOTLOADER_STATE_LOADED_PACKAGE2 2 -#define NX_BOOTLOADER_STATE_FINISHED 3 - -/* Physaddr 0x40002EFC */ -#define MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE (*((volatile uint32_t *)(MAILBOX_NX_BOOTLOADER_BASE + 0xEFCULL))) - -#define NX_BOOTLOADER_BOOTCONFIG_POINTER ((void *)(0x4003D000ULL)) - -#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ULL)) - -#define DRAM_BASE_PHYSICAL (0x80000000ULL) - -#define MAGIC_PK21 (0x31324B50) -#define PACKAGE2_SIZE_MAX 0x7FC000 -#define PACKAGE2_SECTION_MAX 0x3 - -#define PACKAGE2_MINVER_THEORETICAL 0x0 -#define PACKAGE2_MAXVER_100 0x2 -#define PACKAGE2_MAXVER_200 0x3 -#define PACKAGE2_MAXVER_300 0x4 -#define PACKAGE2_MAXVER_302 0x5 -#define PACKAGE2_MAXVER_400_CURRENT 0x6 - -#define PACKAGE2_MINVER_100 0x3 -#define PACKAGE2_MINVER_200 0x4 -#define PACKAGE2_MINVER_300 0x5 -#define PACKAGE2_MINVER_302 0x6 -#define PACKAGE2_MINVER_400_CURRENT 0x7 - - -#pragma pack(push, 1) -typedef struct { - union { - uint8_t ctr[0x10]; - uint32_t ctr_dwords[0x4]; - }; - uint8_t section_ctrs[4][0x10]; - uint32_t magic; - uint32_t entrypoint; - uint32_t _0x58; - uint8_t version_max; /* Must be > TZ value. */ - uint8_t version_min; /* Must be < TZ value. */ - uint16_t _0x5E; - uint32_t section_sizes[4]; - uint32_t section_offsets[4]; - uint8_t section_hashes[4][0x20]; -} package2_meta_t; -#pragma pack(pop) - -#pragma pack(push, 1) -typedef struct { - uint8_t signature[0x100]; - union { - package2_meta_t metadata; - uint8_t encrypted_header[0x100]; - }; -} package2_header_t; -#pragma pack(pop) - -void load_package2(void); - -#endif diff --git a/exosphere/pmc.h b/exosphere/pmc.h deleted file mode 100644 index 1ffb1be..0000000 --- a/exosphere/pmc.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef EXOSPHERE_PMC_H -#define EXOSPHERE_PMC_H - -#include -#include "memory_map.h" - -/* Exosphere register definitions for the Tegra X1 PMC. */ - -#define PMC_BASE (mmio_get_device_address(MMIO_DEVID_RTC_PMC) + 0x400ULL) - -#define APBDEV_PMC_PWRGATE_TOGGLE_0 (*((volatile uint32_t *)(PMC_BASE + 0x30))) -#define APBDEV_PMC_PWRGATE_STATUS_0 (*((volatile uint32_t *)(PMC_BASE + 0x38))) - -#endif diff --git a/exosphere/randomcache.c b/exosphere/randomcache.c deleted file mode 100644 index a941cd3..0000000 --- a/exosphere/randomcache.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include - -#include "utils.h" -#include "randomcache.h" -#include "se.h" - -/* TrustZone maintains a cache of random for the kernel. */ -/* So that requests can still be serviced even when a */ -/* usermode SMC is in progress. */ - -volatile uint8_t g_random_cache[0x400]; -volatile unsigned int g_random_cache_low = 0; -volatile unsigned int g_random_cache_high = 0x3FF; - - -void randomcache_refill_segment(unsigned int offset, unsigned int size) { - if (offset + size >= 0x400) { - size = 0x400 - offset; - } - - flush_dcache_range(&g_random_cache[offset], &g_random_cache[offset + size]); - se_generate_random(KEYSLOT_SWITCH_RNGKEY, &g_random_cache[offset], size); - flush_dcache_range(&g_random_cache[offset], &g_random_cache[offset + size]); - -} - -void randomcache_init(void) { - randomcache_refill_segment(0, 0x400); - g_random_cache_low = 0; - g_random_cache_high = 0x3FF; -} - -void randomcache_refill(void) { - unsigned int high_plus_one = (g_random_cache_high + 1) & 0x3FF; - if (g_random_cache_low != high_plus_one) { - /* Only refill if there's data to refill. */ - if (g_random_cache_low < high_plus_one) { - /* NOTE: There is a bug in official code that causes this to not work properly. */ - /* In particular, official code checks whether high_plus_one == 0x400. */ - /* However, because high_plus_one is &= 0x3FF'd, this can never be the case. */ - /* We will implement according to Nintendo's intention, and not include their bug. */ - /* This should have no impact on actual observable results, anyway, since this data is random anyway... */ - - if (g_random_cache_high != 0x3FF) { /* This is if (true) in Nintendo's code due to the above bug. */ - randomcache_refill_segment(high_plus_one, 0x400 - high_plus_one); - g_random_cache_high = (g_random_cache_high + 0x400 - high_plus_one) & 0x3FF; - } - - if (g_random_cache_low > 0) { - randomcache_refill_segment(0, g_random_cache_low); - g_random_cache_high = (g_random_cache_high + g_random_cache_low) & 0x3FF; - } - } else { /* g_random_cache_low > high_plus_one */ - randomcache_refill_segment(high_plus_one, g_random_cache_low - high_plus_one); - g_random_cache_high = g_random_cache_low - 1; - } - } -} - -void randomcache_getbytes(void *dst, size_t num_bytes) { - unsigned int low = g_random_cache_low; - - memcpy(dst, &g_random_cache[low], num_bytes); - - unsigned int new_low = low + num_bytes; - if (new_low + 0x38 > 0x3FF) { - new_low = 0; - } - - g_random_cache_low = new_low; -} \ No newline at end of file diff --git a/exosphere/randomcache.h b/exosphere/randomcache.h deleted file mode 100644 index 7d0ddc8..0000000 --- a/exosphere/randomcache.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef EXOSPHERE_RANDOM_CACHE_H -#define EXOSPHERE_RANDOM_CACHE_H - -#include - -/* This method must be called on startup. */ -void randomcache_init(void); -void randomcache_refill(void); - -void randomcache_getbytes(void *dst, size_t num_bytes); - - -#endif \ No newline at end of file diff --git a/exosphere/se.c b/exosphere/se.c deleted file mode 100644 index afecd03..0000000 --- a/exosphere/se.c +++ /dev/null @@ -1,603 +0,0 @@ -#include -#include -#include - -#include "utils.h" -#include "interrupt.h" -#include "se.h" -#include "memory_map.h" -#include "cache.h" -#include "se.h" - -/* Macro for the SE registers. */ -#define SECURITY_ENGINE ((volatile security_engine_t *)(mmio_get_device_address(MMIO_DEVID_SE))) - -void trigger_se_rsa_op(void *buf, size_t size); -void trigger_se_blocking_op(unsigned int op, void *dst, size_t dst_size, const void *src, size_t src_size); - -/* Globals for driver. */ -unsigned int (*g_se_callback)(void); - -unsigned int g_se_modulus_sizes[KEYSLOT_RSA_MAX]; -unsigned int g_se_exp_sizes[KEYSLOT_RSA_MAX]; - - -/* Initialize a SE linked list. */ -void ll_init(se_ll_t *ll, void *buffer, size_t size) { - ll->num_entries = 0; /* 1 Entry. */ - - if (buffer != NULL) { - ll->addr_info.address = get_physical_address(buffer); - ll->addr_info.size = (uint32_t) size; - } else { - ll->addr_info.address = 0; - ll->addr_info.size = 0; - } - - flush_dcache_range((uint8_t *)ll, (uint8_t *)ll + sizeof(*ll)); -} - -/* Gets security engine pointer. */ -security_engine_t *get_security_engine_address(void) { - return SECURITY_ENGINE; -} - -void set_security_engine_callback(unsigned int (*callback)(void)) { - if (callback == NULL || g_se_callback != NULL) { - generic_panic(); - } - - g_se_callback = callback; -} - -/* Fires on Security Engine operation completion. */ -void se_operation_completed(void) { - SECURITY_ENGINE->INT_ENABLE_REG = 0; - if (g_se_callback != NULL) { - g_se_callback(); - g_se_callback = NULL; - } -} - - -void se_check_for_error(void) { - if (SECURITY_ENGINE->INT_STATUS_REG & 0x10000 || SECURITY_ENGINE->FLAGS_REG & 3 || SECURITY_ENGINE->ERR_STATUS_REG) { - generic_panic(); - } -} - -void se_trigger_intrrupt(void) { - intr_set_pending(INTERRUPT_ID_USER_SECURITY_ENGINE); -} - -void se_verify_flags_cleared(void) { - if (SECURITY_ENGINE->FLAGS_REG & 3) { - generic_panic(); - } -} - -void se_clear_interrupts(void) { - /* TODO */ -} - -/* Set the flags for an AES keyslot. */ -void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags) { - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - /* Misc flags. */ - if (flags & ~0x80) { - SECURITY_ENGINE->AES_KEYSLOT_FLAGS[keyslot] = ~flags; - } - - /* Disable keyslot reads. */ - if (flags & 0x80) { - SECURITY_ENGINE->AES_KEY_READ_DISABLE_REG &= ~(1 << keyslot); - } -} - -/* Set the flags for an RSA keyslot. */ -void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags) { - if (keyslot >= KEYSLOT_RSA_MAX) { - generic_panic(); - } - - /* Misc flags. */ - if (flags & ~0x80) { - /* TODO: Why are flags assigned this way? */ - SECURITY_ENGINE->RSA_KEYSLOT_FLAGS[keyslot] = (((flags >> 4) & 4) | (flags & 3)) ^ 7; - } - - /* Disable keyslot reads. */ - if (flags & 0x80) { - SECURITY_ENGINE->RSA_KEY_READ_DISABLE_REG &= ~(1 << keyslot); - } -} - -void clear_aes_keyslot(unsigned int keyslot) { - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - /* Zero out the whole keyslot and IV. */ - for (unsigned int i = 0; i < 0x10; i++) { - SECURITY_ENGINE->AES_KEYTABLE_ADDR = (keyslot << 4) | i; - SECURITY_ENGINE->AES_KEYTABLE_DATA = 0; - } -} - -void clear_rsa_keyslot(unsigned int keyslot) { - if (keyslot >= KEYSLOT_RSA_MAX) { - generic_panic(); - } - - /* Zero out the whole keyslot. */ - for (unsigned int i = 0; i < 0x40; i++) { - /* Select Keyslot Modulus[i] */ - SECURITY_ENGINE->RSA_KEYTABLE_ADDR = (keyslot << 7) | i | 0x40; - SECURITY_ENGINE->RSA_KEYTABLE_DATA = 0; - } - for (unsigned int i = 0; i < 0x40; i++) { - /* Select Keyslot Expontent[i] */ - SECURITY_ENGINE->RSA_KEYTABLE_ADDR = (keyslot << 7) | i; - SECURITY_ENGINE->RSA_KEYTABLE_DATA = 0; - } -} - -void set_aes_keyslot(unsigned int keyslot, const void *key, size_t key_size) { - if (keyslot >= KEYSLOT_AES_MAX || key_size > KEYSIZE_AES_MAX) { - generic_panic(); - } - - for (size_t i = 0; i < (key_size >> 2); i++) { - SECURITY_ENGINE->AES_KEYTABLE_ADDR = (keyslot << 4) | i; - SECURITY_ENGINE->AES_KEYTABLE_DATA = read32le(key, 4 * i); - } -} - -void set_rsa_keyslot(unsigned int keyslot, const void *modulus, size_t modulus_size, const void *exponent, size_t exp_size) { - if (keyslot >= KEYSLOT_RSA_MAX || modulus_size > KEYSIZE_RSA_MAX || exp_size > KEYSIZE_RSA_MAX) { - generic_panic(); - } - - for (size_t i = 0; i < (modulus_size >> 2); i++) { - SECURITY_ENGINE->RSA_KEYTABLE_ADDR = (keyslot << 7) | 0x40 | i; - SECURITY_ENGINE->RSA_KEYTABLE_DATA = read32be(modulus, 4 * i); - } - - for (size_t i = 0; i < (exp_size >> 2); i++) { - SECURITY_ENGINE->RSA_KEYTABLE_ADDR = (keyslot << 7) | i; - SECURITY_ENGINE->RSA_KEYTABLE_DATA = read32be(exponent, 4 * i); - } - - g_se_modulus_sizes[keyslot] = modulus_size; - g_se_exp_sizes[keyslot] = exp_size; -} - -void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size) { - if (keyslot >= KEYSLOT_AES_MAX || iv_size > 0x10) { - generic_panic(); - } - - for (size_t i = 0; i < (iv_size >> 2); i++) { - SECURITY_ENGINE->AES_KEYTABLE_ADDR = (keyslot << 4) | 8 | i; - SECURITY_ENGINE->AES_KEYTABLE_DATA = read32le(iv, 4 * i); - } -} - -void clear_aes_keyslot_iv(unsigned int keyslot) { - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - for (size_t i = 0; i < (0x10 >> 2); i++) { - SECURITY_ENGINE->AES_KEYTABLE_ADDR = (keyslot << 4) | 8; - SECURITY_ENGINE->AES_KEYTABLE_DATA = 0; - } -} - -void set_se_ctr(const void *ctr) { - for (unsigned int i = 0; i < 4; i++) { - SECURITY_ENGINE->CRYPTO_CTR_REG[i] = read32le(ctr, i * 4); - } -} - -void decrypt_data_into_keyslot(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size) { - if (keyslot_dst >= KEYSLOT_AES_MAX || keyslot_src >= KEYSIZE_AES_MAX || wrapped_key_size > KEYSIZE_AES_MAX) { - generic_panic(); - } - - SECURITY_ENGINE->CONFIG_REG = (ALG_AES_DEC | DST_KEYTAB); - SECURITY_ENGINE->CRYPTO_REG = keyslot_src << 24; - SECURITY_ENGINE->BLOCK_COUNT_REG = 0; - SECURITY_ENGINE->CRYPTO_KEYTABLE_DST_REG = keyslot_dst << 8; - - flush_dcache_range(wrapped_key, (const uint8_t *)wrapped_key + wrapped_key_size); - trigger_se_aes_op(OP_START, NULL, 0, wrapped_key, wrapped_key_size); -} - -void se_aes_crypt_insecure_internal(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, unsigned int crypt_config, bool encrypt, unsigned int (*callback)(void)) { - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - if (size == 0) { - return; - } - - /* Setup Config register. */ - if (encrypt) { - SECURITY_ENGINE->CONFIG_REG = (ALG_AES_ENC | DST_MEMORY); - } else { - SECURITY_ENGINE->CONFIG_REG = (ALG_AES_DEC | DST_MEMORY); - } - - /* Setup Crypto register. */ - SECURITY_ENGINE->CRYPTO_REG = crypt_config | (keyslot << 24) | (encrypt << 8); - - /* Mark this encryption as insecure -- this makes the SE not a secure busmaster. */ - SECURITY_ENGINE->CRYPTO_REG |= 0x80000000; - - /* Appropriate number of blocks. */ - SECURITY_ENGINE->BLOCK_COUNT_REG = (size >> 4) - 1; - - /* Set the callback, for after the async operation. */ - set_security_engine_callback(callback); - - /* Enable SE Interrupt firing for async op. */ - SECURITY_ENGINE->INT_ENABLE_REG = 0x10; - - /* Setup Input/Output lists */ - SECURITY_ENGINE->IN_LL_ADDR_REG = in_ll_paddr; - SECURITY_ENGINE->OUT_LL_ADDR_REG = out_ll_paddr; - - /* Set registers for operation. */ - SECURITY_ENGINE->ERR_STATUS_REG = SECURITY_ENGINE->ERR_STATUS_REG; - SECURITY_ENGINE->INT_STATUS_REG = SECURITY_ENGINE->INT_STATUS_REG; - SECURITY_ENGINE->OPERATION_REG = 1; - - /* Ensure writes go through. */ - __asm__ __volatile__ ("dsb ish" : : : "memory"); -} - -void se_aes_ctr_crypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *ctr, unsigned int (*callback)(void)) { - /* Unknown what this write does, but official code writes it for CTR mode. */ - SECURITY_ENGINE->_0x80C = 1; - set_se_ctr(ctr); - se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x81E, true, callback); -} - -void se_aes_cbc_encrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)) { - set_aes_keyslot_iv(keyslot, iv, 0x10); - se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x44, true, callback); -} - -void se_aes_cbc_decrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)) { - set_aes_keyslot_iv(keyslot, iv, 0x10); - se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x66, false, callback); -} - - -void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*callback)(void)) { - uint8_t stack_buf[KEYSIZE_RSA_MAX]; - - if (keyslot >= KEYSLOT_RSA_MAX || size > KEYSIZE_RSA_MAX) { - generic_panic(); - } - - /* Endian swap the input. */ - for (size_t i = size; i > 0; i--) { - stack_buf[i] = *((uint8_t *)buf + size - i); - } - - - SECURITY_ENGINE->CONFIG_REG = (ALG_RSA | DST_RSAREG); - SECURITY_ENGINE->RSA_CONFIG = keyslot << 24; - SECURITY_ENGINE->RSA_KEY_SIZE_REG = (g_se_modulus_sizes[keyslot] >> 6) - 1; - SECURITY_ENGINE->RSA_EXP_SIZE_REG = g_se_exp_sizes[keyslot] >> 2; - - set_security_engine_callback(callback); - - /* Enable SE Interrupt firing for async op. */ - SECURITY_ENGINE->INT_ENABLE_REG = 0x10; - - flush_dcache_range(stack_buf, stack_buf + KEYSIZE_RSA_MAX); - trigger_se_rsa_op(stack_buf, size); - - while (!(SECURITY_ENGINE->INT_STATUS_REG & 2)) { /* Wait a while */ } -} - -void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { - uint8_t stack_buf[KEYSIZE_RSA_MAX]; - - if (keyslot >= KEYSLOT_RSA_MAX || src_size > KEYSIZE_RSA_MAX || dst_size > KEYSIZE_RSA_MAX) { - generic_panic(); - } - - /* Endian swap the input. */ - for (size_t i = src_size; i > 0; i--) { - stack_buf[i] = *((uint8_t *)src + src_size - i); - } - - SECURITY_ENGINE->CONFIG_REG = (ALG_RSA | DST_RSAREG); - SECURITY_ENGINE->RSA_CONFIG = keyslot << 24; - SECURITY_ENGINE->RSA_KEY_SIZE_REG = (g_se_modulus_sizes[keyslot] >> 6) - 1; - SECURITY_ENGINE->RSA_EXP_SIZE_REG = g_se_exp_sizes[keyslot] >> 2; - - - flush_dcache_range(stack_buf, stack_buf + KEYSIZE_RSA_MAX); - trigger_se_blocking_op(1, NULL, 0, stack_buf, src_size); - se_get_exp_mod_output(dst, dst_size); -} - -void se_get_exp_mod_output(void *buf, size_t size) { - size_t num_dwords = (size >> 2); - if (num_dwords < 1) { - return; - } - - uint32_t *p_out = ((uint32_t *)buf) + num_dwords - 1; - uint32_t offset = 0; - - /* Copy endian swapped output. */ - while (num_dwords) { - *p_out = read32be(SECURITY_ENGINE->RSA_OUTPUT, offset); - offset += 4; - p_out--; - num_dwords--; - } -} - -void trigger_se_rsa_op(void *buf, size_t size) { - se_ll_t in_ll; - ll_init(&in_ll, buf, size); - - /* Set the input LL. */ - SECURITY_ENGINE->IN_LL_ADDR_REG = get_physical_address(&in_ll); - - /* Set registers for operation. */ - SECURITY_ENGINE->ERR_STATUS_REG = SECURITY_ENGINE->ERR_STATUS_REG; - SECURITY_ENGINE->INT_STATUS_REG = SECURITY_ENGINE->INT_STATUS_REG; - SECURITY_ENGINE->OPERATION_REG = 1; - - /* Ensure writes go through. */ - __asm__ __volatile__ ("dsb ish" : : : "memory"); -} - -void trigger_se_blocking_op(unsigned int op, void *dst, size_t dst_size, const void *src, size_t src_size) { - se_ll_t in_ll; - se_ll_t out_ll; - - ll_init(&in_ll, src, src_size); - ll_init(&out_ll, dst, dst_size); - - /* Set the LLs. */ - SECURITY_ENGINE->IN_LL_ADDR_REG = get_physical_address(&in_ll); - SECURITY_ENGINE->OUT_LL_ADDR_REG = get_physical_address(&out_ll); - - /* Set registers for operation. */ - SECURITY_ENGINE->ERR_STATUS_REG = SECURITY_ENGINE->ERR_STATUS_REG; - SECURITY_ENGINE->INT_STATUS_REG = SECURITY_ENGINE->INT_STATUS_REG; - SECURITY_ENGINE->OPERATION_REG = op; - - while (!(SECURITY_ENGINE->INT_STATUS_REG & 0x10)) { /* Wait a while */ } - se_check_for_error(); -} - - -/* Secure AES Functionality. */ -void se_perform_aes_block_operation(void *dst, size_t dst_size, const void *src, size_t src_size) { - uint8_t block[0x10]; - - if (src_size > sizeof(block) || dst_size > sizeof(block)) { - generic_panic(); - } - - /* Load src data into block. */ - memset(block, 0, sizeof(block)); - memcpy(block, src, src_size); - flush_dcache_range(block, block + sizeof(block)); - - /* Trigger AES operation. */ - SECURITY_ENGINE->BLOCK_COUNT_REG = 0; - trigger_se_blocking_op(1, block, sizeof(block), block, sizeof(block)); - - /* Copy output data into dst. */ - flush_dcache_range(block, block + sizeof(block)); - memcpy(dst, block, dst_size); -} - -void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size) { - if (keyslot >= KEYSLOT_AES_MAX || ctr_size != 0x10) { - generic_panic(); - } - - unsigned int num_blocks = src_size >> 4; - - /* Unknown what this write does, but official code writes it for CTR mode. */ - SECURITY_ENGINE->_0x80C = 1; - SECURITY_ENGINE->CONFIG_REG = (ALG_AES_ENC | DST_MEMORY); - SECURITY_ENGINE->CRYPTO_REG = (keyslot << 24) | 0x91E; - set_se_ctr(ctr); - - /* Handle any aligned blocks. */ - size_t aligned_size = (size_t)num_blocks << 4; - if (aligned_size) { - SECURITY_ENGINE->BLOCK_COUNT_REG = num_blocks - 1; - trigger_se_blocking_op(1, dst, dst_size, src, aligned_size); - } - - /* Handle final, unaligned block. */ - if (aligned_size < dst_size && aligned_size < src_size) { - size_t last_block_size = dst_size - aligned_size; - if (src_size < dst_size) { - last_block_size = src_size - aligned_size; - } - se_perform_aes_block_operation(dst + aligned_size, last_block_size, src + aligned_size, src - aligned_size); - } -} - -void se_aes_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int config_high) { - if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { - generic_panic(); - } - - /* Set configuration high (256-bit vs 128-bit) based on parameter. */ - SECURITY_ENGINE->CONFIG_REG = (ALG_AES_ENC | DST_MEMORY) | (config_high << 16); - SECURITY_ENGINE->CRYPTO_REG = keyslot << 24; - se_perform_aes_block_operation(dst, 0x10, src, 0x10); - -} - -void se_aes_128_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { - se_aes_ecb_encrypt_block(keyslot, dst, dst_size, src, src_size, 0); -} - -void se_aes_256_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { - se_aes_ecb_encrypt_block(keyslot, dst, dst_size, src, src_size, 0x202); -} - - -void se_aes_ecb_decrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { - if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { - generic_panic(); - } - - SECURITY_ENGINE->CONFIG_REG = (ALG_AES_DEC | DST_MEMORY); - SECURITY_ENGINE->CRYPTO_REG = keyslot << 24; - se_perform_aes_block_operation(dst, 0x10, src, 0x10); -} - -void shift_left_xor_rb(uint8_t *key) { - uint8_t prev_high_bit = 0; - for (unsigned int i = 0; i < 0x10; i++) { - uint8_t cur_byte = key[0xF - i]; - key[0xF - i] = (cur_byte << 1) | (prev_high_bit); - prev_high_bit = cur_byte >> 7; - } - if (prev_high_bit) { - key[0xF] ^= 0x87; - } -} - -void se_compute_aes_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size, unsigned int config_high) { - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - /* Generate the derived key, to be XOR'd with final output block. */ - uint8_t derived_key[0x10]; - memset(derived_key, 0, sizeof(derived_key)); - se_aes_128_ecb_encrypt_block(keyslot, derived_key, sizeof(derived_key), derived_key, sizeof(derived_key)); - shift_left_xor_rb(derived_key); - if (data_size & 0xF) { - shift_left_xor_rb(derived_key); - } - - SECURITY_ENGINE->CONFIG_REG = (ALG_AES_ENC | DST_HASHREG) | (config_high << 16); - SECURITY_ENGINE->CRYPTO_REG = (keyslot << 24) | (0x145); - clear_aes_keyslot_iv(keyslot); - - unsigned int num_blocks = (data_size + 0xF) >> 4; - /* Handle aligned blocks. */ - if (num_blocks > 1) { - SECURITY_ENGINE->BLOCK_COUNT_REG = num_blocks - 2; - trigger_se_blocking_op(1, NULL, 0, data, data_size); - SECURITY_ENGINE->CRYPTO_REG |= 0x80; - } - - /* Create final block. */ - uint8_t last_block[0x10]; - memset(last_block, 0, sizeof(last_block)); - if (data_size & 0xF) { - - memcpy(last_block, data + (data_size & ~0xF), data_size & 0xF); - last_block[data_size & 0xF] = 0x80; /* Last block = data || 100...0 */ - } else if (data_size >= 0x10) { - memcpy(last_block, data + data_size - 0x10, 0x10); - } - - for (unsigned int i = 0; i < 0x10; i++) { - last_block[i] ^= derived_key[i]; - } - - /* Perform last operation. */ - flush_dcache_range(last_block, last_block + sizeof(last_block)); - trigger_se_blocking_op(1, NULL, 0, last_block, sizeof(last_block)); - - /* Copy output CMAC. */ - for (unsigned int i = 0; i < (cmac_size >> 2); i++) { - ((uint32_t *)cmac)[i] = read32le(SECURITY_ENGINE->HASH_RESULT_REG, i << 2); - } -} - -void se_compute_aes_128_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size) { - se_compute_aes_cmac(keyslot, cmac, cmac_size, data, data_size, 0); -} -void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size) { - se_compute_aes_cmac(keyslot, cmac, cmac_size, data, data_size, 0x202); -} - -/* SHA256 Implementation. */ -void se_calculate_sha256(void *dst, const void *src, size_t src_size) { - /* Setup config for SHA256, size = BITS(src_size) */ - SECURITY_ENGINE->CONFIG_REG = (ENCMODE_SHA256 | ALG_SHA | DST_HASHREG); - SECURITY_ENGINE->SHA_CONFIG_REG = 1; - SECURITY_ENGINE->SHA_MSG_LENGTH_REG = (unsigned int)(src_size << 3); - SECURITY_ENGINE->_0x20C = 0; - SECURITY_ENGINE->_0x210 = 0; - SECURITY_ENGINE->SHA_MSG_LEFT_REG = 0; - SECURITY_ENGINE->_0x218 = (unsigned int)(src_size << 3); - SECURITY_ENGINE->_0x21C = 0; - SECURITY_ENGINE->_0x220 = 0; - SECURITY_ENGINE->_0x224 = 0; - - /* Trigger the operation. */ - trigger_se_blocking_op(1, NULL, 0, src, src_size); - - /* Copy output hash. */ - for (unsigned int i = 0; i < (0x20 >> 2); i++) { - ((uint32_t *)dst)[i] = read32be(SECURITY_ENGINE->HASH_RESULT_REG, i << 2); - } -} - -/* RNG API */ -void se_initialize_rng(unsigned int keyslot) { - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - /* To initialize the RNG, we'll perform an RNG operation into an output buffer. */ - /* This will be discarded, when done. */ - uint8_t output_buf[0x10]; - - SECURITY_ENGINE->RNG_SRC_CONFIG_REG = 3; /* Entropy enable + Entropy lock enable */ - SECURITY_ENGINE->RNG_RESEED_INTERVAL_REG = 70001; - SECURITY_ENGINE->CONFIG_REG = (ALG_RNG | DST_MEMORY); - SECURITY_ENGINE->CRYPTO_REG = (keyslot << 24) | 0x108; - SECURITY_ENGINE->RNG_CONFIG_REG = 5; - SECURITY_ENGINE->BLOCK_COUNT_REG = 0; - trigger_se_blocking_op(1, output_buf, 0x10, NULL, 0); -} - -void se_generate_random(unsigned int keyslot, void *dst, size_t size) { - if (keyslot >= KEYSLOT_AES_MAX) { - generic_panic(); - } - - uint32_t num_blocks = size >> 4; - size_t aligned_size = num_blocks << 4; - SECURITY_ENGINE->CONFIG_REG = (ALG_RNG | DST_MEMORY); - SECURITY_ENGINE->CRYPTO_REG = (keyslot << 24) | 0x108; - SECURITY_ENGINE->RNG_CONFIG_REG = 4; - - if (num_blocks >= 1) { - SECURITY_ENGINE->BLOCK_COUNT_REG = num_blocks - 1; - trigger_se_blocking_op(1, dst, aligned_size, NULL, 0); - } - if (size > aligned_size) { - se_perform_aes_block_operation(dst + aligned_size, size - aligned_size, NULL, 0); - } - -} diff --git a/exosphere/se.h b/exosphere/se.h deleted file mode 100644 index 4b5faf5..0000000 --- a/exosphere/se.h +++ /dev/null @@ -1,188 +0,0 @@ -#ifndef EXOSPHERE_SE_H -#define EXOSPHERE_SE_H - -#include -#include - -/* Exosphere driver for the Tegra X1 security engine. */ - -#define KEYSLOT_SWITCH_PACKAGE2KEY 0x8 -#define KEYSLOT_SWITCH_TEMPKEY 0x9 -#define KEYSLOT_SWITCH_SESSIONKEY 0xA -#define KEYSLOT_SWITCH_RNGKEY 0xB -#define KEYSLOT_SWITCH_MASTERKEY 0xC -#define KEYSLOT_SWITCH_DEVICEKEY 0xD - -/* This keyslot was added in 4.0.0. */ -#define KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY 0xD -#define KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY 0xE -#define KEYSLOT_SWITCH_4XOLDDEVICEKEY 0xF - -#define KEYSLOT_AES_MAX 0x10 -#define KEYSLOT_RSA_MAX 0x2 - -#define KEYSIZE_AES_MAX 0x20 -#define KEYSIZE_RSA_MAX 0x100 - -#define ALG_SHIFT (12) -#define ALG_DEC_SHIFT (8) -#define ALG_NOP (0 << ALG_SHIFT) -#define ALG_AES_ENC (1 << ALG_SHIFT) -#define ALG_AES_DEC ((1 << ALG_DEC_SHIFT) | ALG_NOP) -#define ALG_RNG (2 << ALG_SHIFT) -#define ALG_SHA (3 << ALG_SHIFT) -#define ALG_RSA (4 << ALG_SHIFT) - -#define DST_SHIFT (2) -#define DST_MEMORY (0 << DST_SHIFT) -#define DST_HASHREG (1 << DST_SHIFT) -#define DST_KEYTAB (2 << DST_SHIFT) -#define DST_SRK (3 << DST_SHIFT) -#define DST_RSAREG (4 << DST_SHIFT) - -#define ENCMODE_SHIFT (24) -#define DECMODE_SHIFT (16) -#define ENCMODE_SHA256 (5 << ENCMODE_SHIFT) - -#define HASH_DISABLE (0x0) -#define HASH_ENABLE (0x1) - -#define OP_ABORT 0 -#define OP_START 1 -#define OP_RESTART 2 -#define OP_CTX_SAVE 3 -#define OP_RESTART_IN 4 - -#define RSA_2048_BYTES 0x100 - -typedef struct security_engine { - unsigned int _0x0; - unsigned int _0x4; - unsigned int OPERATION_REG; - unsigned int INT_ENABLE_REG; - unsigned int INT_STATUS_REG; - unsigned int CONFIG_REG; - unsigned int IN_LL_ADDR_REG; - unsigned int _0x1C; - unsigned int _0x20; - unsigned int OUT_LL_ADDR_REG; - unsigned int _0x28; - unsigned int _0x2C; - unsigned char HASH_RESULT_REG[0x20]; - unsigned char _0x50[0x1B0]; - unsigned int SHA_CONFIG_REG; - unsigned int SHA_MSG_LENGTH_REG; - unsigned int _0x20C; - unsigned int _0x210; - unsigned int SHA_MSG_LEFT_REG; - unsigned int _0x218; - unsigned int _0x21C; - unsigned int _0x220; - unsigned int _0x224; - unsigned char _0x228[0x5C]; - unsigned int AES_KEY_READ_DISABLE_REG; - unsigned int AES_KEYSLOT_FLAGS[0x10]; - unsigned char _0x2C4[0x3C]; - unsigned int _0x300; - unsigned int CRYPTO_REG; - unsigned int CRYPTO_CTR_REG[4]; - unsigned int BLOCK_COUNT_REG; - unsigned int AES_KEYTABLE_ADDR; - unsigned int AES_KEYTABLE_DATA; - unsigned int _0x324; - unsigned int _0x328; - unsigned int _0x32C; - unsigned int CRYPTO_KEYTABLE_DST_REG; - unsigned char _0x334[0xC]; - unsigned int RNG_CONFIG_REG; - unsigned int RNG_SRC_CONFIG_REG; - unsigned int RNG_RESEED_INTERVAL_REG; - unsigned char _0x34C[0xB4]; - unsigned int RSA_CONFIG; - unsigned int RSA_KEY_SIZE_REG; - unsigned int RSA_EXP_SIZE_REG; - unsigned int RSA_KEY_READ_DISABLE_REG; - unsigned int RSA_KEYSLOT_FLAGS[2]; - unsigned int _0x418; - unsigned int _0x41C; - unsigned int RSA_KEYTABLE_ADDR; - unsigned int RSA_KEYTABLE_DATA; - unsigned char RSA_OUTPUT[0x100]; - unsigned char _0x528[0x2D8]; - unsigned int FLAGS_REG; - unsigned int ERR_STATUS_REG; - unsigned int _0x808; - unsigned int _0x80C; - unsigned int _0x810; - unsigned int _0x814; - unsigned int _0x818; - unsigned int _0x81C; - unsigned char _0x820[0x17E0]; -} security_engine_t; - -typedef struct { - uint32_t address; - uint32_t size; -} se_addr_info_t; - -typedef struct { - uint32_t num_entries; /* Set to total entries - 1 */ - se_addr_info_t addr_info; /* This should really be an array...but for our use case it works. */ -} se_ll_t; - -/* TODO: Define constants for the C driver. */ - - -/* WIP, API subject to change. */ - - -/* This function MUST be registered to fire on the appropriate interrupt. */ -void se_operation_completed(void); - -security_engine_t *get_security_engine_address(void); - -void se_check_for_error(void); -void se_trigger_interrupt(void); - -void se_verify_flags_cleared(void); - -void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags); -void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags); -void clear_aes_keyslot(unsigned int keyslot); -void clear_rsa_keyslot(unsigned int keyslot); - -void set_aes_keyslot(unsigned int keyslot, const void *key, size_t key_size); -void decrypt_data_into_keyslot(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size); -void set_rsa_keyslot(unsigned int keyslot, const void *modulus, size_t modulus_size, const void *exponent, size_t exp_size); -void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size); -void set_se_ctr(const void *ctr); - - -/* Insecure AES API */ -void se_aes_ctr_crypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *ctr, unsigned int (*callback)(void)); -void se_aes_cbc_encrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)); -void se_aes_cbc_decrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)); - -/* Secure AES API */ -void se_compute_aes_128_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size); -void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size); -void se_aes_128_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); -void se_aes_256_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); -void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size); -void se_aes_ecb_decrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); - -/* Hash API */ -void se_calculate_sha256(void *dst, const void *src, size_t src_size); - -/* RSA API */ -void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*callback)(void)); -void se_get_exp_mod_output(void *buf, size_t size); -void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); - -/* RNG API */ -void se_initialize_rng(unsigned int keyslot); -void se_generate_random(unsigned int keyslot, void *dst, size_t size); - -/* TODO: SE context save API. */ - -#endif /* EXOSPHERE_SE_H */ diff --git a/exosphere/sealedkeys.c b/exosphere/sealedkeys.c deleted file mode 100644 index 450915c..0000000 --- a/exosphere/sealedkeys.c +++ /dev/null @@ -1,63 +0,0 @@ -#include - -#include "utils.h" -#include "sealedkeys.h" -#include "se.h" - -const uint8_t g_titlekey_seal_key_source[0x10] = { - 0xCB, 0xB7, 0x6E, 0x38, 0xA1, 0xCB, 0x77, 0x0F, 0xB2, 0xA5, 0xB2, 0x9D, 0xD8, 0x56, 0x9F, 0x76 -}; - - -const uint8_t g_seal_key_sources[CRYPTOUSECASE_MAX][0x10] = { - {0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6}, - {0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06}, - {0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63}, - {0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D} -}; - -void seal_key_internal(void *dst, const void *src, const uint8_t *seal_key_source) { - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_SESSIONKEY, seal_key_source, 0x10); - se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10); -} - -void unseal_key_internal(unsigned int keyslot, const void *src, const uint8_t *seal_key_source) { - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_SESSIONKEY, seal_key_source, 0x10); - decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, src, 0x10); -} - - -void seal_titlekey(void *dst, size_t dst_size, const void *src, size_t src_size) { - if (dst_size != 0x10 || src_size != 0x10) { - generic_panic(); - } - - seal_key_internal(dst, src, g_titlekey_seal_key_source); - -} - -void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size) { - if (src_size != 0x10) { - generic_panic(); - } - - unseal_key_internal(keyslot, src, g_titlekey_seal_key_source); -} - - -void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int usecase) { - if (usecase >= CRYPTOUSECASE_MAX || dst_size != 0x10 || src_size != 0x10) { - generic_panic(); - } - - - seal_key_internal(dst, src, g_seal_key_sources[usecase]); -} - -void unseal_key(unsigned int keyslot, const void *src, size_t src_size, unsigned int usecase) { - if (usecase >= CRYPTOUSECASE_MAX || src_size != 0x10) { - generic_panic(); - } - - unseal_key_internal(keyslot, src, g_seal_key_sources[usecase]); -} \ No newline at end of file diff --git a/exosphere/sealedkeys.h b/exosphere/sealedkeys.h deleted file mode 100644 index 78226c7..0000000 --- a/exosphere/sealedkeys.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef EXOSPHERE_SEALED_KEYS_H -#define EXOSPHERE_SEALED_KEYS_H - -#include - -/* Key sealing/unsealing functionality. */ - -#define CRYPTOUSECASE_AES 0 -#define CRYPTOUSECASE_RSAPRIVATE 1 -#define CRYPTOUSECASE_SECUREEXPMOD 2 -#define CRYPTOUSECASE_RSAOAEP 3 - -#define CRYPTOUSECASE_MAX 4 - -void seal_titlekey(void *dst, size_t dst_size, const void *src, size_t src_size); -void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size); - -void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int usecase); -void unseal_key(unsigned int keyslot, const void *src, size_t src_size, unsigned int usecase); - -#endif \ No newline at end of file diff --git a/exosphere/smc_api.c b/exosphere/smc_api.c deleted file mode 100644 index e22fbc8..0000000 --- a/exosphere/smc_api.c +++ /dev/null @@ -1,533 +0,0 @@ -#include - -#include "utils.h" -#include "memory_map.h" - -#include "configitem.h" -#include "cpu_context.h" -#include "lock.h" -#include "masterkey.h" -#include "mc.h" -#include "memory_map.h" -#include "pmc.h" -#include "randomcache.h" -#include "sealedkeys.h" -#include "smc_api.h" -#include "smc_user.h" -#include "se.h" -#include "userpage.h" -#include "titlekey.h" - -#define SMC_USER_HANDLERS 0x13 -#define SMC_PRIV_HANDLERS 0x9 - -/* User SMC prototypes */ -uint32_t smc_set_config(smc_args_t *args); -uint32_t smc_get_config(smc_args_t *args); -uint32_t smc_check_status(smc_args_t *args); -uint32_t smc_get_result(smc_args_t *args); -uint32_t smc_exp_mod(smc_args_t *args); -uint32_t smc_get_random_bytes_for_user(smc_args_t *args); -uint32_t smc_generate_aes_kek(smc_args_t *args); -uint32_t smc_load_aes_key(smc_args_t *args); -uint32_t smc_crypt_aes(smc_args_t *args); -uint32_t smc_generate_specific_aes_key(smc_args_t *args); -uint32_t smc_compute_cmac(smc_args_t *args); -uint32_t smc_load_rsa_oaep_key(smc_args_t *args); -uint32_t smc_decrypt_rsa_private_key(smc_args_t *args); -uint32_t smc_load_secure_exp_mod_key(smc_args_t *args); -uint32_t smc_secure_exp_mod(smc_args_t *args); -uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args); -uint32_t smc_load_titlekey(smc_args_t *args); -uint32_t smc_unwrap_aes_wrapped_titlekey(smc_args_t *args); - -/* Privileged SMC prototypes */ -uint32_t smc_cpu_suspend(smc_args_t *args); -uint32_t smc_cpu_off(smc_args_t *args); -uint32_t smc_cpu_on(smc_args_t *args); -/* uint32_t smc_get_config(smc_args_t *args); */ -uint32_t smc_get_random_bytes_for_priv(smc_args_t *args); -uint32_t smc_panic(smc_args_t *args); -uint32_t smc_configure_carveout(smc_args_t *args); -uint32_t smc_read_write_register(smc_args_t *args); - -typedef struct { - uint32_t id; - uint32_t (*handler)(smc_args_t *args); -} smc_table_entry_t; - -typedef struct { - smc_table_entry_t *handlers; - uint32_t num_handlers; -} smc_table_t; - -smc_table_entry_t g_smc_user_table[SMC_USER_HANDLERS] = { - {0, NULL}, - {0xC3000401, smc_set_config}, - {0xC3000002, smc_get_config}, - {0xC3000003, smc_check_status}, - {0xC3000404, smc_get_result}, - {0xC3000E05, smc_exp_mod}, - {0xC3000006, smc_get_random_bytes_for_user}, - {0xC3000007, smc_generate_aes_kek}, - {0xC3000008, smc_load_aes_key}, - {0xC3000009, smc_crypt_aes}, - {0xC300000A, smc_generate_specific_aes_key}, - {0xC300040B, smc_compute_cmac}, - {0xC300100C, smc_load_rsa_oaep_key}, - {0xC300100D, smc_decrypt_rsa_private_key}, - {0xC300100E, smc_load_secure_exp_mod_key}, - {0xC300060F, smc_secure_exp_mod}, - {0xC3000610, smc_unwrap_rsa_oaep_wrapped_titlekey}, - {0xC3000011, smc_load_titlekey}, - {0xC3000012, smc_unwrap_aes_wrapped_titlekey} -}; - -smc_table_entry_t g_smc_priv_table[SMC_PRIV_HANDLERS] = { - {0, NULL}, - {0xC4000001, smc_cpu_suspend}, - {0x84000002, smc_cpu_off}, - {0xC4000003, smc_cpu_on}, - {0xC3000004, smc_get_config}, /* NOTE: Same function as for USER */ - {0xC3000005, smc_get_random_bytes_for_priv}, - {0xC3000006, smc_panic}, - {0xC3000007, smc_configure_carveout}, - {0xC3000008, smc_read_write_register} -}; - -smc_table_t g_smc_tables[2] = { - { /* SMC_HANDLER_USER */ - g_smc_user_table, - SMC_USER_HANDLERS - }, - { /* SMC_HANDLER_PRIV */ - g_smc_priv_table, - SMC_PRIV_HANDLERS - } -}; - -bool g_is_user_smc_in_progress = false; -bool g_is_priv_smc_in_progress = false; - -uintptr_t get_smc_core012_stack_address(void) { - return tzram_get_segment_address(TZRAM_SEGMENT_ID_CORE012_STACK) + 0x1000; -} - -uintptr_t get_exception_entry_core3_stack_address(unsigned int core_id) { - /* For core3, this is also the smc stack */ - if (core_id == 3) { - return tzram_get_segment_address(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x1000; - } - else { - return tzram_get_segment_address(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x80 * (core_id + 1); - } -} - -/* Privileged SMC lock must be available to exceptions.s. */ -void set_priv_smc_in_progress(void) { - lock_acquire(&g_is_priv_smc_in_progress); -} -void clear_priv_smc_in_progress(void) { - lock_release(&g_is_priv_smc_in_progress); -} - -uint32_t (*g_smc_callback)(void *, uint64_t) = NULL; -uint64_t g_smc_callback_key = 0; - -uint64_t try_set_smc_callback(uint32_t (*callback)(void *, uint64_t)) { - uint64_t key; - /* TODO: Atomics... */ - if (g_smc_callback_key) { - return 0; - } - - se_generate_random(KEYSLOT_SWITCH_RNGKEY, &key, sizeof(uint64_t)); - g_smc_callback_key = key; - g_smc_callback = callback; - return key; -} - -void clear_smc_callback(uint64_t key) { - /* TODO: Atomics... */ - if (g_smc_callback_key == key) { - g_smc_callback_key = 0; - } -} - -void call_smc_handler(uint32_t handler_id, smc_args_t *args) { - unsigned char smc_id; - unsigned int result; - unsigned int (*smc_handler)(smc_args_t *args); - - /* Validate top-level handler. */ - if (handler_id != SMC_HANDLER_USER && handler_id != SMC_HANDLER_PRIV) { - generic_panic(); - } - - /* Validate core is appropriate for handler. */ - if (handler_id == SMC_HANDLER_USER && get_core_id() != 3) { - /* USER SMCs must be called via svcCallSecureMonitor on core 3 (where spl runs) */ - generic_panic(); - } - - /* Validate sub-handler index */ - if ((smc_id = (unsigned char)args->X[0]) >= g_smc_tables[handler_id].num_handlers) { - generic_panic(); - } - - /* Validate sub-handler */ - if (g_smc_tables[handler_id].handlers[smc_id].id != args->X[0]) { - generic_panic(); - } - - /* Validate handler. */ - if ((smc_handler = g_smc_tables[handler_id].handlers[smc_id].handler) == NULL) { - generic_panic(); - } - - /* Call function. */ - args->X[0] = smc_handler(args); -} - -uint32_t smc_wrapper_sync(smc_args_t *args, uint32_t (*handler)(smc_args_t *)) { - uint32_t result; - if (!lock_try_acquire(&g_is_user_smc_in_progress)) { - return 3; - } - result = handler(args); - lock_release(&g_is_user_smc_in_progress); - return result; -} - -uint32_t smc_wrapper_async(smc_args_t *args, uint32_t (*handler)(smc_args_t *), uint32_t (*callback)(void *, uint64_t)) { - uint32_t result; - uint64_t key; - if (!lock_try_acquire(&g_is_user_smc_in_progress)) { - return 3; - } - if ((key = try_set_smc_callback(callback)) != 0) { - result = handler(args); - if (result == 0) { - /* Pass the status check key back to userland. */ - args->X[1] = key; - /* Early return, leaving g_is_user_smc_in_progress locked */ - return result; - } else { - /* No status to check. */ - clear_smc_callback(key); - } - } else { - /* smcCheckStatus needs to be called. */ - result = 3; - } - lock_release(&g_is_user_smc_in_progress); - return result; -} - -uint32_t smc_set_config(smc_args_t *args) { - /* Actual value presumed in X3 on hardware. */ - return configitem_set((enum ConfigItem)args->X[1], args->X[3]); -} - -uint32_t smc_get_config(smc_args_t *args) { - uint64_t out_item = 0; - uint32_t result; - result = configitem_get((enum ConfigItem)args->X[1], &out_item); - args->X[1] = out_item; - return result; -} - -uint32_t smc_check_status(smc_args_t *args) { - if (g_smc_callback_key == 0) { - return 4; - } - - if (args->X[1] != g_smc_callback_key) { - return 5; - } - - args->X[1] = g_smc_callback(NULL, 0); - - g_smc_callback_key = 0; - return 0; -} - -uint32_t smc_get_result(smc_args_t *args) { - uint32_t status; - unsigned char result_buf[0x400]; - upage_ref_t page_ref; - - void *user_address = (void *)args->X[2]; - - if (g_smc_callback_key == 0) { - return 4; - } - - if (args->X[1] != g_smc_callback_key) { - return 5; - } - - /* Check result size */ - if (args->X[3] > 0x400) { - return 2; - } - - args->X[1] = g_smc_callback(result_buf, args->X[3]); - g_smc_callback_key = 0; - - /* Initialize page reference. */ - if (upage_init(&page_ref, user_address) == 0) { - return 2; - } - - /* Copy result output back to user. */ - if (secure_copy_to_user(&page_ref, user_address, result_buf, (size_t)args->X[3]) == 0) { - return 2; - } - - return 0; -} - -uint32_t smc_exp_mod_get_result(void *buf, uint64_t size) { - if (get_exp_mod_done() != 1) { - return 3; - } - - if (size != 0x100) { - return 2; - } - - se_get_exp_mod_output(buf, 0x100); - - /* smc_exp_mod is done now. */ - lock_release(&g_is_user_smc_in_progress); - return 0; -} - -uint32_t smc_exp_mod(smc_args_t *args) { - return smc_wrapper_async(args, user_exp_mod, smc_exp_mod_get_result); -} - -uint32_t smc_get_random_bytes_for_user(smc_args_t *args) { - return smc_wrapper_sync(args, user_get_random_bytes); -} - -uint32_t smc_generate_aes_kek(smc_args_t *args) { - return smc_wrapper_sync(args, user_generate_aes_kek); -} - -uint32_t smc_load_aes_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_load_aes_key); -} - -uint32_t smc_crypt_aes_status_check(void *buf, uint64_t size) { - /* Buf and size are unused. */ - if (get_crypt_aes_done() != 1) { - return 3; - } - /* smc_crypt_aes is done now. */ - lock_release(&g_is_user_smc_in_progress); - return 0; -} - -uint32_t smc_crypt_aes(smc_args_t *args) { - return smc_wrapper_async(args, user_crypt_aes, smc_crypt_aes_status_check); -} - -uint32_t smc_generate_specific_aes_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_generate_specific_aes_key); -} - -uint32_t smc_compute_cmac(smc_args_t *args) { - return smc_wrapper_sync(args, user_compute_cmac); -} - -uint32_t smc_load_rsa_oaep_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_load_rsa_oaep_key); -} - -uint32_t smc_decrypt_rsa_private_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_decrypt_rsa_private_key); -} - -uint32_t smc_load_secure_exp_mod_key(smc_args_t *args) { - return smc_wrapper_sync(args, user_load_secure_exp_mod_key); -} - -uint32_t smc_secure_exp_mod(smc_args_t *args) { - return smc_wrapper_async(args, user_secure_exp_mod, smc_exp_mod_get_result); -} - -uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey_get_result(void *buf, uint64_t size) { - uint64_t *p_sealed_key = (uint64_t *)buf; - uint8_t rsa_wrapped_titlekey[0x100]; - uint8_t aes_wrapped_titlekey[0x10]; - uint8_t titlekey[0x10]; - uint64_t sealed_titlekey[2]; - if (get_exp_mod_done() != 1) { - return 3; - } - - if (size != 0x10) { - return 2; - } - - se_get_exp_mod_output(rsa_wrapped_titlekey, 0x100); - if (tkey_rsa_oaep_unwrap(aes_wrapped_titlekey, 0x10, rsa_wrapped_titlekey, 0x100) != 0x10) { - /* Failed to extract RSA OAEP wrapped key. */ - lock_release(&g_is_user_smc_in_progress); - return 2; - } - - tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10); - seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10); - - p_sealed_key[0] = sealed_titlekey[0]; - p_sealed_key[1] = sealed_titlekey[1]; - - /* smc_unwrap_rsa_oaep_wrapped_titlekey is done now. */ - lock_release(&g_is_user_smc_in_progress); - return 0; -} - -uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { - return smc_wrapper_async(args, user_unwrap_rsa_oaep_wrapped_titlekey, smc_unwrap_rsa_oaep_wrapped_titlekey_get_result); -} - -uint32_t smc_load_titlekey(smc_args_t *args) { - return smc_wrapper_sync(args, user_load_titlekey); -} - -uint32_t smc_unwrap_aes_wrapped_titlekey(smc_args_t *args) { - return smc_wrapper_sync(args, user_unwrap_aes_wrapped_titlekey); -} - -uint32_t smc_cpu_on(smc_args_t *args) { - return cpu_on((uint32_t)args->X[1], args->X[2], args->X[3]); -} - -uint32_t smc_cpu_off(smc_args_t *args) { - return cpu_off(); -} - -/* Wrapper for cpu_suspend */ -uint32_t cpu_suspend_wrapper(smc_args_t *args) { - return cpu_suspend(args->X[1], args->X[2], args->X[3]); -} - -uint32_t smc_cpu_suspend(smc_args_t *args) { - return smc_wrapper_sync(args, cpu_suspend_wrapper); -} - -uint32_t smc_get_random_bytes_for_priv(smc_args_t *args) { - /* This is an interesting SMC. */ - /* The kernel must NEVER be unable to get random bytes, if it needs them */ - /* As such: */ - - uint32_t result; - - if (!lock_try_acquire(&g_is_user_smc_in_progress)) { - if (args->X[1] > 0x38) { - return 2; - } - /* Retrieve bytes from the cache. */ - size_t num_bytes = (size_t)args->X[1]; - randomcache_getbytes(&args->X[1], num_bytes); - result = 0; - } else { - /* If the kernel isn't denied service by a usermode SMC, generate fresh random bytes. */ - result = user_get_random_bytes(args); - /* Also, refill our cache while we have the chance in case we get denied later. */ - randomcache_refill(); - lock_release(&g_is_user_smc_in_progress); - } - return result; -} - -uint32_t smc_read_write_register(smc_args_t *args) { - uint64_t address = args->X[1]; - uint32_t mask = (uint32_t)(args->X[2]); - uint32_t value = (uint32_t)(args->X[3]); - volatile uint32_t *p_mmio = NULL; - /* Address must be aligned. */ - if (address & 3) { - return 2; - } - /* Check for PMC registers. */ - if (0x7000E400ULL <= address && address <= 0x7000EFFFULL) { - const uint8_t pmc_whitelist[0x28] = {0xB9, 0xF9, 0x07, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0xC4, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00}; - /* Offset = Address - PMC_BASE */ - uint32_t offset = (uint32_t)(address - 0x7000E400ULL); - uint32_t wl_ind = (offset >> 5); - /* If address is whitelisted, allow write. */ - if (wl_ind < sizeof(pmc_whitelist) && (pmc_whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7)))) { - p_mmio = (volatile uint32_t *)(PMC_BASE + offset); - } else { - return 2; - } - } else if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT && devices[MMIO_DEVID_MC].pa <= address && address < devices[MMIO_DEVID_MC].pa + devices[MMIO_DEVID_MC].size) { - /* Memory Controller RW supported only on 4.0.0+ */ - const uint8_t mc_whitelist[0x68] = {0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01, 0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x03, 0x40, 0x73, 0x3E, 0x2F, 0x00, 0x00, 0x6E, 0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04, 0x80, 0x1F, 0x08, 0x80, 0x03, 0x00, 0x0E, 0x00, 0x08, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F, 0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00}; - uint32_t offset = (uint32_t)(address - 0x70019000ULL); - uint32_t wl_ind = (offset >> 5); - /* If address is whitelisted, allow write. */ - if (wl_ind < sizeof(mc_whitelist) && (mc_whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7)))) { - p_mmio = (volatile uint32_t *)(mmio_get_device_address(MMIO_DEVID_MC) + offset); - } else { - /* These addresses are not allowed by the whitelist. */ - /* They correspond to SMMU DISABLE for the BPMP, and for APB-DMA. */ - /* However, smcReadWriteRegister returns 0 for these addresses despite not actually performing the write. */ - /* This is "probably" to fuck with hackers who got access to smcReadWriteRegister and are trying to get */ - /* control of the BPMP for jamais vu etc., since there's no other reason to return 0 despite failure. */ - if (address == 0x7001923C || address == 0x70019298) { - return 0; - } - return 2; - } - } - - /* Perform actual write. */ - if (p_mmio != NULL) { - uint32_t old_value; - /* Write whole value. */ - if (mask == 0xFFFFFFFF) { - old_value = 0; - } else { - old_value = *p_mmio; - } - if (mask) { - *p_mmio = (old_value & ~mask) | (value & mask); - } - /* Return old value. */ - args->X[1] = old_value; - return 0; - } - - return 2; -} - - -uint32_t smc_configure_carveout(smc_args_t *args) { - if (args->X[0] > 1) { - return 2; - } - - unsigned int carveout_id = (unsigned int)args->X[1]; - uint64_t address = args->X[2]; - uint64_t size = args->X[3]; - - /* Ensure carveout isn't too big. */ - if (size > KERNEL_CARVEOUT_SIZE_MAX) { - return 2; - } - - /* Configuration is one-shot, and cannot be done multiple times. */ - static bool configured_carveouts[2] = {false, false}; - if (configured_carveouts[carveout_id]) { - return 2; - } - - configure_kernel_carveout(carveout_id + 4, address, size); - configured_carveouts[carveout_id] = true; - return 0; -} diff --git a/exosphere/smc_api.h b/exosphere/smc_api.h deleted file mode 100644 index d006e65..0000000 --- a/exosphere/smc_api.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef EXOSPHERE_SMC_API_H -#define EXOSPHERE_SMC_API_H - -#include - -#define SMC_HANDLER_USER 0 -#define SMC_HANDLER_PRIV 1 - -typedef struct { - uint64_t X[8]; -} smc_args_t; - -void set_priv_smc_in_progress(void); -void clear_priv_smc_in_progress(void); - -uintptr_t get_smc_core012_stack_address(void); -uintptr_t get_exception_entry_core3_stack_address(void); - -void call_smc_handler(unsigned int handler_id, smc_args_t *args); - -#endif diff --git a/exosphere/smc_user.c b/exosphere/smc_user.c deleted file mode 100644 index 51cb1dd..0000000 --- a/exosphere/smc_user.c +++ /dev/null @@ -1,615 +0,0 @@ -#include -#include -#include - -#include "utils.h" -#include "cache.h" -#include "configitem.h" -#include "gcm.h" -#include "masterkey.h" -#include "smc_api.h" -#include "smc_user.h" -#include "se.h" -#include "sealedkeys.h" -#include "userpage.h" -#include "titlekey.h" - -/* Globals. */ -bool g_crypt_aes_done = false; -bool g_exp_mod_done = false; - -uint8_t g_secure_exp_mod_exponent[0x100]; -uint8_t g_rsa_oaep_exponent[0x100]; - - -void set_exp_mod_done(bool done) { - g_exp_mod_done = done; -} - -bool get_exp_mod_done(void) { - return g_exp_mod_done; -} - -uint32_t exp_mod_done_handler(void) { - set_exp_mod_done(true); - - se_trigger_interrupt(); - - return 0; -} - -uint32_t user_exp_mod(smc_args_t *args) { - uint8_t modulus[0x100]; - uint8_t exponent[0x100]; - uint8_t input[0x100]; - - upage_ref_t page_ref; - - /* Validate size. */ - if (args->X[4] == 0 || args->X[4] > 0x100 || (args->X[4] & 3) != 0) { - return 2; - } - - size_t exponent_size = (size_t)args->X[4]; - - void *user_input = (void *)args->X[1]; - void *user_exponent = (void *)args->X[2]; - void *user_modulus = (void *)args->X[3]; - - /* Copy user data into secure memory. */ - if (upage_init(&page_ref, user_input) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, input, user_input, 0x100) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, exponent, user_exponent, exponent_size) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { - return 2; - } - - set_exp_mod_done(false); - /* Hardcode RSA keyslot 0. */ - set_rsa_keyslot(0, modulus, 0x100, exponent, exponent_size); - se_exp_mod(0, input, 0x100, exp_mod_done_handler); - - return 0; -} - -uint32_t user_get_random_bytes(smc_args_t *args) { - uint8_t random_bytes[0x40]; - if (args->X[1] > 0x38) { - return 2; - } - - size_t size = (size_t)args->X[1]; - - flush_dcache_range(random_bytes, random_bytes + size); - se_generate_random(KEYSLOT_SWITCH_RNGKEY, random_bytes, size); - flush_dcache_range(random_bytes, random_bytes + size); - - memcpy(&args->X[1], random_bytes, size); - return 0; -} - -uint32_t user_generate_aes_kek(smc_args_t *args) { - uint64_t wrapped_kek[2]; - uint8_t kek_source[0x10]; - uint64_t kek[2]; - uint64_t sealed_kek[2]; - - wrapped_kek[0] = args->X[1]; - wrapped_kek[1] = args->X[2]; - - unsigned int master_key_rev = (unsigned int)args->X[3]; - - if (master_key_rev > 0) { - master_key_rev -= 1; /* GenerateAesKek offsets by one. */ - } - - if (master_key_rev >= MASTERKEY_REVISION_MAX) { - return 2; - } - - uint64_t packed_options = args->X[4]; - if (packed_options > 0xFF) { - return 2; - } - - /* Switched the output based on how the system was booted. */ - uint8_t mask_id = (uint8_t)((packed_options >> 1) & 3); - - /* Switches the output based on how it will be used. */ - uint8_t usecase = (uint8_t)((packed_options >> 5) & 3); - - /* Switched the output based on whether it should be console unique. */ - bool is_personalized = (int)(packed_options & 1); - - bool is_recovery_boot = configitem_is_recovery_boot(); - - /* Mask 2 is only allowed when booted from recovery. */ - if (mask_id == 2 && !is_recovery_boot) { - return 2; - } - /* Mask 1 is only allowed when booted normally. */ - if (mask_id == 1 && is_recovery_boot) { - return 2; - } - - /* Masks 0, 3 are allowed all the time. */ - - const uint8_t kek_seeds[4][0x10] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74}, - {0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C}, - {0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB} - }; - const uint8_t kek_masks[4][0x10] = { - {0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9}, - {0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77}, - {0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81}, - {0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B} - }; - - /* Create kek source. */ - for (unsigned int i = 0; i < 0x10; i++) { - kek_source[i] = kek_seeds[usecase][i] ^ kek_masks[mask_id][i]; - } - - unsigned int keyslot; - if (is_personalized) { - /* Behavior changed in 4.0.0. */ - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { - if (master_key_rev >= 1) { - keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ - } else { - keyslot = KEYSLOT_SWITCH_4XOLDDEVICEKEY; /* Old device key, 4.x. */ - } - } else { - keyslot = KEYSLOT_SWITCH_DEVICEKEY; - } - } else { - keyslot = mkey_get_keyslot(master_key_rev); - } - - /* Derive kek. */ - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, kek_source, 0x10); - se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, kek, 0x10, wrapped_kek, 0x10); - - - /* Seal kek. */ - seal_key(sealed_kek, 0x10, kek, 0x10, usecase); - - args->X[1] = sealed_kek[0]; - args->X[2] = sealed_kek[1]; - - return 0; -} - -uint32_t user_load_aes_key(smc_args_t *args) { - uint64_t sealed_kek[2]; - uint64_t wrapped_key[2]; - - uint32_t keyslot = (uint32_t)args->X[1]; - if (keyslot > 3) { - return 2; - } - - /* Copy keydata */ - sealed_kek[0] = args->X[2]; - sealed_kek[1] = args->X[3]; - wrapped_key[0] = args->X[4]; - wrapped_key[1] = args->X[5]; - - /* Unseal the kek. */ - unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10, CRYPTOUSECASE_AES); - - /* Unwrap the key. */ - decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, 0x10); - return 0; -} - - -void set_crypt_aes_done(bool done) { - g_crypt_aes_done = done; -} - -bool get_crypt_aes_done(void) { - return g_crypt_aes_done; -} - -uint32_t crypt_aes_done_handler(void) { - se_check_for_error(); - - set_crypt_aes_done(true); - - se_trigger_interrupt(); - - return 0; -} - -uint32_t user_crypt_aes(smc_args_t *args) { - uint32_t keyslot = args->X[1] & 3; - uint32_t mode = (args->X[1] >> 4) & 3; - - uint64_t iv_ctr[2]; - iv_ctr[0] = args->X[2]; - iv_ctr[1] = args->X[3]; - - uint32_t in_ll_paddr = (uint32_t)(args->X[4]); - uint32_t out_ll_paddr = (uint32_t)(args->X[5]); - - size_t size = args->X[6]; - if (size & 0xF) { - generic_panic(); - } - - set_crypt_aes_done(false); - - uint64_t result = 0; - switch (mode) { - case 0: /* CBC Encryption */ - se_aes_cbc_encrypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler); - result = 0; - break; - case 1: /* CBC Decryption */ - se_aes_cbc_decrypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler); - result = 0; - break; - case 2: /* CTR "Encryption" */ - se_aes_ctr_crypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler); - result = 0; - break; - case 3: - default: - result = 1; - break; - } - - return result; -} - -uint32_t user_generate_specific_aes_key(smc_args_t *args) { - uint64_t wrapped_key[2]; - uint8_t key[0x10]; - unsigned int master_key_rev; - bool should_mask; - - wrapped_key[0] = args->X[1]; - wrapped_key[1] = args->X[2]; - if (args->X[4] > MASTERKEY_REVISION_MAX) { - return 2; - } - master_key_rev = (unsigned int)(args->X[4]); - if (args->X[3] > 1) { - return 2; - } - should_mask = args->X[3] != 0; - - unsigned int keyslot; - - /* Behavior changed in 4.0.0. */ - if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { - if (master_key_rev >= 2) { - keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ - } else { - keyslot = KEYSLOT_SWITCH_4XOLDDEVICEKEY; /* Old device key, 4.x. */ - } - } else { - keyslot = KEYSLOT_SWITCH_DEVICEKEY; - } - - if (fuse_get_bootrom_patch_version() < 0x7F) { - /* On dev units, use a fixed "all-zeroes" seed. */ - /* Yes, this data really is all-zero in actual TrustZone .rodata. */ - uint8_t dev_specific_aes_key_source[0x10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t dev_specific_aes_key_ctr[0x10] = {0x3C, 0xD5, 0x92, 0xEC, 0x68, 0x31, 0x4A, 0x06, 0xD4, 0x1B, 0x0C, 0xD9, 0xF6, 0x2E, 0xD9, 0xE9}; - uint8_t dev_specific_aes_key_mask[0x10] = {0xAC, 0xCA, 0x9A, 0xCA, 0xFF, 0x2E, 0xB9, 0x22, 0xCC, 0x1F, 0x4F, 0xAD, 0xDD, 0x77, 0x21, 0x1E}; - - flush_dcache_range(key, key + 0x10); - se_aes_ctr_crypt(keyslot, key, 0x10, dev_specific_aes_key_source, 0x10, dev_specific_aes_key_ctr, 0x10); - flush_dcache_range(key, key + 0x10); - - if (should_mask) { - for (unsigned int i = 0; i < 0x10; i++) { - key[i] ^= dev_specific_aes_key_mask[i]; - } - } - } else { - /* On retail, standard kek->key decryption. */ - uint8_t retail_specific_aes_key_source[0x10] = {0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95}; - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, retail_specific_aes_key_source, 0x10); - se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, key, 0x10, wrapped_key, 0x10); - } - - - args->X[1] = key[0]; - args->X[2] = key[1]; - return 0; -} - -uint32_t user_compute_cmac(smc_args_t *args) { - uint32_t keyslot = (uint32_t)args->X[1]; - void *user_address = (void *)args->X[2]; - size_t size = (size_t)args->X[3]; - - uint8_t user_data[0x400]; - uint64_t result_cmac[2]; - upage_ref_t page_ref; - - /* Validate keyslot and size. */ - if (keyslot > 3 || args->X[3] > 0x400) { - return 2; - } - - if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { - return 2; - } - - flush_dcache_range(user_data, user_data + size); - se_compute_aes_128_cmac(keyslot, result_cmac, 0x10, user_data, size); - - /* Copy CMAC out. */ - args->X[1] = result_cmac[0]; - args->X[2] = result_cmac[1]; - - return 0; -} - -uint32_t user_load_rsa_oaep_key(smc_args_t *args) { - uint64_t sealed_kek[2]; - uint64_t wrapped_key[2]; - bool is_personalized; - - uint8_t user_data[0x400]; - void *user_address; - size_t size; - upage_ref_t page_ref; - - - /* Copy keydata */ - sealed_kek[0] = args->X[1]; - sealed_kek[1] = args->X[2]; - if (args->X[3] > 1) { - return 2; - } - is_personalized = args->X[3] != 0; - user_address = (void *)args->X[4]; - size = (size_t)args->X[5]; - wrapped_key[0] = args->X[6]; - wrapped_key[1] = args->X[7]; - - if (is_personalized && size != 0x240) { - return 2; - } - if (!is_personalized && (size != 0x220 || fuse_get_bootrom_patch_version() >= 0x7F)) { - return 2; - } - - if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { - return 2; - } - - /* Ensure that our private key is 0x100 bytes. */ - if (gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAOAEP, is_personalized) < 0x100) { - return 2; - } - - memcpy(g_rsa_oaep_exponent, user_data, 0x100); - return 0; -} - -uint32_t user_decrypt_rsa_private_key(smc_args_t *args) { - uint64_t sealed_kek[2]; - uint64_t wrapped_key[2]; - bool is_personalized; - - uint8_t user_data[0x400]; - void *user_address; - size_t size; - upage_ref_t page_ref; - - - /* Copy keydata */ - sealed_kek[0] = args->X[1]; - sealed_kek[1] = args->X[2]; - if (args->X[3] > 1) { - return 2; - } - is_personalized = args->X[3] != 0; - user_address = (void *)args->X[4]; - size = (size_t)args->X[5]; - wrapped_key[0] = args->X[6]; - wrapped_key[1] = args->X[7]; - - if (size > 0x240) { - return 2; - } - - if (is_personalized && size < 0x31) { - return 2; - } - if (!is_personalized && (size < 0x11 || fuse_get_bootrom_patch_version() >= 0x7F)) { - return 2; - } - - if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { - return 2; - } - - size_t out_size; - - if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAPRIVATE, is_personalized)) == 0) { - return 2; - } - - if (secure_copy_to_user(&page_ref, user_address, user_data, size) == 0) { - return 2; - } - - args->X[1] = out_size; - return 0; -} - -uint32_t user_load_secure_exp_mod_key(smc_args_t *args) { - uint64_t sealed_kek[2]; - uint64_t wrapped_key[2]; - bool is_personalized; - - uint8_t user_data[0x400]; - void *user_address; - size_t size; - upage_ref_t page_ref; - - - /* Copy keydata */ - sealed_kek[0] = args->X[1]; - sealed_kek[1] = args->X[2]; - if (args->X[3] > 1) { - return 2; - } - is_personalized = args->X[3] != 0; - user_address = (void *)args->X[4]; - size = (size_t)args->X[5]; - wrapped_key[0] = args->X[6]; - wrapped_key[1] = args->X[7]; - - if (is_personalized && size != 0x130) { - return 2; - } - if (!is_personalized && (size != 0x110 || fuse_get_bootrom_patch_version() >= 0x7F)) { - return 2; - } - - if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { - return 2; - } - - size_t out_size; - - /* Ensure that our key is non-zero bytes. */ - if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_SECUREEXPMOD, is_personalized)) == 0) { - return 2; - } - - /* Copy key to global. */ - if (out_size <= 0x100) { - memcpy(g_secure_exp_mod_exponent, user_data, out_size); - } else { - memcpy(g_secure_exp_mod_exponent, user_data, 0x100); - } - - return 0; -} - -uint32_t user_secure_exp_mod(smc_args_t *args) { - uint8_t modulus[0x100]; - uint8_t input[0x100]; - - upage_ref_t page_ref; - - void *user_input = (void *)args->X[1]; - void *user_modulus = (void *)args->X[2]; - - /* Copy user data into secure memory. */ - if (upage_init(&page_ref, user_input) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, input, user_input, 0x100) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { - return 2; - } - - set_exp_mod_done(false); - /* Hardcode RSA keyslot 0. */ - set_rsa_keyslot(0, modulus, 0x100, g_secure_exp_mod_exponent, 0x100); - se_exp_mod(0, input, 0x100, exp_mod_done_handler); - - return 0; -} - -uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { - uint8_t modulus[0x100]; - uint8_t wrapped_key[0x100]; - - upage_ref_t page_ref; - - void *user_wrapped_key = (void *)args->X[1]; - void *user_modulus = (void *)args->X[2]; - unsigned int master_key_rev = (unsigned int)args->X[7]; - - if (master_key_rev >= MASTERKEY_REVISION_MAX) { - return 2; - } - - /* Copy user data into secure memory. */ - if (upage_init(&page_ref, user_wrapped_key) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, wrapped_key, user_wrapped_key, 0x100) == 0) { - return 2; - } - if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { - return 2; - } - - set_exp_mod_done(false); - - /* Expected label_hash occupies args->X[3] to args->X[6]. */ - tkey_set_expected_label_hash(&args->X[3]); - - tkey_set_master_key_rev(master_key_rev); - - /* Hardcode RSA keyslot 0. */ - set_rsa_keyslot(0, modulus, 0x100, g_rsa_oaep_exponent, 0x100); - se_exp_mod(0, wrapped_key, 0x100, exp_mod_done_handler); - - return 0; - -} - -uint32_t user_load_titlekey(smc_args_t *args) { - uint64_t sealed_titlekey[2]; - - uint32_t keyslot = (uint32_t)args->X[1]; - if (keyslot > 3) { - return 2; - } - - /* Copy keydata */ - sealed_titlekey[0] = args->X[2]; - sealed_titlekey[1] = args->X[3]; - - /* Unseal the key. */ - unseal_titlekey(keyslot, sealed_titlekey, 0x10); - return 0; - -} - -uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args) { - uint64_t aes_wrapped_titlekey[2]; - uint8_t titlekey[0x10]; - uint64_t sealed_titlekey[2]; - - aes_wrapped_titlekey[0] = args->X[1]; - aes_wrapped_titlekey[1] = args->X[2]; - unsigned int master_key_rev = (unsigned int)args->X[3]; - - - if (master_key_rev >= MASTERKEY_REVISION_MAX) { - return 2; - } - - tkey_set_master_key_rev(master_key_rev); - - - tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10); - seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10); - - args->X[1] = sealed_titlekey[0]; - args->X[2] = sealed_titlekey[1]; -} diff --git a/exosphere/smc_user.h b/exosphere/smc_user.h deleted file mode 100644 index 58deda4..0000000 --- a/exosphere/smc_user.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef EXOSPHERE_SMC_USER_H -#define EXOSPHERE_SMC_USER_H - -#include "smc_api.h" - -uint32_t user_exp_mod(smc_args_t *args); -uint32_t user_get_random_bytes(smc_args_t *args); -uint32_t user_generate_aes_kek(smc_args_t *args); -uint32_t user_load_aes_key(smc_args_t *args); -uint32_t user_crypt_aes(smc_args_t *args); -uint32_t user_generate_specific_aes_key(smc_args_t *args); -uint32_t user_compute_cmac(smc_args_t *args); -uint32_t user_load_rsa_oaep_key(smc_args_t *args); -uint32_t user_decrypt_rsa_private_key(smc_args_t *args); -uint32_t user_load_secure_exp_mod_key(smc_args_t *args); -uint32_t user_secure_exp_mod(smc_args_t *args); -uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args); -uint32_t user_load_titlekey(smc_args_t *args); -uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args); - - -void set_crypt_aes_done(bool done); -bool get_crypt_aes_done(void); - -void set_exp_mod_done(bool done); -bool get_exp_mod_done(void); - -#endif \ No newline at end of file diff --git a/exosphere/src/bootconfig.c b/exosphere/src/bootconfig.c new file mode 100644 index 0000000..5893273 --- /dev/null +++ b/exosphere/src/bootconfig.c @@ -0,0 +1,40 @@ +#include "bootconfig.h" + +void bootconfig_load_and_verify(const bootconfig_t *bootconfig) { + /* TODO */ +} + +void bootconfig_clear(void){ + /* TODO */ +} + +/* Actual configuration getters. */ +bool bootconfig_is_package2_plaintext(void) { + return false; + /* TODO */ +} + +bool bootconfig_is_package2_unsigned(void) { + return false; + /* TODO */ +} + +bool bootconfig_disable_program_verification(void) { + return false; + /* TODO */ +} + +bool bootconfig_is_debug_mode(void) { + return false; + /* TODO */ +} + +uint64_t bootconfig_get_memory_arrangement(void) { + return 0ULL; + /* TODO */ +} + +uint64_t bootconfig_get_kernel_memory_configuration(void) { + return 0ULL; + /* TODO */ +} diff --git a/exosphere/src/bootconfig.h b/exosphere/src/bootconfig.h new file mode 100644 index 0000000..5fda115 --- /dev/null +++ b/exosphere/src/bootconfig.h @@ -0,0 +1,28 @@ +#ifndef EXOSPHERE_BOOTCONFIG_H +#define EXOSPHERE_BOOTCONFIG_H + +#include +#include + +/* This provides management for Switch BootConfig. */ + +typedef struct { + uint8_t unsigned_config[0x200]; + uint8_t signature[0x100]; + uint8_t signed_config[0x100]; + uint8_t unknown_config[0x240]; +} bootconfig_t; + +void bootconfig_load_and_verify(const bootconfig_t *bootconfig); +void bootconfig_clear(void); + +/* Actual configuration getters. */ +bool bootconfig_is_package2_plaintext(void); +bool bootconfig_is_package2_unsigned(void); +bool bootconfig_disable_program_verification(void); +bool bootconfig_is_debug_mode(void); + +uint64_t bootconfig_get_memory_arrangement(void); +uint64_t bootconfig_get_kernel_memory_configuration(void); + +#endif \ No newline at end of file diff --git a/exosphere/src/cache.h b/exosphere/src/cache.h new file mode 100644 index 0000000..e8f8c4a --- /dev/null +++ b/exosphere/src/cache.h @@ -0,0 +1,20 @@ +#ifndef EXOSPHERE_CACHE_H +#define EXOSPHERE_CACHE_H + +#include + +void tlb_invalidate_all(void); +void tlb_invalidate_all_inner_shareable(void); + +void tlb_invalidate_page(const volatile void *page); +void tlb_invalidate_page_inner_shareable(const void *page); + +void flush_dcache_all(void); +void invalidate_dcache_all(void); + +void flush_dcache_range(const void *start, const void *end); +void invalidate_dcache_range(const void *start, const void *end); + +void invalidate_icache_inner_shareable(void); + +#endif diff --git a/exosphere/src/cache.s b/exosphere/src/cache.s new file mode 100644 index 0000000..128afe9 --- /dev/null +++ b/exosphere/src/cache.s @@ -0,0 +1,229 @@ +.section .text.tlb_invalidate_all, "ax", %progbits +.type tlb_invalidate_all, %function +.global tlb_invalidate_all +tlb_invalidate_all: + dsb sy + tlbi alle3 + dsb sy + isb + ret + +.section .text.tlb_invalidate_all_inner_shareable, "ax", %progbits +.type tlb_invalidate_all_inner_shareable, %function +.global tlb_invalidate_all_inner_shareable +tlb_invalidate_all_inner_shareable: + dsb ish + tlbi alle3is + dsb ish + isb + ret + +.section .text.tlb_invalidate_page, "ax", %progbits +.type tlb_invalidate_page, %function +.global tlb_invalidate_page +tlb_invalidate_page: + lsr x8, x0, #12 + dsb sy + tlbi vale3, x8 + dsb sy + isb + ret + +.section .text.tlb_invalidate_page_inner_shareable, "ax", %progbits +.type tlb_invalidate_page_inner_shareable, %function +.global tlb_invalidate_page_inner_shareable +tlb_invalidate_page_inner_shareable: + lsr x8, x0, #12 + dsb ish + tlbi vale3is, x8 + dsb ish + isb + ret + +/* The following functions are taken/adapted from https://github.com/u-boot/u-boot/blob/master/arch/arm/cpu/armv8/cache.S */ + +/* + * (C) Copyright 2013 + * David Feng + * + * This file is based on sample code from ARMv8 ARM. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * void __asm_dcache_level(level) + * + * flush or invalidate one level cache. + * + * x0: cache level + * x1: 0 clean & invalidate, 1 invalidate only + * x2~x9: clobbered + */ +.section .text.__asm_dcache_level, "ax", %progbits +.type __asm_dcache_level, %function +__asm_dcache_level: + lsl x12, x0, #1 + msr csselr_el1, x12 /* select cache level */ + isb /* sync change of cssidr_el1 */ + mrs x6, ccsidr_el1 /* read the new cssidr_el1 */ + and x2, x6, #7 /* x2 <- log2(cache line size)-4 */ + add x2, x2, #4 /* x2 <- log2(cache line size) */ + mov x3, #0x3ff + and x3, x3, x6, lsr #3 /* x3 <- max number of #ways */ + clz w5, w3 /* bit position of #ways */ + mov x4, #0x7fff + and x4, x4, x6, lsr #13 /* x4 <- max number of #sets */ + /* x12 <- cache level << 1 */ + /* x2 <- line length offset */ + /* x3 <- number of cache ways - 1 */ + /* x4 <- number of cache sets - 1 */ + /* x5 <- bit position of #ways */ + +loop_set: + mov x6, x3 /* x6 <- working copy of #ways */ +loop_way: + lsl x7, x6, x5 + orr x9, x12, x7 /* map way and level to cisw value */ + lsl x7, x4, x2 + orr x9, x9, x7 /* map set number to cisw value */ + tbz w1, #0, 1f + dc isw, x9 + b 2f +1: dc cisw, x9 /* clean & invalidate by set/way */ +2: subs x6, x6, #1 /* decrement the way */ + b.ge loop_way + subs x4, x4, #1 /* decrement the set */ + b.ge loop_set + + ret + +/* + * void __asm_flush_dcache_all(int invalidate_only) + * + * x0: 0 clean & invalidate, 1 invalidate only + * + * flush or invalidate all data cache by SET/WAY. + */ +.section .text.__asm_dcache_all, "ax", %progbits +.type __asm_dcache_all, %function +__asm_dcache_all: + mov x1, x0 + dsb sy + mrs x10, clidr_el1 /* read clidr_el1 */ + lsr x11, x10, #24 + and x11, x11, #0x7 /* x11 <- loc */ + cbz x11, finished /* if loc is 0, exit */ + mov x15, lr + mov x0, #0 /* start flush at cache level 0 */ + /* x0 <- cache level */ + /* x10 <- clidr_el1 */ + /* x11 <- loc */ + /* x15 <- return address */ + +loop_level: + lsl x12, x0, #1 + add x12, x12, x0 /* x0 <- tripled cache level */ + lsr x12, x10, x12 + and x12, x12, #7 /* x12 <- cache type */ + cmp x12, #2 + b.lt skip /* skip if no cache or icache */ + bl __asm_dcache_level /* x1 = 0 flush, 1 invalidate */ +skip: + add x0, x0, #1 /* increment cache level */ + cmp x11, x0 + b.gt loop_level + + mov x0, #0 + msr csselr_el1, x0 /* restore csselr_el1 */ + dsb sy + isb + mov lr, x15 + +finished: + ret + +.section .text.flush_dcache_all, "ax", %progbits +.type flush_dcache_all, %function +.global flush_dcache_all +flush_dcache_all: + mov x0, #0 + b __asm_dcache_all + +.section .text.invalidate_dcache_all, "ax", %progbits +.type invalidate_dcache_all, %function +.global invalidate_dcache_all +invalidate_dcache_all: + mov x0, #1 + b __asm_dcache_all + +/* + * void __asm_flush_dcache_range(start, end) (renamed -> flush_dcache_range) + * + * clean & invalidate data cache in the range + * + * x0: start address + * x1: end address + */ +.section .text.flush_dcache_range, "ax", %progbits +.type flush_dcache_range, %function +.global flush_dcache_range +flush_dcache_range: + mrs x3, ctr_el0 + lsr x3, x3, #16 + and x3, x3, #0xf + mov x2, #4 + lsl x2, x2, x3 /* cache line size */ + + /* x2 <- minimal cache line size in cache system */ + sub x3, x2, #1 + bic x0, x0, x3 +1: dc civac, x0 /* clean & invalidate data or unified cache */ + add x0, x0, x2 + cmp x0, x1 + b.lo 1b + dsb sy + ret + +/* + * void __asm_invalidate_dcache_range(start, end) (-> invalidate_dcache_range) + * + * invalidate data cache in the range + * + * x0: start address + * x1: end address + */ +.section .text.invalidate_dcache_range, "ax", %progbits +.type invalidate_dcache_range, %function +.global invalidate_dcache_range +invalidate_dcache_range: + mrs x3, ctr_el0 + ubfm x3, x3, #16, #19 + mov x2, #4 + lsl x2, x2, x3 /* cache line size */ + + /* x2 <- minimal cache line size in cache system */ + sub x3, x2, #1 + bic x0, x0, x3 +1: dc ivac, x0 /* invalidate data or unified cache */ + add x0, x0, x2 + cmp x0, x1 + b.lo 1b + dsb sy + ret + +/* + * void __asm_invalidate_icache_all(void) (-> invalidate_icache_inner_shareable) + * + * invalidate all icache entries. + */ +.section .text.invalidate_icache_inner_shareable, "ax", %progbits +.type invalidate_icache_inner_shareable, %function +.global invalidate_icache_inner_shareable +invalidate_icache_inner_shareable: + dsb ish + isb + ic ialluis + dsb ish + isb + ret diff --git a/exosphere/src/coldboot_init.c b/exosphere/src/coldboot_init.c new file mode 100644 index 0000000..b62650c --- /dev/null +++ b/exosphere/src/coldboot_init.c @@ -0,0 +1,89 @@ +#include "utils.h" +#include "mmu.h" +#include "memory_map.h" + +/* +extern void (*__preinit_array_start[])(void); +extern void (*__preinit_array_end[])(void); +extern void (*__init_array_start[])(void); +extern void (*__init_array_end[])(void); +extern void _init(void); + +extern uint8_t __warmboot_crt0_start__[], __warmboot_crt0_end__[], __warmboot_crt0_lma__[]; +extern uint8_t __main_start__[], __main_end__[], __main_lma__[]; +extern uint8_t __pk2ldr_start__[], __pk2ldr_end__[], __pk2ldr_lma__[]; +extern uint8_t __vectors_start__[], __vectors_end__[], __vectors_lma__[];*/ +extern void flush_dcache_all_tzram_pa(void); +extern void invalidate_icache_all_tzram_pa(void); + +uintptr_t get_coldboot_crt0_stack_address(void); + +static void configure_ttbls(void) { + uintptr_t *mmu_l1_tbl = (uintptr_t *)(tzram_get_segment_pa(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x800 - 64); + uintptr_t *mmu_l2_tbl = (uintptr_t *)tzram_get_segment_pa(TZRAM_SEGMENT_ID_L2_TRANSLATION_TABLE); + uintptr_t *mmu_l3_tbl = (uintptr_t *)tzram_get_segment_pa(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); + + mmu_init_table(mmu_l1_tbl, 64); /* 33-bit address space */ + mmu_init_table(mmu_l2_tbl, 4096); + /* + Nintendo uses the same L3 table for everything, but they make sure + nothing clashes. + */ + mmu_init_table(mmu_l3_tbl, 4096); + + mmu_map_table(1, mmu_l1_tbl, 0x40000000, mmu_l2_tbl, 0); + mmu_map_table(1, mmu_l1_tbl, 0x1C0000000, mmu_l2_tbl, 0); + + mmu_map_table(2, mmu_l2_tbl, 0x40000000, mmu_l3_tbl, 0); + mmu_map_table(2, mmu_l2_tbl, 0x7C000000, mmu_l3_tbl, 0); + mmu_map_table(2, mmu_l2_tbl, 0x1F0000000ull, mmu_l3_tbl, 0); + + identity_map_all_mappings(mmu_l1_tbl, mmu_l3_tbl); + mmio_map_all_devices(mmu_l3_tbl); + lp0_map_all_plaintext_ram_segments(mmu_l3_tbl); + lp0_map_all_ciphertext_ram_segments(mmu_l3_tbl); + tzram_map_all_segments(mmu_l3_tbl); +} + +#if 0 + +static void copy_lma_to_vma(unsigned int segment_id, void *lma, size_t size, bool vma_is_pa) { + uintptr_t vma = vma_is_pa ? tzram_get_segment_pa(segment_id) : tzram_get_segment_address(segment_id); + uintptr_t vma_offset = (uintptr_t)lma & 0xFFF; + uint64_t *p_vma = (uint64_t *)vma; + uint64_t *p_lma = (uint64_t *)lma; + for (size_t i = 0; i < size / 8; i++) { + p_vma[vma_offset / 8 + i] = p_lma[i]; + } +} + +static void __libc_init_array(void) { + for (size_t i = 0; i < __preinit_array_end - __preinit_array_start; i++) + __preinit_array_start[i](); + _init(); /* FIXME: do we have this gcc-provided symbol if we build with -nostartfiles? */ + for (size_t i = 0; i < __init_array_end - __init_array_start; i++) + __init_array_start[i](); +} +#endif + +uintptr_t get_coldboot_crt0_stack_address(void) { + return tzram_get_segment_pa(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x800; +} + +void coldboot_init(void) { + /* TODO: Set NX BOOTLOADER clock time field */ + /*copy_lma_to_vma(TZRAM_SEGMENT_ID_WARMBOOT_CRT0_AND_MAIN, __warmboot_crt0_lma__, __warmboot_crt0_end__ - __warmboot_crt0_start__, true);*/ + /* TODO: set some mmio regs, etc. */ + /* TODO: initialize DMA controllers */ + configure_ttbls(); + /*copy_lma_to_vma(TZRAM_SEGMENT_ID_WARMBOOT_CRT0_AND_MAIN, __main_lma__, __main_end__ - __main_start__, false); + copy_lma_to_vma(TZRAM_SEGMENT_ID_PK2LDR, __pk2ldr_lma__, __pk2ldr_end__ - __pk2ldr_start__, false); + copy_lma_to_vma(TZRAM_SEGEMENT_ID_SECMON_EVT, __vectors_lma__, __vectors_end__ - __vectors_start__, false);*/ + /* TODO: set the MMU regs & tlbi & enable MMU */ + + flush_dcache_all_tzram_pa(); + invalidate_icache_all_tzram_pa(); + /* TODO: zero-initialize the cpu context */ + /* Nintendo clears the (emtpy) pk2ldr's BSS section, but we embed it 0-filled in the binary */ + /*__libc_init_array(); construct global objects */ +} diff --git a/exosphere/src/coldboot_main.c b/exosphere/src/coldboot_main.c new file mode 100644 index 0000000..9917edf --- /dev/null +++ b/exosphere/src/coldboot_main.c @@ -0,0 +1,39 @@ +#include +#include "utils.h" +#include "mmu.h" +#include "memory_map.h" +#include "cache.h" + +/* +extern void (*__fini_array_start[])(void); +extern void (*__fini_array_end[])(void); +extern void _fini(void);*/ + +extern uint8_t __pk2ldr_start__[], __pk2ldr_end__[]; + +extern void __jump_to_lower_el(uint64_t arg, uintptr_t ep, unsigned int el); + +void coldboot_main(void); + +#if 0 +/* Needs to be called for EL3->EL3 chainloading (and only in that case). TODO: use it */ +__attribute__((used)) static void __libc_fini_array(void) { + for (size_t i = __fini_array_end - __fini_array_start; i > 0; i--) + __fini_array_start[i - 1](); + _fini(); /* FIXME: do we have this gcc-provided symbol if we build with -nostartfiles? */ +} +#endif + +void coldboot_main(void) { + #if 0 + uintptr_t *mmu_l3_table = (uintptr_t *)tzram_get_segment_address(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); + uintptr_t pk2ldr = tzram_get_segment_address(TZRAM_SEGMENT_ID_PK2LDR); + + /* Clear and unmap pk2ldr (which is reused as exception entry stacks) */ + memset((void *)pk2ldr, 0, __pk2ldr_end__ - __pk2ldr_start__); + mmu_unmap_range(3, mmu_l3_table, pk2ldr, __pk2ldr_end__ - __pk2ldr_start__); + tlb_invalidate_all_inner_shareable(); + #endif + + /* TODO: stuff & jump to lower EL */ +} diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c new file mode 100644 index 0000000..ed5a322 --- /dev/null +++ b/exosphere/src/configitem.c @@ -0,0 +1,90 @@ +#include + +#include "bootconfig.h" +#include "configitem.h" +#include "interrupt.h" +#include "package2.h" +#include "se.h" +#include "fuse.h" +#include "utils.h" + +int g_battery_profile = 0; + +uint32_t configitem_set(enum ConfigItem item, uint64_t value) { + if (item != CONFIGITEM_BATTERYPROFILE) { + return 2; + } + + g_battery_profile = ((int)(value != 0)) & 1; + return 0; /* FIXME: what should we return there */ +} + +bool configitem_is_recovery_boot(void) { + uint64_t is_recovery_boot; + if (configitem_get(CONFIGITEM_ISRECOVERYBOOT, &is_recovery_boot) != 0) { + generic_panic(); + } + + return is_recovery_boot != 0; +} + +bool configitem_is_retail(void) { + uint64_t is_retail; + if (configitem_get(CONFIGITEM_ISRETAIL, &is_retail) != 0) { + generic_panic(); + } + + return is_retail != 0; +} + +uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue) { + uint32_t result = 0; + switch (item) { + case CONFIGITEM_DISABLEPROGRAMVERIFICATION: + *p_outvalue = (int)(bootconfig_disable_program_verification()); + break; + case CONFIGITEM_DRAMID: + *p_outvalue = fuse_get_dram_id(); + break; + case CONFIGITEM_SECURITYENGINEIRQ: + /* SE is interrupt #0x2C. */ + *p_outvalue = INTERRUPT_ID_USER_SECURITY_ENGINE; + break; + case CONFIGITEM_VERSION: + /* Always returns maxver - 1 on hardware. */ + *p_outvalue = PACKAGE2_MAXVER_400_CURRENT - 1; + break; + case CONFIGITEM_HARDWARETYPE: + *p_outvalue = fuse_get_hardware_type(); + break; + case CONFIGITEM_ISRETAIL: + *p_outvalue = fuse_get_retail_type(); + break; + case CONFIGITEM_ISRECOVERYBOOT: + /* TODO: This requires reading values passed to crt0 via NX_Bootloader. TBD pending crt0 implementation. */ + *p_outvalue = 0; + break; + case CONFIGITEM_DEVICEID: + *p_outvalue = fuse_get_device_id(); + break; + case CONFIGITEM_BOOTREASON: + /* TODO: This requires reading values passed to crt0 via NX_Bootloader. TBD pending crt0 implementation. */ + break; + case CONFIGITEM_MEMORYARRANGE: + *p_outvalue = bootconfig_get_memory_arrangement(); + break; + case CONFIGITEM_ISDEBUGMODE: + *p_outvalue = (int)(bootconfig_is_debug_mode()); + break; + case CONFIGITEM_KERNELMEMORYCONFIGURATION: + *p_outvalue = bootconfig_get_kernel_memory_configuration(); + break; + case CONFIGITEM_BATTERYPROFILE: + *p_outvalue = g_battery_profile; + break; + default: + result = 2; + break; + } + return result; +} diff --git a/exosphere/src/configitem.h b/exosphere/src/configitem.h new file mode 100644 index 0000000..65ba0c9 --- /dev/null +++ b/exosphere/src/configitem.h @@ -0,0 +1,29 @@ +#ifndef EXOSPHERE_CFG_ITEM_H +#define EXOSPHERE_CFG_ITEM_H + +#include +#include + +enum ConfigItem { + CONFIGITEM_DISABLEPROGRAMVERIFICATION = 1, + CONFIGITEM_DRAMID = 2, + CONFIGITEM_SECURITYENGINEIRQ = 3, + CONFIGITEM_VERSION = 4, + CONFIGITEM_HARDWARETYPE = 5, + CONFIGITEM_ISRETAIL = 6, + CONFIGITEM_ISRECOVERYBOOT = 7, + CONFIGITEM_DEVICEID = 8, + CONFIGITEM_BOOTREASON = 9, + CONFIGITEM_MEMORYARRANGE = 10, + CONFIGITEM_ISDEBUGMODE = 11, + CONFIGITEM_KERNELMEMORYCONFIGURATION = 12, + CONFIGITEM_BATTERYPROFILE = 13 +}; + +uint32_t configitem_set(enum ConfigItem item, uint64_t value); +uint32_t configitem_get(enum ConfigItem item, uint64_t *p_outvalue); + +bool configitem_is_recovery_boot(void); +bool configitem_is_retail(void); + +#endif diff --git a/exosphere/src/cpu_context.c b/exosphere/src/cpu_context.c new file mode 100644 index 0000000..c5e84f5 --- /dev/null +++ b/exosphere/src/cpu_context.c @@ -0,0 +1,70 @@ +#include +#include "cpu_context.h" +#include "pmc.h" +#include "timers.h" +#include "utils.h" + +saved_cpu_context_t g_cpu_contexts[NUM_CPU_CORES] = {0}; + +void set_core_entrypoint_and_context_id(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id) { + g_cpu_contexts[core].ELR_EL3 = entrypoint_addr; + g_cpu_contexts[core].context_id = context_id; +} + +uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id) { + /* Is core valid? */ + if (core >= NUM_CPU_CORES) { + return 0xFFFFFFFE; + } + + /* Is core already on? */ + if (g_cpu_contexts[core].is_active) { + return 0xFFFFFFFC; + } + + set_core_entrypoint_and_context_id(core, entrypoint_addr, context_id); + + const uint32_t status_masks[NUM_CPU_CORES] = {0x4000, 0x200, 0x400, 0x800}; + const uint32_t toggle_vals[NUM_CPU_CORES] = {0xE, 0x9, 0xA, 0xB}; + + /* Check if we're already in the correct state. */ + if ((APBDEV_PMC_PWRGATE_STATUS_0 & status_masks[core]) != status_masks[core]) { + uint32_t counter = 5001; + + /* Poll the start bit until 0 */ + while (APBDEV_PMC_PWRGATE_TOGGLE_0 & 0x100) { + wait(1); + counter--; + if (counter < 1) { + return 0; + } + } + + /* Program PWRGATE_TOGGLE with the START bit set to 1, selecting CE[N] */ + APBDEV_PMC_PWRGATE_TOGGLE_0 = toggle_vals[core] | 0x100; + + /* Poll until we're in the correct state. */ + counter = 5001; + while (counter > 0) { + if ((APBDEV_PMC_PWRGATE_STATUS_0 & status_masks[core]) == status_masks[core]) { + break; + } + wait(1); + counter--; + } + } + return 0; +} + +uint32_t cpu_off(void) { + return 0; + /* TODO */ +} + +uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint_addr, uint64_t context_id) { + (void)power_state; + (void)entrypoint_addr; + (void)context_id; + return 0; + /* TODO */ +} diff --git a/exosphere/src/cpu_context.h b/exosphere/src/cpu_context.h new file mode 100644 index 0000000..85149ea --- /dev/null +++ b/exosphere/src/cpu_context.h @@ -0,0 +1,54 @@ +#ifndef EXOSPHERE_CPU_CTX_H +#define EXOSPHERE_CPU_CTX_H + +#include + +/* Exosphere CPU Management functionality. */ + +typedef struct { + uint64_t context_id; + uint64_t ELR_EL3; + int is_active; + int is_saved; + uint32_t OSDTRRX_EL1; + uint32_t OSDTRTX_EL1; + uint32_t MDSCR_EL1; + uint32_t OSECCR_EL1; + uint32_t MDCCINT_EL1; + uint32_t DBGCLAIMCLR_EL1; + uint32_t DBGVCR32_EL2; + uint32_t SDER32_EL3; + uint32_t MDCR_EL2; + uint32_t MDCR_EL3; + uint64_t DBGBVR0_EL1; + uint64_t DBGBCR0_EL1; + uint64_t DBGBVR1_EL1; + uint64_t DBGBCR1_EL1; + uint64_t DBGBVR2_EL1; + uint64_t DBGBCR2_EL1; + uint64_t DBGBVR3_EL1; + uint64_t DBGBCR3_EL1; + uint64_t DBGBVR4_EL1; + uint64_t DBGBCR4_EL1; + uint64_t DBGBVR5_EL1; + uint64_t DBGBCR5_EL1; + uint64_t DBGWVR0_EL1; + uint64_t DBGWCR0_EL1; + uint64_t DBGWVR1_EL1; + uint64_t DBGWCR1_EL1; + uint64_t DBGWVR2_EL1; + uint64_t DBGWCR2_EL1; + uint64_t DBGWVR3_EL1; + uint64_t DBGWCR3_EL1; +} saved_cpu_context_t; + +#define NUM_CPU_CORES 4 + +void set_core_entrypoint_and_context_id(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id); + +uint32_t cpu_on(uint32_t core, uint64_t entrypoint_addr, uint64_t context_id); +uint32_t cpu_off(void); /* TODO */ +uint32_t cpu_suspend(uint64_t power_state, uint64_t entrypoint_addr, uint64_t context_id); /* TODO */ + + +#endif diff --git a/exosphere/src/exceptions.s b/exosphere/src/exceptions.s new file mode 100644 index 0000000..660792e --- /dev/null +++ b/exosphere/src/exceptions.s @@ -0,0 +1,253 @@ +/* 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 Exosphere. */ +.global exosphere_vectors +vector_base exosphere_vectors + +/* Current EL, SP0 */ +.global unknown_exception +unknown_exception: +vector_entry synch_sp0 + /* Panic with color FF7700, code 10. */ + mov x0, #0x10 + movk x0, #0x07F0,lsl#16 + b panic + check_vector_size synch_sp0 + +vector_entry irq_sp0 + b unknown_exception + check_vector_size irq_sp0 + +vector_entry fiq_sp0 + b unknown_exception + check_vector_size fiq_sp0 + +vector_entry serror_sp0 + b unknown_exception + check_vector_size serror_sp0 + +/* Current EL, SPx */ +vector_entry synch_spx + b unknown_exception + check_vector_size synch_spx + +vector_entry irq_spx + b unknown_exception + check_vector_size irq_spx + +vector_entry fiq_spx + b unknown_exception + check_vector_size fiq_spx + +vector_entry serror_spx + b unknown_exception + check_vector_size serror_spx + +/* Lower EL, A64 */ +vector_entry synch_a64 + stp x29, x30, [sp, #-0x10]! + /* Verify SMC. */ + mrs x30, esr_el3 + lsr w29, w30, #0x1A + cmp w29, #0x17 + ldp x29, x30, [sp],#0x10 + b.ne unknown_exception + /* Call appropriate handler. */ + stp x29, x30, [sp, #-0x10]! + mrs x29, mpidr_el1 + and x29, x29, #0x3 + cmp x29, #0x3 + b.ne handle_core012_smc_exception + bl handle_core3_smc_exception + ldp x29, x30, [sp],#0x10 + eret + check_vector_size synch_a64 + +vector_entry irq_a64 + b unknown_exception + check_vector_size irq_a64 + +vector_entry fiq_a64 + stp x29, x30, [sp, #-0x10]! + mrs x29, mpidr_el1 + and x29, x29, #0x3 + cmp x29, #0x3 + b.ne unknown_exception + stp x28, x29, [sp, #-0x10]! + stp x26, x27, [sp, #-0x10]! + bl handle_fiq_exception + ldp x26, x27, [sp],#0x10 + ldp x28, x29, [sp],#0x10 + ldp x29, x30, [sp],#0x10 + eret + check_vector_size fiq_a64 + +vector_entry serror_a64 + b unknown_exception + .endfunc + .cfi_endproc +/* To save space, insert in an unused vector segment. */ +.global handle_core012_smc_exception +.type handle_core012_smc_exception, %function +handle_core012_smc_exception: + stp x6, x7, [sp, #-0x10]! + stp x4, x5, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x0, x1, [sp, #-0x10]! + bl set_priv_smc_in_progress + bl get_smc_core012_stack_address + mov x29, x0 + ldp x0, x1, [sp],#0x10 + ldp x2, x3, [sp],#0x10 + ldp x4, x5, [sp],#0x10 + ldp x6, x7, [sp],#0x10 + mov x30, sp + mov sp, x29 + stp x29, x30, [sp, #-0x10]! + bl handle_core3_smc_exception + ldp x29, x30, [sp],#0x10 + mov sp, x30 + stp x6, x7, [sp, #-0x10]! + stp x4, x5, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x0, x1, [sp, #-0x10]! + bl clear_priv_smc_in_progress + ldp x0, x1, [sp],#0x10 + ldp x2, x3, [sp],#0x10 + ldp x4, x5, [sp],#0x10 + ldp x6, x7, [sp],#0x10 + ldp x29, x30, [sp],#0x10 + eret + +/* Lower EL, A32 */ +vector_entry synch_a32 + b unknown_exception + check_vector_size synch_a32 + +vector_entry irq_a32 + b unknown_exception + check_vector_size irq_a32 + +vector_entry fiq_a32 + b fiq_a64 + .endfunc + .cfi_endproc +/* To save space, insert in an unused vector segment. */ +.global handle_fiq_exception +.type handle_fiq_exception, %function +handle_fiq_exception: + 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]! + bl handle_registered_interrupt + 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 + b unknown_exception + .endfunc + .cfi_endproc +/* To save space, insert in an unused vector segment. */ +.global handle_core3_smc_exception +.type handle_core3_smc_exception, %function +handle_core3_smc_exception: + 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]! + mrs x0, esr_el3 + and x0, x0, #0xFFFF + mov x1, sp + bl call_smc_handler + 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 + ret diff --git a/exosphere/src/fuse.c b/exosphere/src/fuse.c new file mode 100644 index 0000000..62794a3 --- /dev/null +++ b/exosphere/src/fuse.c @@ -0,0 +1,229 @@ +#include + +#include "fuse.h" +#include "utils.h" +#include "timers.h" + +/* Prototypes for internal commands. */ +void fuse_make_regs_visible(void); + +void fuse_enable_power(void); +void fuse_disable_power(void); +void fuse_wait_idle(void); + +/* Initialize the FUSE driver */ +void fuse_init(void) +{ + fuse_make_regs_visible(); + + /* TODO: Overrides (iROM patches) and various reads happen here */ +} + +/* Make all fuse registers visible */ +void fuse_make_regs_visible(void) +{ + /* TODO: Replace this with a proper CLKRST driver */ + volatile uint32_t* misc_clk_reg = (volatile uint32_t *)mmio_get_device_address(MMIO_DEVID_CLKRST) + 0x48; + uint32_t misc_clk_val = *misc_clk_reg; + *misc_clk_reg = (misc_clk_val | (1 << 28)); +} + +/* Enable power to the fuse hardware array */ +void fuse_enable_power(void) +{ + FUSE_REGS->FUSE_PWR_GOOD_SW = 1; + wait(1); +} + +/* Disable power to the fuse hardware array */ +void fuse_disable_power(void) +{ + FUSE_REGS->FUSE_PWR_GOOD_SW = 0; + wait(1); +} + +/* Wait for the fuse driver to go idle */ +void fuse_wait_idle(void) +{ + uint32_t ctrl_val = 0; + + /* Wait for STATE_IDLE */ + while ((ctrl_val & (0xF0000)) != 0x40000) + { + wait(1); + ctrl_val = FUSE_REGS->FUSE_CTRL; + } +} + +/* Read a fuse from the hardware array */ +uint32_t fuse_hw_read(uint32_t addr) +{ + fuse_wait_idle(); + + /* Program the target address */ + FUSE_REGS->FUSE_REG_ADDR = addr; + + /* Enable read operation in control register */ + uint32_t ctrl_val = FUSE_REGS->FUSE_CTRL; + ctrl_val &= ~0x3; + ctrl_val |= 0x1; /* Set FUSE_READ command */ + FUSE_REGS->FUSE_CTRL = ctrl_val; + + fuse_wait_idle(); + + return FUSE_REGS->FUSE_REG_READ; +} + +/* Write a fuse in the hardware array */ +void fuse_hw_write(uint32_t value, uint32_t addr) +{ + fuse_wait_idle(); + + /* Program the target address and value */ + FUSE_REGS->FUSE_REG_ADDR = addr; + FUSE_REGS->FUSE_REG_WRITE = value; + + /* Enable write operation in control register */ + uint32_t ctrl_val = FUSE_REGS->FUSE_CTRL; + ctrl_val &= ~0x3; + ctrl_val |= 0x2; /* Set FUSE_WRITE command */ + FUSE_REGS->FUSE_CTRL = ctrl_val; + + fuse_wait_idle(); +} + +/* Sense the fuse hardware array into the shadow cache */ +void fuse_hw_sense(void) +{ + fuse_wait_idle(); + + /* Enable sense operation in control register */ + uint32_t ctrl_val = FUSE_REGS->FUSE_CTRL; + ctrl_val &= ~0x3; + ctrl_val |= 0x3; /* Set FUSE_SENSE command */ + FUSE_REGS->FUSE_CTRL = ctrl_val; + + fuse_wait_idle(); +} + +/* Disables all fuse programming. */ +void fuse_disable_programming(void) { + FUSE_REGS->FUSE_DIS_PGM = 1; +} + +/* Unknown exactly what this does, but it alters the contents read from the fuse cache. */ +void fuse_secondary_private_key_disable(void) { + FUSE_REGS->FUSE_PRIVATEKEYDISABLE = 0x10; +} + + +/* Read the SKU info register from the shadow cache */ +uint32_t fuse_get_sku_info(void) +{ + return FUSE_CHIP_REGS->FUSE_SKU_INFO; +} + +/* Read the bootrom patch version from a register in the shadow cache */ +uint32_t fuse_get_bootrom_patch_version(void) +{ + return FUSE_CHIP_REGS->FUSE_SOC_SPEEDO_1; +} + +/* Read a spare bit register from the shadow cache */ +uint32_t fuse_get_spare_bit(uint32_t idx) +{ + uint32_t spare_bit_val = 0; + + if ((idx >= 0) && (idx < 32)) + spare_bit_val = FUSE_CHIP_REGS->FUSE_SPARE_BIT[idx]; + + return spare_bit_val; +} + +/* Read a reserved ODM register from the shadow cache */ +uint32_t fuse_get_reserved_odm(uint32_t idx) +{ + uint32_t reserved_odm_val = 0; + + if ((idx >= 0) && (idx < 8)) + reserved_odm_val = FUSE_CHIP_REGS->FUSE_RESERVED_ODM[idx]; + + return reserved_odm_val; +} + +/* Derive the Device ID using values in the shadow cache */ +uint64_t fuse_get_device_id(void) { + uint64_t device_id = 0; + uint64_t y_coord = FUSE_CHIP_REGS->FUSE_Y_COORDINATE & 0x1FF; + uint64_t x_coord = FUSE_CHIP_REGS->FUSE_X_COORDINATE & 0x1FF; + uint64_t wafer_id = FUSE_CHIP_REGS->FUSE_WAFER_ID & 0x3F; + uint32_t lot_code = FUSE_CHIP_REGS->FUSE_LOT_CODE_0; + uint64_t fab_code = FUSE_CHIP_REGS->FUSE_FAB_CODE & 0x3F; + uint64_t derived_lot_code = 0; + for (unsigned int i = 0; i < 5; i++) { + derived_lot_code = (derived_lot_code * 0x24) + ((lot_code >> (24 - 6*i)) & 0x3F); + } + derived_lot_code &= 0x03FFFFFF; + + device_id |= y_coord << 0; + device_id |= x_coord << 9; + device_id |= wafer_id << 18; + device_id |= derived_lot_code << 24; + device_id |= fab_code << 50; + return device_id; +} + +/* Get the DRAM ID using values in the shadow cache */ +uint32_t fuse_get_dram_id(void) { + return (FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 3) & 0x7; +} + +/* Derive the Hardware Type using values in the shadow cache */ +uint32_t fuse_get_hardware_type(void) { + uint32_t hardware_type = ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 7) & 2) | ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 2) & 1); + if (hardware_type) { + if (hardware_type == 1) { + return 0; + } + if (hardware_type == 2) { + return 1; + } + } else if ((FUSE_CHIP_REGS->FUSE_SPARE_BIT[9] & 1) == 0) { + return 0; + } + return 3; +} + +/* Derive the Retail Type using values in the shadow cache */ +uint32_t fuse_get_retail_type(void) { + /* Retail type = IS_RETAIL | UNIT_TYPE */ + uint32_t retail_type = ((FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] >> 7) & 4) | (FUSE_CHIP_REGS->FUSE_RESERVED_ODM[4] & 3); + if (retail_type == 4) { /* Standard retail unit, IS_RETAIL | 0. */ + return 1; + } else if (retail_type == 3) { /* Standard dev unit, 0 | DEV_UNIT. */ + return 0; + } + return 2; /* IS_RETAIL | DEV_UNIT */ +} + +/* Derive the 16-byte Hardware Info using values in the shadow cache, and copy to output buffer. */ +void fuse_get_hardware_info(void *dst) { + uint32_t hw_info[0x4]; + + uint32_t unk_hw_fuse = FUSE_CHIP_REGS->_0x120 & 0x3F; + uint32_t y_coord = FUSE_CHIP_REGS->FUSE_Y_COORDINATE & 0x1FF; + uint32_t x_coord = FUSE_CHIP_REGS->FUSE_X_COORDINATE & 0x1FF; + uint32_t wafer_id = FUSE_CHIP_REGS->FUSE_WAFER_ID & 0x3F; + uint32_t lot_code_0 = FUSE_CHIP_REGS->FUSE_LOT_CODE_0; + uint32_t lot_code_1 = FUSE_CHIP_REGS->FUSE_LOT_CODE_1 & 0x0FFFFFFF; + uint32_t fab_code = FUSE_CHIP_REGS->FUSE_FAB_CODE & 0x3F; + uint32_t vendor_code = FUSE_CHIP_REGS->FUSE_VENDOR_CODE & 0xF; + + /* Hardware Info = unk_hw_fuse || Y_COORD || X_COORD || WAFER_ID || LOT_CODE || FAB_CODE || VENDOR_ID */ + hw_info[0] = (uint32_t)((lot_code_1 << 30) | (wafer_id << 24) | (x_coord << 15) | (y_coord << 6) | (unk_hw_fuse)); + hw_info[1] = (uint32_t)((lot_code_0 << 26) | (lot_code_1 >> 2)); + hw_info[2] = (uint32_t)((fab_code << 26) | (lot_code_0 >> 6)); + hw_info[3] = (uint32_t)(vendor_code); + + memcpy(dst, hw_info, 0x10); +} diff --git a/exosphere/src/fuse.h b/exosphere/src/fuse.h new file mode 100644 index 0000000..1d602f7 --- /dev/null +++ b/exosphere/src/fuse.h @@ -0,0 +1,193 @@ +#ifndef EXOSPHERE_FUSE_H +#define EXOSPHERE_FUSE_H + +#include +#include +#include "memory_map.h" + +/* Exosphere driver for the Tegra X1 FUSE registers. */ + +typedef struct { + uint32_t FUSE_CTRL; + uint32_t FUSE_REG_ADDR; + uint32_t FUSE_REG_READ; + uint32_t FUSE_REG_WRITE; + uint32_t FUSE_TIME_RD1; + uint32_t FUSE_TIME_RD2; + uint32_t FUSE_TIME_PGM1; + uint32_t FUSE_TIME_PGM2; + uint32_t FUSE_PRIV2INTFC; + uint32_t FUSE_FUSEBYPASS; + uint32_t FUSE_PRIVATEKEYDISABLE; + uint32_t FUSE_DIS_PGM; + uint32_t FUSE_WRITE_ACCESS; + uint32_t FUSE_PWR_GOOD_SW; + uint32_t _0x38[0x32]; +} fuse_registers_t; + +typedef struct { + uint32_t FUSE_PRODUCTION_MODE; + uint32_t _0x4; + uint32_t _0x8; + uint32_t _0xC; + uint32_t FUSE_SKU_INFO; + uint32_t FUSE_CPU_SPEEDO_0; + uint32_t FUSE_CPU_IDDQ; + uint32_t _0x1C; + uint32_t _0x20; + uint32_t _0x24; + uint32_t FUSE_FT_REV; + uint32_t FUSE_CPU_SPEEDO_1; + uint32_t FUSE_CPU_SPEEDO_2; + uint32_t FUSE_SOC_SPEEDO_0; + uint32_t FUSE_SOC_SPEEDO_1; + uint32_t FUSE_SOC_SPEEDO_2; + uint32_t FUSE_SOC_IDDQ; + uint32_t _0x44; + uint32_t FUSE_FA; + uint32_t _0x4C; + uint32_t _0x50; + uint32_t _0x54; + uint32_t _0x58; + uint32_t _0x5C; + uint32_t _0x60; + uint32_t FUSE_PUBLIC_KEY[0x8]; + uint32_t FUSE_TSENSOR_1; + uint32_t FUSE_TSENSOR_2; + uint32_t _0x8C; + uint32_t FUSE_CP_REV; + uint32_t _0x94; + uint32_t FUSE_TSENSOR_0; + uint32_t FUSE_FIRST_BOOTROM_PATCH_SIZE_REG; + uint32_t FUSE_SECURITY_MODE; + uint32_t FUSE_PRIVATE_KEY[0x4]; + uint32_t FUSE_DEVICE_KEY; + uint32_t _0xB8; + uint32_t _0xBC; + uint32_t FUSE_RESERVED_SW; + uint32_t FUSE_VP8_ENABLE; + uint32_t FUSE_RESERVED_ODM[0x8]; + uint32_t _0xE8; + uint32_t _0xEC; + uint32_t FUSE_SKU_USB_CALIB; + uint32_t FUSE_SKU_DIRECT_CONFIG; + uint32_t _0xF8; + uint32_t _0xFC; + uint32_t FUSE_VENDOR_CODE; + uint32_t FUSE_FAB_CODE; + uint32_t FUSE_LOT_CODE_0; + uint32_t FUSE_LOT_CODE_1; + uint32_t FUSE_WAFER_ID; + uint32_t FUSE_X_COORDINATE; + uint32_t FUSE_Y_COORDINATE; + uint32_t _0x11C; + uint32_t _0x120; + uint32_t FUSE_SATA_CALIB; + uint32_t FUSE_GPU_IDDQ; + uint32_t FUSE_TSENSOR_3; + uint32_t _0x130; + uint32_t _0x134; + uint32_t _0x138; + uint32_t _0x13C; + uint32_t _0x140; + uint32_t _0x144; + uint32_t FUSE_OPT_SUBREVISION; + uint32_t _0x14C; + uint32_t _0x150; + uint32_t FUSE_TSENSOR_4; + uint32_t FUSE_TSENSOR_5; + uint32_t FUSE_TSENSOR_6; + uint32_t FUSE_TSENSOR_7; + uint32_t FUSE_OPT_PRIV_SEC_DIS; + uint32_t FUSE_PKC_DISABLE; + uint32_t _0x16C; + uint32_t _0x170; + uint32_t _0x174; + uint32_t _0x178; + uint32_t _0x17C; + uint32_t FUSE_TSENSOR_COMMON; + uint32_t _0x184; + uint32_t _0x188; + uint32_t _0x18C; + uint32_t _0x190; + uint32_t _0x194; + uint32_t _0x198; + uint32_t FUSE_DEBUG_AUTH_OVERRIDE; + uint32_t _0x1A0; + uint32_t _0x1A4; + uint32_t _0x1A8; + uint32_t _0x1AC; + uint32_t _0x1B0; + uint32_t _0x1B4; + uint32_t _0x1B8; + uint32_t _0x1BC; + uint32_t _0x1D0; + uint32_t FUSE_TSENSOR_8; + uint32_t _0x1D8; + uint32_t _0x1DC; + uint32_t _0x1E0; + uint32_t _0x1E4; + uint32_t _0x1E8; + uint32_t _0x1EC; + uint32_t _0x1F0; + uint32_t _0x1F4; + uint32_t _0x1F8; + uint32_t _0x1FC; + uint32_t _0x200; + uint32_t FUSE_RESERVED_CALIB; + uint32_t _0x208; + uint32_t _0x20C; + uint32_t _0x210; + uint32_t _0x214; + uint32_t _0x218; + uint32_t FUSE_TSENSOR_9; + uint32_t _0x220; + uint32_t _0x224; + uint32_t _0x228; + uint32_t _0x22C; + uint32_t _0x230; + uint32_t _0x234; + uint32_t _0x238; + uint32_t _0x23C; + uint32_t _0x240; + uint32_t _0x244; + uint32_t _0x248; + uint32_t _0x24C; + uint32_t FUSE_USB_CALIB_EXT; + uint32_t _0x254; + uint32_t _0x258; + uint32_t _0x25C; + uint32_t _0x260; + uint32_t _0x264; + uint32_t _0x268; + uint32_t _0x26C; + uint32_t _0x270; + uint32_t _0x274; + uint32_t _0x278; + uint32_t _0x27C; + uint32_t FUSE_SPARE_BIT[0x20]; +} fuse_chip_registers_t; + +#define FUSE_REGS ((volatile fuse_registers_t *)(mmio_get_device_address(MMIO_DEVID_FUSE) + 0x800)) +#define FUSE_CHIP_REGS ((volatile fuse_chip_registers_t *)(mmio_get_device_address(MMIO_DEVID_FUSE) + 0x900)) + +void fuse_init(void); + +uint32_t fuse_hw_read(uint32_t addr); +void fuse_hw_write(uint32_t value, uint32_t addr); +void fuse_hw_sense(void); +void fuse_disable_programming(void); +void fuse_secondary_private_key_disable(void); + +uint32_t fuse_get_sku_info(void); +uint32_t fuse_get_spare_bit(uint32_t idx); +uint32_t fuse_get_reserved_odm(uint32_t idx); + +uint32_t fuse_get_bootrom_patch_version(void); +uint64_t fuse_get_device_id(void); +uint32_t fuse_get_dram_id(void); +uint32_t fuse_get_hardware_type(void); +uint32_t fuse_get_retail_type(void); +void fuse_get_hardware_info(void *dst); + +#endif diff --git a/exosphere/src/gcm.c b/exosphere/src/gcm.c new file mode 100644 index 0000000..f09ae2d --- /dev/null +++ b/exosphere/src/gcm.c @@ -0,0 +1,167 @@ +#include +#include + +#include "utils.h" +#include "fuse.h" +#include "gcm.h" + +#include "sealedkeys.h" +#include "se.h" + +/* Shifts right a little endian 128-bit value. */ +static void shr_128(uint64_t *val) { + val[0] >>= 1; + val[0] |= (val[1] & 1) << 63; + val[1] >>= 1; +} + +/* Shifts left a little endian 128-bit value. */ +static void shl_128(uint64_t *val) { + val[1] <<= 1; + val[1] |= (val[0] & (1ULL << 63)) >> 63; + val[0] <<= 1; +} + + +/* Multiplies two 128-bit numbers X,Y in the GF(128) Galois Field. */ +static void gf128_mul(uint8_t *dst, const uint8_t *x, const uint8_t *y) { + uint8_t x_work[0x10]; + uint8_t y_work[0x10]; + uint8_t dst_work[0x10]; + + uint64_t *p_x = (uint64_t *)(&x_work[0]); + uint64_t *p_y = (uint64_t *)(&y_work[0]); + uint64_t *p_dst = (uint64_t *)(&dst_work[0]); + + /* Initialize buffers. */ + for (unsigned int i = 0; i < 0x10; i++) { + x_work[i] = x[0xF-i]; + y_work[i] = y[0xF-i]; + dst_work[i] = 0; + } + + /* Perform operation for each bit in y. */ + for (unsigned int round = 0; round < 0x80; round++) { + p_dst[0] ^= p_x[0] * ((y_work[0xF] & 0x80) >> 7); + p_dst[1] ^= p_x[1] * ((y_work[0xF] & 0x80) >> 7); + shl_128(p_y); + uint8_t xval = 0xE1 * (x_work[0] & 1); + shr_128(p_x); + x_work[0xF] ^= xval; + } + + for (unsigned int i = 0; i < 0x10; i++) { + dst[i] = dst_work[0xF-i]; + } +} + + + +/* Performs an AES-GCM GHASH operation over the data into dst. */ +static void ghash(void *dst, const void *data, size_t data_size, const void *j_block, bool encrypt) { + uint8_t x[0x10]; + uint8_t h[0x10]; + + uint64_t *p_x = (uint64_t *)(&x[0]); + uint64_t *p_data = (uint64_t *)data; + + memset(x, 0, 0x10); + + /* H = aes_ecb_encrypt(zeroes) */ + se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, h, 0x10, x, 0x10); + + size_t total_size = data_size; + + while (data_size >= 0x10) { + /* X = (X ^ current_block) * H */ + p_x[0] ^= p_data[0]; + p_x[1] ^= p_data[1]; + + gf128_mul(x, x, h); + + /* Increment p_data by 0x10 bytes. */ + p_data += 2; + data_size -= 0x10; + } + + /* Nintendo's code *discards all data in the last block* if unaligned. */ + /* And treats that block as though it were all-zero. */ + /* This is a bug, they just forget to XOR with the copy of the last block they save. */ + if (data_size & 0xF) { + gf128_mul(x, x, h); + } + + /* Due to a Nintendo bug, the wrong QWORD gets XOR'd in the "final output block" case. */ + if (encrypt) { + p_x[1] ^= (uint64_t)(total_size << 3); + } else { + p_x[0] ^= (uint64_t)(total_size << 3); + } + + gf128_mul(x, x, h); + + /* If final output block, XOR with encrypted J block. */ + if (encrypt) { + se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, h, 0x10, j_block, 0x10); + for (unsigned int i = 0; i < 0x10; i++) { + x[i] ^= h[i]; + } + } + + /* Copy output. */ + memcpy(dst, x, 0x10); +} + + +/* This function is a doozy. It decrypts and validates a (non-standard) AES-GCM wrapped keypair. */ +size_t gcm_decrypt_key(void *dst, size_t dst_size, const void *src, size_t src_size, const void *sealed_kek, size_t kek_size, const void *wrapped_key, size_t key_size, unsigned int usecase, bool is_personalized) { + if (is_personalized == 0) { + /* Devkit keys use a different keyformat without a MAC/Device ID. */ + if (src_size <= 0x10 || src_size - 0x10 > dst_size) { + generic_panic(); + } + } else { + if (src_size <= 0x30 || src_size - 0x20 > dst_size) { + generic_panic(); + } + } + + /* Unwrap the key */ + unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, kek_size, usecase); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, key_size); + + /* Decrypt the GCM keypair, AES-CTR with CTR = blob[:0x10]. */ + se_aes_ctr_crypt(KEYSLOT_SWITCH_TEMPKEY, dst, dst_size, src + 0x10, src_size - 0x10, src, 0x10); + + + if (!is_personalized) { + /* Devkit non-personalized keys have no further authentication. */ + return src_size - 0x10; + } + + /* J = GHASH(CTR); */ + uint8_t j_block[0x10]; + ghash(j_block, src, 0x10, NULL, false); + + /* MAC = GHASH(PLAINTEXT) ^ ENCRYPT(J) */ + /* Note: That MAC is calculated over plaintext is non-standard. */ + /* It is supposed to be over the ciphertext. */ + uint8_t calc_mac[0x10]; + ghash(calc_mac, dst, src_size - 0x20, j_block, true); + + /* Const-time memcmp. */ + const uint8_t *src_bytes = src; + int different = 0; + for (unsigned int i = 0; i < 0x10; i++) { + different |= src_bytes[src_size - 0x10 + i] ^ calc_mac[i]; + } + if (different) { + return 0; + } + + if (read64le(src_bytes, src_size - 0x28) != fuse_get_device_id()) { + return 0; + } + + return src_size - 0x30; +} diff --git a/exosphere/src/gcm.h b/exosphere/src/gcm.h new file mode 100644 index 0000000..15caa11 --- /dev/null +++ b/exosphere/src/gcm.h @@ -0,0 +1,13 @@ +#ifndef EXOSPHERE_GCM_H +#define EXOSPHERE_GCM_H + +#include +#include + +size_t gcm_decrypt_key(void *dst, size_t dst_size, + const void *src, size_t src_size, + const void *sealed_kek, size_t kek_size, + const void *wrapped_key, size_t key_size, + unsigned int usecase, bool is_personalized); + +#endif diff --git a/exosphere/src/i2c.c b/exosphere/src/i2c.c new file mode 100644 index 0000000..d987008 --- /dev/null +++ b/exosphere/src/i2c.c @@ -0,0 +1,201 @@ +#include + +#include "i2c.h" +#include "utils.h" +#include "timers.h" + +/* Prototypes for internal commands. */ +volatile i2c_registers_t *i2c_get_registers_from_id(unsigned int id); +void i2c_load_config(volatile i2c_registers_t *regs); + +bool i2c_query(unsigned int id, uint8_t device, uint8_t r, void *dst, size_t dst_size); +bool i2c_send(unsigned int id, uint8_t device, uint8_t r, void *src, size_t src_size); + +bool i2c_write(volatile i2c_registers_t *regs, uint8_t device, void *src, size_t src_size); +bool i2c_read(volatile i2c_registers_t *regs, uint8_t device, void *dst, size_t dst_size); + +/* Initialize I2C based on registers. */ +void i2c_init(unsigned int id) { + volatile i2c_registers_t *regs = i2c_get_registers_from_id(id); + + /* Setup divisor, and clear the bus. */ + regs->I2C_I2C_CLK_DIVISOR_REGISTER_0 = 0x50001; + regs->I2C_I2C_BUS_CLEAR_CONFIG_0 = 0x90003; + + /* Load hardware configuration. */ + i2c_load_config(regs); + + /* Wait a while until BUS_CLEAR_DONE is set. */ + for (unsigned int i = 0; i < 10; i++) { + wait(20000); + if (regs->I2C_INTERRUPT_STATUS_REGISTER_0 & 0x800) { + break; + } + } + + /* Read the BUS_CLEAR_STATUS. Result doesn't matter. */ + regs->I2C_I2C_BUS_CLEAR_STATUS_0; + + /* Read and set the Interrupt Status. */ + uint32_t int_status = regs->I2C_INTERRUPT_STATUS_REGISTER_0; + regs->I2C_INTERRUPT_STATUS_REGISTER_0 = int_status; +} + +/* Sets a bit in a PMIC register over I2C during CPU shutdown. */ +void i2c_send_pmic_cpu_shutdown_cmd(void) { + uint32_t val = 0; + /* PMIC == Device 4:3C. */ + i2c_query(4, 0x3C, 0x41, &val, 1); + val |= 4; + i2c_send(4, 0x3C, 0x41, &val, 1); +} + +/* Queries the value of TI charger bit over I2C. */ +bool i2c_query_ti_charger_bit_7(void) { + uint32_t val = 0; + /* TI Charger = Device 0:6B. */ + i2c_query(0, 0x6B, 0, &val, 1); + return (val & 0x80) != 0; +} + +/* Clears TI charger bit over I2C. */ +void i2c_clear_ti_charger_bit_7(void) { + uint32_t val = 0; + /* TI Charger = Device 0:6B. */ + i2c_query(0, 0x6B, 0, &val, 1); + val &= 0x7F; + i2c_send(0, 0x6B, 0, &val, 1); +} + +/* Sets TI charger bit over I2C. */ +void i2c_set_ti_charger_bit_7(void) { + uint32_t val = 0; + /* TI Charger = Device 0:6B. */ + i2c_query(0, 0x6B, 0, &val, 1); + val |= 0x80; + i2c_send(0, 0x6B, 0, &val, 1); +} + +/* Get registers pointer based on I2C ID. */ +volatile i2c_registers_t *i2c_get_registers_from_id(unsigned int id) { + switch (id) { + case 0: + return I2C1_REGS; + case 1: + return I2C2_REGS; + case 2: + return I2C3_REGS; + case 3: + return I2C4_REGS; + case 4: + return I2C5_REGS; + case 5: + return I2C6_REGS; + default: + generic_panic(); + } + return NULL; +} + +/* Load hardware config for I2C4. */ +void i2c_load_config(volatile i2c_registers_t *regs) { + /* Set MSTR_CONFIG_LOAD, TIMEOUT_CONFIG_LOAD, undocumented bit. */ + regs->I2C_I2C_CONFIG_LOAD_0 = 0x25; + + /* Wait a bit for master config to be loaded. */ + for (unsigned int i = 0; i < 20; i++) { + wait(1); + if (!(regs->I2C_I2C_CONFIG_LOAD_0 & 1)) { + break; + } + } +} + +/* Reads a register from a device over I2C, writes result to output. */ +bool i2c_query(unsigned int id, uint8_t device, uint8_t r, void *dst, size_t dst_size) { + volatile i2c_registers_t *regs = i2c_get_registers_from_id(id); + uint32_t val = r; + + /* Write single byte register ID to device. */ + if (!i2c_write(regs, device, &val, 1)) { + return false; + } + /* Limit output size to 32-bits. */ + if (dst_size > 4) { + return false; + } + + return i2c_read(regs, device, dst, dst_size); +} + +/* Writes a value to a register over I2C. */ +bool i2c_send(unsigned int id, uint8_t device, uint8_t r, void *src, size_t src_size) { + uint32_t val = r; + if (src_size <= 3) { + memcpy(((uint8_t *)&val) + 1, src, src_size); + return i2c_write(i2c_get_registers_from_id(id), device, &val, src_size + 1); + } else { + return false; + } +} + +/* Writes bytes to device over I2C. */ +bool i2c_write(volatile i2c_registers_t *regs, uint8_t device, void *src, size_t src_size) { + if (src_size > 4) { + return false; + } + + /* Set device for 7-bit write mode. */ + regs->I2C_I2C_CMD_ADDR0_0 = device << 1; + + /* Load in data to write. */ + regs->I2C_I2C_CMD_DATA1_0 = read32le(src, 0); + + /* Set config with LENGTH = src_size, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */ + regs->I2C_I2C_CNFG_0 = ((src_size << 1) - 2) | 0x2800; + + i2c_load_config(regs); + + /* Config |= SEND; */ + regs->I2C_I2C_CNFG_0 |= 0x200; + + + while (regs->I2C_I2C_STATUS_0 & 0x100) { + /* Wait until not busy. */ + } + + /* Return CMD1_STAT == SL1_XFER_SUCCESSFUL. */ + return (regs->I2C_I2C_STATUS_0 & 0xF) == 0; +} + +/* Reads bytes from device over I2C. */ +bool i2c_read(volatile i2c_registers_t *regs, uint8_t device, void *dst, size_t dst_size) { + if (dst_size > 4) { + return false; + } + + /* Set device for 7-bit read mode. */ + regs->I2C_I2C_CMD_ADDR0_0 = (device << 1) | 1; + + /* Set config with LENGTH = dst_size, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */ + regs->I2C_I2C_CNFG_0 = ((dst_size << 1) - 2) | 0x2840; + + i2c_load_config(regs); + + /* Config |= SEND; */ + regs->I2C_I2C_CNFG_0 |= 0x200; + + + while (regs->I2C_I2C_STATUS_0 & 0x100) { + /* Wait until not busy. */ + } + + /* Ensure success. */ + if ((regs->I2C_I2C_STATUS_0 & 0xF) != 0) { + return false; + } + + uint32_t val = regs->I2C_I2C_CMD_DATA1_0; + memcpy(dst, &val, dst_size); + return true; +} diff --git a/exosphere/src/i2c.h b/exosphere/src/i2c.h new file mode 100644 index 0000000..1890b59 --- /dev/null +++ b/exosphere/src/i2c.h @@ -0,0 +1,70 @@ +#ifndef EXOSPHERE_I2C_H +#define EXOSPHERE_I2C_H + +#include +#include +#include "memory_map.h" + +/* Exosphere driver for the Tegra X1 I2C registers. */ + +typedef struct { + uint32_t I2C_I2C_CNFG_0; + uint32_t I2C_I2C_CMD_ADDR0_0; + uint32_t I2C_I2C_CMD_ADDR1_0; + uint32_t I2C_I2C_CMD_DATA1_0; + uint32_t I2C_I2C_CMD_DATA2_0; + uint32_t _0x14; + uint32_t _0x18; + uint32_t I2C_I2C_STATUS_0; + uint32_t I2C_I2C_SL_CNFG_0; + uint32_t I2C_I2C_SL_RCVD_0; + uint32_t I2C_I2C_SL_STATUS_0; + uint32_t I2C_I2C_SL_ADDR1_0; + uint32_t I2C_I2C_SL_ADDR2_0; + uint32_t I2C_I2C_TLOW_SEXT_0; + uint32_t _0x38; + uint32_t I2C_I2C_SL_DELAY_COUNT_0; + uint32_t I2C_I2C_SL_INT_MASK_0; + uint32_t I2C_I2C_SL_INT_SOURCE_0; + uint32_t I2C_I2C_SL_INT_SET_0; + uint32_t _0x4C; + uint32_t I2C_I2C_TX_PACKET_FIFO_0; + uint32_t I2C_I2C_RX_FIFO_0; + uint32_t I2C_PACKET_TRANSFER_STATUS_0; + uint32_t I2C_FIFO_CONTROL_0; + uint32_t I2C_FIFO_STATUS_0; + uint32_t I2C_INTERRUPT_MASK_REGISTER_0; + uint32_t I2C_INTERRUPT_STATUS_REGISTER_0; + uint32_t I2C_I2C_CLK_DIVISOR_REGISTER_0; + uint32_t I2C_I2C_INTERRUPT_SOURCE_REGISTER_0; + uint32_t I2C_I2C_INTERRUPT_SET_REGISTER_0; + uint32_t I2C_I2C_SLV_TX_PACKET_FIFO_0; + uint32_t I2C_I2C_SLV_RX_FIFO_0; + uint32_t I2C_I2C_SLV_PACKET_STATUS_0; + uint32_t I2C_I2C_BUS_CLEAR_CONFIG_0; + uint32_t I2C_I2C_BUS_CLEAR_STATUS_0; + uint32_t I2C_I2C_CONFIG_LOAD_0; + uint32_t _0x90; + uint32_t I2C_I2C_INTERFACE_TIMING_0_0; + uint32_t I2C_I2C_INTERFACE_TIMING_1_0; + uint32_t I2C_I2C_HS_INTERFACE_TIMING_0_0; + uint32_t I2C_I2C_HS_INTERFACE_TIMING_1_0; +} i2c_registers_t; + + +#define I2C1_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_DTV_I2C234) + 0x000)) +#define I2C2_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_DTV_I2C234) + 0x400)) +#define I2C3_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_DTV_I2C234) + 0x500)) +#define I2C4_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_DTV_I2C234) + 0x700)) +#define I2C5_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_I2C56_SPI2B) + 0x000)) +#define I2C6_REGS ((volatile i2c_registers_t *)(mmio_get_device_address(MMIO_DEVID_I2C56_SPI2B) + 0x100)) + +void i2c_init(unsigned int id); + +void i2c_send_pmic_cpu_shutdown_cmd(void); + +bool i2c_query_ti_charger_bit_7(void); +void i2c_clear_ti_charger_bit_7(void); +void i2c_set_ti_charger_bit_7(void); + +#endif diff --git a/exosphere/src/interrupt.c b/exosphere/src/interrupt.c new file mode 100644 index 0000000..fd4d1df --- /dev/null +++ b/exosphere/src/interrupt.c @@ -0,0 +1,99 @@ +#include +#include + +#include "utils.h" +#include "interrupt.h" + +/* Global of registered handlers. */ +struct { + unsigned int id; + void (*handler)(void); +} g_registered_interrupts[MAX_REGISTERED_INTERRUPTS] = { {0, NULL}, {0, NULL}, {0, NULL}, {0, NULL} }; + +static unsigned int get_interrupt_id(void) { + return 0; + /* TODO */ +} + +/* Initializes the GIC. TODO: This must be called during wakeup. */ +void intr_initialize_gic(void) { + /* Setup interrupts 0-0x1F as nonsecure with highest non-secure priority. */ + GICD_IGROUPR[0] = 0xFFFFFFFF; + for (unsigned int i = 0; i < 0x20; i++) { + GICD_IPRIORITYR[i] = GIC_PRI_HIGHEST_NONSECURE; + } + + /* Setup the GICC. */ + GICC_CTLR = 0x1D9; + GICC_PMR = GIC_PRI_HIGHEST_NONSECURE; + GICC_BPR = 7; +} + +/* Sets an interrupt's group in the GICD. */ +void intr_set_group(unsigned int id, int group) { + GICD_IGROUPR[id >> 5] = (GICD_IGROUPR[id >> 5] & (~(1 << (id & 0x1F)))) | ((group & 1) << (id & 0x1F)); +} + +/* Sets an interrupt id as pending in the GICD. */ +void intr_set_pending(unsigned int id) { + GICD_ISPENDR[id >> 5] = 1 << (id & 0x1F); +} + +/* Sets an interrupt's priority in the GICD. */ +void intr_set_priority(unsigned int id, uint8_t priority) { + GICD_IPRIORITYR[id] = priority; +} + +/* Sets an interrupt's target CPU mask in the GICD. */ +void intr_set_cpu_mask(unsigned int id, uint8_t mask) { + GICD_ITARGETSR[id] = mask; +} + +/* Sets an interrupt's edge/level bits in the GICD. */ +void intr_set_edge_level(unsigned int id, int edge_level) { + GICD_ICFGR[id >> 4] = GICD_ICFGR[id >> 4] & ((~(3 << ((id & 0xF) << 1))) | (((edge_level & 1) << 1) << ((id & 0xF) << 1))); +} + +/* Sets an interrupt's enabled status in the GICD. */ +void intr_set_enabled(unsigned int id, int enabled) { + GICD_ISENABLER[id >> 5] = (enabled & 1) << (id & 0x1F); +} + +/* To be called by FIQ handler. */ +void handle_registered_interrupt(void) { + unsigned int interrupt_id = get_interrupt_id(); + if (interrupt_id <= 0xDF) { + bool found_handler = false; + for (unsigned int i = 0; i < MAX_REGISTERED_INTERRUPTS; i++) { + if (g_registered_interrupts[i].id == interrupt_id) { + found_handler = true; + g_registered_interrupts[i].handler(); + /* Mark that interrupt is done. */ + GICC_EOIR = interrupt_id; + break; + } + } + /* We must have found a handler, or something went wrong. */ + if (!found_handler) { + generic_panic(); + } + } +} + +/* Registers an interrupt into the global. */ +void intr_register_handler(unsigned int id, void (*handler)(void)) { + bool registered_handler = false; + for (unsigned int i = 0; i < MAX_REGISTERED_INTERRUPTS; i++) { + if (g_registered_interrupts[i].id == 0) { + g_registered_interrupts[i].handler = handler; + g_registered_interrupts[i].id = id; + registered_handler = true; + break; + } + } + /* Failure to register is an error condition. */ + if (!registered_handler) { + generic_panic(); + } +} + diff --git a/exosphere/src/interrupt.h b/exosphere/src/interrupt.h new file mode 100644 index 0000000..f46d778 --- /dev/null +++ b/exosphere/src/interrupt.h @@ -0,0 +1,50 @@ +#ifndef EXOSPHERE_INTERRUPT_H +#define EXOSPHERE_INTERRUPT_H + +#include +#include "memory_map.h" + +/* Exosphere driver for the Tegra X1 GIC-400 registers. */ + + +#define MAX_REGISTERED_INTERRUPTS 4 +#define INTERRUPT_ID_SECURITY_ENGINE 0x5A +#define INTERRUPT_ID_USER_SECURITY_ENGINE 0x2C + +#define GICD_BASE (mmio_get_device_address(MMIO_DEVID_GICD)) +#define GICC_BASE (mmio_get_device_address(MMIO_DEVID_GICC)) + +#define GICD_IGROUPR ((volatile uint32_t *)(GICD_BASE + 0x080ULL)) +#define GICD_ISENABLER ((volatile uint32_t *)(GICD_BASE + 0x100ULL)) +#define GICD_ISPENDR ((volatile uint32_t *)(GICD_BASE + 0x200ULL)) +#define GICD_IPRIORITYR ((volatile uint8_t *)(GICD_BASE + 0x400ULL)) +#define GICD_ITARGETSR ((volatile uint8_t *)(GICD_BASE + 0x800ULL)) +#define GICD_ICFGR ((volatile uint32_t *)(GICD_BASE + 0xC00ULL)) + +#define GICC_CTLR (*((volatile uint32_t *)(GICC_BASE + 0x0000ULL))) +#define GICC_PMR (*((volatile uint32_t *)(GICC_BASE + 0x0004ULL))) +#define GICC_BPR (*((volatile uint32_t *)(GICC_BASE + 0x0008ULL))) +#define GICC_IAR (*((volatile uint32_t *)(GICC_BASE + 0x000CULL))) +#define GICC_EOIR (*((volatile uint32_t *)(GICC_BASE + 0x0010ULL))) + +#define GIC_PRI_HIGHEST_SECURE 0x00 +#define GIC_PRI_HIGHEST_NONSECURE 0x80 + +#define GIC_GROUP_SECURE 0 +#define GIC_GROUP_NONSECURE 1 + +/* To be called by FIQ handler. */ +void handle_registered_interrupt(void); + +/* Initializes the GIC. TODO: This must be called during wakeup. */ +void intr_initialize_gic(void); + + +void intr_register_handler(unsigned int id, void (*handler)(void)); +void intr_set_group(unsigned int id, int group); +void intr_set_pending(unsigned int id); +void intr_set_priority(unsigned int id, uint8_t priority); +void intr_set_cpu_mask(unsigned int id, uint8_t mask); +void intr_set_edge_level(unsigned int id, int edge_level); +void intr_set_enabled(unsigned int id, int enabled); +#endif diff --git a/exosphere/src/lock.h b/exosphere/src/lock.h new file mode 100644 index 0000000..5a4ca4a --- /dev/null +++ b/exosphere/src/lock.h @@ -0,0 +1,25 @@ +#ifndef EXOSPHERE_LOCK_H +#define EXOSPHERE_LOCK_H + +#include + +/* Simple atomics driver for Exosphere. */ + +/* Acquire a lock. */ +static inline void lock_acquire(bool *l) { + while (__atomic_test_and_set(l, __ATOMIC_ACQUIRE)) { + /* Wait to acquire lock. */ + } +} + +/* Release a lock. */ +static inline void lock_release(bool *l) { + __atomic_clear(l, __ATOMIC_RELEASE); +} + +/* Try to acquire a lock. */ +static inline bool lock_try_acquire(bool *l) { + return __atomic_test_and_set(l, __ATOMIC_ACQUIRE); +} + +#endif \ No newline at end of file diff --git a/exosphere/src/masterkey.c b/exosphere/src/masterkey.c new file mode 100644 index 0000000..5288b94 --- /dev/null +++ b/exosphere/src/masterkey.c @@ -0,0 +1,91 @@ +#include +#include + +#include "utils.h" +#include "masterkey.h" +#include "se.h" + +unsigned int g_mkey_revision = 0; +bool g_determined_mkey_revision = false; + +uint8_t g_old_masterkeys[MASTERKEY_REVISION_MAX][0x10]; + +/* TODO: Dev keys. */ + +/* TODO: Extend with new vectors, as needed. */ +const uint8_t mkey_vectors[MASTERKEY_REVISION_MAX][0x10] = +{ + {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. */ +}; + +bool check_mkey_revision(unsigned int revision) { + uint8_t final_vector[0x10]; + + unsigned int check_keyslot = KEYSLOT_SWITCH_MASTERKEY; + if (revision > 0) { + /* Generate old master key array. */ + for (unsigned int i = revision; i > 0; i--) { + se_aes_ecb_decrypt_block(check_keyslot, g_old_masterkeys[i-1], 0x10, mkey_vectors[i], 0x10); + set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, g_old_masterkeys[i-1], 0x10); + check_keyslot = KEYSLOT_SWITCH_TEMPKEY; + } + } + + se_aes_ecb_decrypt_block(check_keyslot, final_vector, 0x10, mkey_vectors[0], 0x10); + for (unsigned int i = 0; i < 0x10; i++) { + if (final_vector[i] != 0) { + return false; + } + } + return true; +} + +void mkey_detect_revision(void) { + if (g_determined_mkey_revision) { + generic_panic(); + } + + for (unsigned int rev = 0; rev < MASTERKEY_REVISION_MAX; rev++) { + if (check_mkey_revision(rev)) { + g_determined_mkey_revision = true; + g_mkey_revision = rev; + break; + } + } + + /* We must have determined the master key, or we're not running on a Switch. */ + /* TODO: When panic is implemented, make this a really distinctive color. */ + /* Maybe bright red? */ + if (!g_determined_mkey_revision) { + generic_panic(); + } +} + +unsigned int mkey_get_revision(void) { + if (!g_determined_mkey_revision) { + generic_panic(); + } + + return g_mkey_revision; +} + +unsigned int mkey_get_keyslot(unsigned int revision) { + if (!g_determined_mkey_revision || revision >= MASTERKEY_REVISION_MAX) { + generic_panic(); + } + + if (revision > g_mkey_revision) { + generic_panic(); + } + + if (revision == g_mkey_revision) { + return KEYSLOT_SWITCH_MASTERKEY; + } else { + /* Load into a temp keyslot. */ + set_aes_keyslot(KEYSLOT_SWITCH_TEMPKEY, g_old_masterkeys[revision], 0x10); + return KEYSLOT_SWITCH_TEMPKEY; + } +} diff --git a/exosphere/src/masterkey.h b/exosphere/src/masterkey.h new file mode 100644 index 0000000..b6bd59e --- /dev/null +++ b/exosphere/src/masterkey.h @@ -0,0 +1,21 @@ +#ifndef EXOSPHERE_MASTERKEY_H +#define EXOSPHERE_MASTERKEY_H + +/* This is glue code to enable master key support across versions. */ + +/* TODO: Update to 0x5 on release of new master key. */ +#define MASTERKEY_REVISION_MAX 0x4 + +#define MASTERKEY_REVISION_100_230 0x00 +#define MASTERKEY_REVISION_300 0x01 +#define MASTERKEY_REVISION_301_302 0x02 +#define MASTERKEY_REVISION_400_CURRENT 0x03 + +/* This should be called early on in initialization. */ +void mkey_detect_revision(void); + +unsigned int mkey_get_revision(void); + +unsigned int mkey_get_keyslot(unsigned int revision); + +#endif \ No newline at end of file diff --git a/exosphere/src/mc.c b/exosphere/src/mc.c new file mode 100644 index 0000000..f76f8d8 --- /dev/null +++ b/exosphere/src/mc.c @@ -0,0 +1,104 @@ +#include + +#include "memory_map.h" +#include "mc.h" + +volatile security_carveout_t *get_carveout_by_id(unsigned int carveout) { + if (CARVEOUT_ID_MIN <= carveout && carveout <= CARVEOUT_ID_MAX) { + return (volatile security_carveout_t *)(MC_BASE + 0xC08ULL + 0x50 * (carveout - CARVEOUT_ID_MIN)); + } + generic_panic(); + return NULL; + switch (carveout) { + case 4: /* Kernel carveout */ + return (volatile security_carveout_t *)(MC_BASE + 0xCF8ULL); + case 5: /* Unused Kernel carveout */ + return (volatile security_carveout_t *)(MC_BASE + 0xD48ULL); + default: + generic_panic(); + return NULL; + } +} + +void configure_default_carveouts(void) { + /* Configure Carveout 1 (UNUSED) */ + volatile security_carveout_t *carveout = get_carveout_by_id(1); + carveout->paddr_low = 0; + carveout->paddr_high = 0; + carveout->size_big_pages = 0; + carveout->flags_0 = 0; + carveout->flags_1 = 0; + carveout->flags_2 = 0; + carveout->flags_3 = 0; + carveout->flags_4 = 0; + carveout->flags_5 = 0; + carveout->flags_6 = 0; + carveout->flags_7 = 0; + carveout->flags_8 = 0; + carveout->flags_9 = 0; + carveout->allowed_clients = 0x04000006; + + /* Configure Carveout 2 (GPU UCODE) */ + carveout = get_carveout_by_id(2); + carveout->paddr_low = 0x80020000; + carveout->paddr_high = 0; + carveout->size_big_pages = 2; /* 0x40000 */ + carveout->flags_0 = 0; + carveout->flags_1 = 0; + carveout->flags_2 = 0x3000000; + carveout->flags_3 = 0; + carveout->flags_4 = 0x300; + carveout->flags_5 = 0; + carveout->flags_6 = 0; + carveout->flags_7 = 0; + carveout->flags_8 = 0; + carveout->flags_9 = 0; + carveout->allowed_clients = 0x440167E; + + /* Configure Carveout 3 (UNUSED GPU) */ + carveout = get_carveout_by_id(3); + carveout->paddr_low = 0; + carveout->paddr_high = 0; + carveout->size_big_pages = 0; + carveout->flags_0 = 0; + carveout->flags_1 = 0; + carveout->flags_2 = 0x3000000; + carveout->flags_3 = 0; + carveout->flags_4 = 0x300; + carveout->flags_5 = 0; + carveout->flags_6 = 0; + carveout->flags_7 = 0; + carveout->flags_8 = 0; + carveout->flags_9 = 0; + carveout->allowed_clients = 0x4401E7E; + + /* Configure default Kernel carveouts based on 2.0.0+. */ + + /* Configure Carveout 4 (KERNEL_BUILTINS) */ + configure_kernel_carveout(5, 0x80060000, KERNEL_CARVEOUT_SIZE_MAX); + + /* Configure Carveout 5 (KERNEL_UNUSED) */ + configure_kernel_carveout(5, 0, 0); +} + +void configure_kernel_carveout(unsigned int carveout_id, uint64_t address, uint64_t size) { + if (carveout_id != 4 && carveout_id != 5) { + generic_panic(); + } + + volatile security_carveout_t *carveout = get_carveout_by_id(carveout_id); + carveout->paddr_low = (uint32_t)(address & 0xFFFFFFFF); + carveout->paddr_high = (uint32_t)(address >> 32); + carveout->size_big_pages = (uint32_t)(size >> 17); + carveout->flags_0 = 0x70E3407F; + carveout->flags_1 = 0x1A620880; + carveout->flags_2 = 0x303C00; + carveout->flags_3 = 0xCF0830BB; + carveout->flags_4 = 0x3; + carveout->flags_5 = 0; + carveout->flags_6 = 0; + carveout->flags_7 = 0; + carveout->flags_8 = 0; + carveout->flags_9 = 0; + carveout->allowed_clients = 0x8B; +} diff --git a/exosphere/src/mc.h b/exosphere/src/mc.h new file mode 100644 index 0000000..b26911d --- /dev/null +++ b/exosphere/src/mc.h @@ -0,0 +1,40 @@ +#ifndef EXOSPHERE_MC_H +#define EXOSPHERE_MC_H + +#include +#include "memory_map.h" + +/* Exosphere driver for the Tegra X1 Memory Controller. */ + +#define MC_BASE (mmio_get_device_address(MMIO_DEVID_MC)) + +#define CARVEOUT_ID_MIN 1 +#define CARVEOUT_ID_MAX 5 + +#define KERNEL_CARVEOUT_SIZE_MAX 0x1FFE0000 + +typedef struct { + uint32_t allowed_clients; + uint32_t paddr_low; + uint32_t paddr_high; + uint32_t size_big_pages; + uint32_t flags_0; + uint32_t flags_1; + uint32_t flags_2; + uint32_t flags_3; + uint32_t flags_4; + uint32_t flags_5; + uint32_t flags_6; + uint32_t flags_7; + uint32_t flags_8; + uint32_t flags_9; + uint8_t padding[0x28]; +} security_carveout_t; + + +volatile security_carveout_t *get_carveout_by_id(unsigned int carveout); +void configure_default_carveouts(void); +void configure_kernel_carveout(unsigned int carveout_id, uint64_t address, uint64_t size); + + +#endif diff --git a/exosphere/src/memory_map.h b/exosphere/src/memory_map.h new file mode 100644 index 0000000..1bb9dd9 --- /dev/null +++ b/exosphere/src/memory_map.h @@ -0,0 +1,310 @@ +#ifndef EXOSPHERE_MEMORY_MAP_H +#define EXOSPHERE_MEMORY_MAP_H + +#include "mmu.h" + +#define ATTRIB_MEMTYPE_NORMAL MMU_PTE_BLOCK_MEMTYPE(0) +#define ATTRIB_MEMTYPE_DEVICE MMU_PTE_BLOCK_MEMTYPE(1) + +static const struct { + uintptr_t address; + uint64_t size; + uint64_t attributes; + bool is_block_range; +} g_identity_mappings[] = { + { 0x40020000, 0x20000, 0, false }, /* iRAM-C+D (contains the secmon's coldboot crt0) */ + { 0x7C010000, 0x10000, 0, false }, /* TZRAM (contains the secmon's warmboot crt0) */ + { 0x80000000, 4u << 30, MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_NS, true }, /* DRAM (4GB) */ +}; + +static const struct { + uintptr_t pa; + size_t size; + bool is_secure; +} g_devices[] = { + { 0x50041000, 0x1000, true }, /* ARM Interrupt Distributor */ + { 0x50042000, 0x2000, true }, /* Interrupt Controller Physical CPU interface */ + { 0x70006000, 0x1000, false }, /* UART-A */ + { 0x60006000, 0x1000, false }, /* Clock and Reset */ + { 0x7000E000, 0x1000, true }, /* RTC, PMC */ + { 0x60005000, 0x1000, true }, /* TMRs, WDTs */ + { 0x6000C000, 0x1000, true }, /* System Registers */ + { 0x70012000, 0x2000, true }, /* SE */ + { 0x700F0000, 0x1000, true }, /* SYSCTR0 */ + { 0x70019000, 0x1000, true }, /* MC */ + { 0x7000F000, 0x1000, true }, /* FUSE (0x7000F800) */ + { 0x70000000, 0x4000, true }, /* MISC */ + { 0x60007000, 0x1000, true }, /* Flow Controller */ + { 0x40002000, 0x1000, true }, /* NX bootloader mailbox page */ + { 0x7000D000, 0x1000, true }, /* I2C-5,6 - SPI 2B-1 to 4 */ + { 0x6000D000, 0x1000, true }, /* GPIO-1 - GPIO-8 */ + { 0x7000C000, 0x1000, true }, /* I2C-I2C4 */ + { 0x6000F000, 0x1000, true }, /* Exception vectors */ +}; + +static const struct { + uintptr_t pa; + size_t size; + uint64_t attributes; +} g_lp0_entry_ram_segments[] = { + { 0x40020000, 0x10000, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE }, /* Encrypted TZRAM */ + { 0x40003000, 0x01000, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE }, /* LP0 entry code */ + { 0x7C010000, 0x10000, MMU_AP_PRIV_RO | ATTRIB_MEMTYPE_NORMAL }, /* TZRAM to encrypt */ +}; + +static const struct { + uintptr_t pa; + size_t size; + uint64_t attributes; +} g_warmboot_ram_segments[] = { + { 0x8000F000, 0x01000, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE }, /* Encrypted SE state for bootROM */ + { 0x80010000, 0x10000, MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_DEVICE }, /* Encrypted TZRAM for warmboot.bin */ +}; + +static const struct { + size_t tzram_offset; + size_t map_size; + size_t increment; /* for alignment, guard pages, etc. */ + bool is_code_segment; /* note: code is RWX */ +} g_tzram_segments[] = { + { 0x3000, 0x10000 - 0x2000 - 0x3000, 0x10000, true }, /* Warmboot crt0 sections and main code segment */ + { 0x10000 - 0x2000, 0x2000, 0x04000, true }, /* pk2ldr segment */ + { 0, 0, 0x02000, false }, /* SPL .bss buffer, NOT mapped at startup */ + { 0x10000 - 0x2000, 0x1000, 0x02000, false }, /* Core 0,1,2 stack */ + { 0x10000 - 0x1000, 0x1000, 0x02000, false }, /* Core 3 stack */ + { 0, 0x1000, 0x02000, true }, /* Secure Monitor exception vectors, some init stacks */ + { 0x1000, 0x1000, 0x02000, false }, /* L2 translation table */ + { 0x2000, 0x1000, 0x02000, false }, /* L3 translation table */ +}; + +#define MMIO_BASE 0x1F0080000ull +#define LP0_ENTRY_RAM_SEGMENT_BASE (MMIO_BASE + 0x000100000) +#define WARMBOOT_RAM_SEGMENT_BASE (LP0_ENTRY_RAM_SEGMENT_BASE + 0x000047000) /* increment seems to be arbitrary ? */ +#define TZRAM_SEGMENT_BASE (MMIO_BASE + 0x0001E0000) + +#define MMIO_DEVID_GICD 0 +#define MMIO_DEVID_GICC 1 +#define MMIO_DEVID_UART_A 2 +#define MMIO_DEVID_CLKRST 3 +#define MMIO_DEVID_RTC_PMC 4 +#define MMIO_DEVID_TMRs_WDTs 5 +#define MMIO_DEVID_SYSREGS 6 +#define MMIO_DEVID_SE 7 +#define MMIO_DEVID_SYSCTR0 8 +#define MMIO_DEVID_MC 9 +#define MMIO_DEVID_FUSE 10 +#define MMIO_DEVID_MISC 11 +#define MMIO_DEVID_FLOWCTRL 12 +#define MMIO_DEVID_NXBOOTLOADER_MAILBOX 13 +#define MMIO_DEVID_I2C56_SPI2B 14 +#define MMIO_DEVID_GPIO 15 +#define MMIO_DEVID_DTV_I2C234 16 +#define MMIO_DEVID_EXCEPTION_VECTORS 17 + +#define LP0_ENTRY_RAM_SEGMENT_ID_DECRYPTED_TZRAM 0 +#define LP0_ENTRY_RAM_SEGMENT_ID_LP0_ENTRY_CODE 1 +#define LP0_ENTRY_RAM_SEGMENT_ID_CURRENT_TZRAM 2 + +#define WARMBOOT_RAM_SEGMENT_ID_SE_STATE 0 +#define WARMBOOT_RAM_SEGMENT_ID_TZRAM 1 + +#define TZRAM_SEGMENT_ID_WARMBOOT_CRT0_AND_MAIN 0 +#define TZRAM_SEGMENT_ID_PK2LDR 1 +#define TZRAM_SEGMENT_ID_USERPAGE 2 +#define TZRAM_SEGMENT_ID_CORE012_STACK 3 +#define TZRAM_SEGMENT_ID_CORE3_STACK 4 +#define TZRAM_SEGEMENT_ID_SECMON_EVT 5 +#define TZRAM_SEGMENT_ID_L2_TRANSLATION_TABLE 6 +#define TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE 7 + +/**********************************************************************************************/ + +static inline void identity_map_all_mappings(uintptr_t *mmu_l1_tbl, uintptr_t *mmu_l3_tbl) { + static uint64_t base_attributes = MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_NORMAL; + for(size_t i = 0; i < sizeof(g_identity_mappings) / sizeof(g_identity_mappings[0]); i++) { + uint64_t attributes = base_attributes | g_identity_mappings[i].attributes; + if(g_identity_mappings[i].is_block_range) { + mmu_map_block_range(1, mmu_l1_tbl, g_identity_mappings[i].address, g_identity_mappings[i].address, + g_identity_mappings[i].size, attributes); + } + else { + mmu_map_page_range(mmu_l3_tbl, g_identity_mappings[i].address, g_identity_mappings[i].address, + g_identity_mappings[i].size, attributes); + } + } +} + +static inline void identity_unmap_all_mappings(uintptr_t *mmu_l1_tbl, uintptr_t *mmu_l3_tbl) { + for(size_t i = 0; i < sizeof(g_identity_mappings) / sizeof(g_identity_mappings[0]); i++) { + if(g_identity_mappings[i].is_block_range) { + mmu_unmap_range(1, mmu_l1_tbl, g_identity_mappings[i].address, g_identity_mappings[i].size); + } + else { + mmu_unmap_range(3, mmu_l3_tbl, g_identity_mappings[i].address, g_identity_mappings[i].size); + } + } +} + +/**********************************************************************************************/ + +static inline uintptr_t mmio_get_device_pa(unsigned int device_id) { + return g_devices[device_id].pa; +} + +#ifndef MEMORY_MAP_USE_IDENTIY_MAPPING +static inline uintptr_t mmio_get_device_address(unsigned int device_id) { + size_t offset = 0; + for(unsigned int i = 0; i < device_id; i++) { + offset += g_devices[i].size; + offset += 0x1000; /* guard page */ + } + + return MMIO_BASE + offset; +} + +#else +static inline uintptr_t mmio_get_device_address(unsigned int device_id) { + return mmio_get_device_pa(device_id); +} +#endif + +static inline void mmio_map_all_devices(uintptr_t *mmu_l3_tbl) { + static const uint64_t secure_device_attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_DEVICE; + static const uint64_t device_attributes = MMU_PTE_BLOCK_NS | secure_device_attributes; + + for(size_t i = 0, offset = 0; i < sizeof(g_devices) / sizeof(g_devices[0]); i++) { + uint64_t attributes = g_devices[i].is_secure ? secure_device_attributes : device_attributes; + mmu_map_page_range(mmu_l3_tbl, MMIO_BASE + offset, g_devices[i].pa, g_devices[i].size, attributes); + + offset += g_devices[i].size; + offset += 0x1000; /* insert guard page */ + } +} + +static inline void mmio_unmap_all_devices(uintptr_t *mmu_l3_tbl) { + for(size_t i = 0, offset = 0; i < sizeof(g_devices) / sizeof(g_devices[0]); i++) { + mmu_unmap_range(3, mmu_l3_tbl, MMIO_BASE + offset, g_devices[i].size); + + offset += g_devices[i].size; + offset += 0x1000; /* insert guard page */ + } +} + +/**********************************************************************************************/ + +static inline uintptr_t lp0_get_plaintext_ram_segment_pa(unsigned int segment_id) { + return g_lp0_entry_ram_segments[segment_id].pa; +} + +#ifndef MEMORY_MAP_USE_IDENTIY_MAPPING +static inline uintptr_t lp0_get_plaintext_ram_segment_address(unsigned int segment_id) { + return LP0_ENTRY_RAM_SEGMENT_BASE + 0x10000 * segment_id; +} +#else +static inline uintptr_t lp0_get_plaintext_ram_segment_address(unsigned int segment_id) { + return lp0_get_plaintext_ram_segment_pa(segment_id); +} +#endif + +static inline void lp0_map_all_plaintext_ram_segments(uintptr_t *mmu_l3_tbl) { + for(size_t i = 0, offset = 0; i < sizeof(g_lp0_entry_ram_segments) / sizeof(g_lp0_entry_ram_segments[0]); i++) { + uint64_t attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | g_lp0_entry_ram_segments[i].attributes; + mmu_map_page_range(mmu_l3_tbl, LP0_ENTRY_RAM_SEGMENT_BASE + offset, g_lp0_entry_ram_segments[i].pa, + g_lp0_entry_ram_segments[i].size, attributes); + offset += 0x10000; + } +} + +static inline void lp0_unmap_all_plaintext_ram_segments(uintptr_t *mmu_l3_tbl) { + for(size_t i = 0, offset = 0; i < sizeof(g_lp0_entry_ram_segments) / sizeof(g_lp0_entry_ram_segments[0]); i++) { + mmu_unmap_range(3, mmu_l3_tbl, LP0_ENTRY_RAM_SEGMENT_BASE + offset, g_lp0_entry_ram_segments[i].size); + + offset += 0x10000; + } +} + +/**********************************************************************************************/ + +static inline uintptr_t lp0_get_ciphertext_ram_segment_pa(unsigned int segment_id) { + return g_warmboot_ram_segments[segment_id].pa; +} + +#ifndef MEMORY_MAP_USE_IDENTIY_MAPPING +static inline uintptr_t lp0_get_ciphertext_ram_segment_address(unsigned int segment_id) { + size_t offset = 0; + for(unsigned int i = 0; i < segment_id; i++) { + offset += g_warmboot_ram_segments[i].size; + } + + return WARMBOOT_RAM_SEGMENT_BASE + offset; +} +#else +static inline uintptr_t lp0_get_ciphertext_ram_segment_address(unsigned int segment_id) { + return lp0_get_ciphertext_ram_segment_pa(segment_id); +} +#endif + +static inline void lp0_map_all_ciphertext_ram_segments(uintptr_t *mmu_l3_tbl) { + for(size_t i = 0, offset = 0; i < sizeof(g_warmboot_ram_segments) / sizeof(g_warmboot_ram_segments[0]); i++) { + uint64_t attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | g_warmboot_ram_segments[i].attributes; + mmu_map_page_range(mmu_l3_tbl, WARMBOOT_RAM_SEGMENT_BASE + offset, g_warmboot_ram_segments[i].pa, + g_warmboot_ram_segments[i].size, attributes); + offset += g_warmboot_ram_segments[i].size; + } +} + +static inline void lp0_unmap_all_ciphertext_ram_segments(uintptr_t *mmu_l3_tbl) { + for(size_t i = 0, offset = 0; i < sizeof(g_warmboot_ram_segments) / sizeof(g_warmboot_ram_segments[0]); i++) { + mmu_unmap_range(3, mmu_l3_tbl, WARMBOOT_RAM_SEGMENT_BASE + offset, g_warmboot_ram_segments[i].size); + + offset += g_warmboot_ram_segments[i].size; + } +} + +/**********************************************************************************************/ + +static inline uintptr_t tzram_get_segment_pa(unsigned int segment_id) { + return 0x7C010000 + g_tzram_segments[segment_id].tzram_offset; +} + +#ifndef MEMORY_MAP_USE_IDENTIY_MAPPING +static inline uintptr_t tzram_get_segment_address(unsigned int segment_id) { + size_t offset = 0; + for(unsigned int i = 0; i < segment_id; i++) { + offset += g_tzram_segments[i].increment; + } + + return TZRAM_SEGMENT_BASE + offset; +} +#else +static inline uintptr_t tzram_get_segment_address(unsigned int segment_id) { + return tzram_get_segment_pa(segment_id); +} +#endif + +static inline void tzram_map_all_segments(uintptr_t *mmu_l3_tbl) { + /* Except the SPL userpage */ + for(size_t i = 0, offset = 0; i < sizeof(g_tzram_segments) / sizeof(g_tzram_segments[0]); i++) { + uint64_t attributes = (g_tzram_segments[i].is_code_segment ? 0 : MMU_PTE_BLOCK_XN) | MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_NORMAL; + if(g_tzram_segments[i].map_size == 0) { + continue; + } + mmu_map_page_range(mmu_l3_tbl, TZRAM_SEGMENT_BASE + offset, 0x7C010000 + g_tzram_segments[i].tzram_offset, + g_tzram_segments[i].map_size, attributes); + offset += g_tzram_segments[i].increment; + } +} + +static inline void tzram_unmap_all_segments(uintptr_t *mmu_l3_tbl) { + /* Except the SPL userpage */ + for(size_t i = 0, offset = 0; i < sizeof(g_warmboot_ram_segments) / sizeof(g_warmboot_ram_segments[0]); i++) { + if(g_tzram_segments[i].map_size == 0) { + continue; + } + mmu_unmap_range(3, mmu_l3_tbl, TZRAM_SEGMENT_BASE + offset, g_tzram_segments[i].map_size); + + offset += g_tzram_segments[i].increment; + } +} + +#endif diff --git a/exosphere/src/mmu.h b/exosphere/src/mmu.h new file mode 100644 index 0000000..391e0b9 --- /dev/null +++ b/exosphere/src/mmu.h @@ -0,0 +1,173 @@ +#ifndef EXOSPHERE_MMU_H +#define EXOSPHERE_MMU_H + +#include +#include +#include +#include "utils.h" + + +#ifndef MMU_GRANULE_TYPE +#define MMU_GRANULE_TYPE 0 /* 0: 4KB, 1: 64KB, 2: 16KB. The Switch always uses a 4KB granule size. */ +#endif + +#if MMU_GRANULE_TYPE == 0 +#define MMU_Lx_SHIFT(x) (12 + 9 * (3 - (x))) +#define MMU_Lx_MASK(x) (BIT(9) - 1) +#elif MMU_GRANULE_TYPE == 1 +/* 64 KB, no L0 here */ +#define MMU_Lx_SHIFT(x) (16 + 13 * (3 - (x))) +#define MMU_Lx_MASK(x) ((x) == 1 ? (BIT(5) - 1) : (BIT(13) - 1)) +#elif MMU_GRANULE_TYPE == 2 +#define MMU_Lx_SHIFT(x) (14 + 11 * (3 - (x))) +#define MMU_Lx_MASK(x) ((x) == 0 ? 1 : (BIT(11) - 1)) +#endif + +/* + * The following defines are adapted from uboot: + * + * (C) Copyright 2013 + * David Feng + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define MMU_MT_DEVICE_NGNRNE 0 +#define MMU_MT_DEVICE_NGNRE 1 +#define MMU_MT_DEVICE_GRE 2 +#define MMU_MT_NORMAL_NC 3 +#define MMU_MT_NORMAL 4 + +/* + * Hardware page table definitions. + * + */ + +#define MMU_PTE_TYPE_MASK 3 +#define MMU_PTE_TYPE_FAULT 0 +#define MMU_PTE_TYPE_TABLE 3 +#define MMU_PTE_TYPE_BLOCK 1 + +/* L3 only */ +#define MMU_PTE_TYPE_PAGE 3 + +#define MMU_PTE_TABLE_PXN BITL(59) +#define MMU_PTE_TABLE_XN BITL(60) +#define MMU_PTE_TABLE_AP BITL(61) +#define MMU_PTE_TABLE_NS BITL(63) + +/* + * Block + */ +#define MMU_PTE_BLOCK_MEMTYPE(x) ((x) << 2) +#define MMU_PTE_BLOCK_NS BIT(5) +#define MMU_PTE_BLOCK_NON_SHAREABLE (0 << 8) +#define MMU_PTE_BLOCK_OUTER_SHAREABLE (2 << 8) +#define MMU_PTE_BLOCK_INNER_SHAREBLE (3 << 8) +#define MMU_PTE_BLOCK_AF BIT(10) +#define MMU_PTE_BLOCK_NG BIT(11) +#define MMU_PTE_BLOCK_PXN BITL(53) +#define MMU_PTE_BLOCK_UXN BITL(54) +#define MMU_PTE_BLOCK_XN MMU_PTE_BLOCK_UXN + +/* + * AP[2:1] + */ +#define MMU_AP_PRIV_RW (0 << 6) +#define MMU_AP_RW (1 << 6) +#define MMU_AP_PRIV_RO (2 << 6) +#define MMU_AP_RO (3 << 6) + +/* + * S2AP[2:1] (for stage2 translations; secmon doesn't use it) + */ +#define MMU_S2AP_NONE (0 << 6) +#define MMU_S2AP_RO (1 << 6) +#define MMU_S2AP_WO (2 << 6) +#define MMU_S2AP_RW (3 << 6) + +/* + * AttrIndx[2:0] + */ +#define MMU_PMD_ATTRINDX(t) ((t) << 2) +#define MMU_PMD_ATTRINDX_MASK (7 << 2) + +/* + * TCR flags. + */ +#define TCR_T0SZ(x) ((64 - (x)) << 0) +#define TCR_IRGN_NC (0 << 8) +#define TCR_IRGN_WBWA (1 << 8) +#define TCR_IRGN_WT (2 << 8) +#define TCR_IRGN_WBNWA (3 << 8) +#define TCR_IRGN_MASK (3 << 8) +#define TCR_ORGN_NC (0 << 10) +#define TCR_ORGN_WBWA (1 << 10) +#define TCR_ORGN_WT (2 << 10) +#define TCR_ORGN_WBNWA (3 << 10) +#define TCR_ORGN_MASK (3 << 10) +#define TCR_NOT_SHARED (0 << 12) +#define TCR_SHARED_OUTER (2 << 12) +#define TCR_SHARED_INNER (3 << 12) +#define TCR_TG0_4K (0 << 14) +#define TCR_TG0_64K (1 << 14) +#define TCR_TG0_16K (2 << 14) +#define TCR_EPD1_DISABLE BIT(23) + +#define TCR_EL1_RSVD BIT(31) +#define TCR_EL2_RSVD (BIT(31) | BIT(23)) +#define TCR_EL3_RSVD (BIT(31) | BIT(23)) + +static inline void mmu_init_table(uintptr_t *tbl, size_t num_entries) { + for(size_t i = 0; i < num_entries; i++) { + tbl[i] = MMU_PTE_TYPE_FAULT; + } +} + +/* + All the functions below assume base_addr is valid. + They do not invalidate the TLB, which must be done separately. +*/ + +static inline unsigned int mmu_compute_index(unsigned int level, uintptr_t base_addr) { + return (base_addr >> MMU_Lx_SHIFT(level)) & MMU_Lx_MASK(level); +} + +static inline void mmu_map_table(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, uintptr_t *next_lvl_tbl_pa, uint64_t attrs) { + tbl[mmu_compute_index(level, base_addr)] = (uintptr_t)next_lvl_tbl_pa | attrs | MMU_PTE_TYPE_TABLE; +} + +static inline void mmu_map_block(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, uint64_t attrs) { + tbl[mmu_compute_index(level, base_addr)] = phys_addr | attrs | MMU_PTE_BLOCK_AF | MMU_PTE_TYPE_BLOCK; +} + +static inline void mmu_map_page(uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, uint64_t attrs) { + tbl[mmu_compute_index(3, base_addr)] = phys_addr | attrs | MMU_PTE_BLOCK_AF | MMU_PTE_TYPE_PAGE; +} + +static inline void mmu_unmap(unsigned int level, uintptr_t *tbl, uintptr_t base_addr) { + tbl[mmu_compute_index(level, base_addr)] = MMU_PTE_TYPE_FAULT; +} + +static inline void mmu_map_block_range(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, size_t size, uint64_t attrs) { + size = ((size + (BITL(MMU_Lx_SHIFT(level)) - 1)) >> MMU_Lx_SHIFT(level)) << MMU_Lx_SHIFT(level); + for(size_t offset = 0; offset < size; offset += MMU_Lx_SHIFT(level)) { + mmu_map_block(level, tbl, base_addr + offset, phys_addr + offset, attrs); + } +} + +static inline void mmu_map_page_range(uintptr_t *tbl, uintptr_t base_addr, uintptr_t phys_addr, size_t size, uint64_t attrs) { + size = ((size + (BITL(MMU_Lx_SHIFT(3)) - 1)) >> MMU_Lx_SHIFT(3)) << MMU_Lx_SHIFT(3); + for(size_t offset = 0; offset < size; offset += MMU_Lx_SHIFT(3)) { + mmu_map_page(tbl, base_addr + offset, phys_addr + offset, attrs); + } +} + +static inline void mmu_unmap_range(unsigned int level, uintptr_t *tbl, uintptr_t base_addr, size_t size) { + size = ((size + (BITL(MMU_Lx_SHIFT(level)) - 1)) >> MMU_Lx_SHIFT(level)) << MMU_Lx_SHIFT(level); + for(size_t offset = 0; offset < size; offset += MMU_Lx_SHIFT(level)) { + mmu_unmap(level, tbl, base_addr + offset); + } +} + +#endif diff --git a/exosphere/src/package2.c b/exosphere/src/package2.c new file mode 100644 index 0000000..7c96d1e --- /dev/null +++ b/exosphere/src/package2.c @@ -0,0 +1,451 @@ +#include + +#include "utils.h" +#include "memory_map.h" + +#include "cpu_context.h" +#include "package2.h" +#include "configitem.h" +#include "se.h" +#include "masterkey.h" +#include "cache.h" +#include "randomcache.h" +#include "timers.h" + +/* Hardware init, sets up the RNG and SESSION keyslots, derives new DEVICE key. */ +static void setup_se(void) { + uint8_t work_buffer[0x10]; + + /* Sanity check the Security Engine. */ + se_verify_flags_cleared(); + se_clear_interrupts(); + + /* Perform some sanity initialization. */ + volatile security_engine_t *p_security_engine = get_security_engine_address(); + p_security_engine->_0x4 = 0; + p_security_engine->AES_KEY_READ_DISABLE_REG = 0; + p_security_engine->RSA_KEY_READ_DISABLE_REG = 0; + p_security_engine->_0x0 &= 0xFFFFFFFB; + + /* Currently unknown what each flag does. */ + for (unsigned int i = 0; i < KEYSLOT_AES_MAX; i++) { + set_aes_keyslot_flags(i, 0x15); + } + + for (unsigned int i = 4; i < KEYSLOT_AES_MAX; i++) { + set_aes_keyslot_flags(i, 0x40); + } + + for (unsigned int i = 0; i < KEYSLOT_RSA_MAX; i++) { + set_rsa_keyslot_flags(i, 0x41); + } + + /* Detect Master Key revision. */ + mkey_detect_revision(); + + /* Setup new device key, if necessary. */ + if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + const uint8_t new_devicekey_source_4x[0x10] = {0x8B, 0x4E, 0x1C, 0x22, 0x42, 0x07, 0xC8, 0x73, 0x56, 0x94, 0x08, 0x8B, 0xCC, 0x47, 0x0F, 0x5D}; + se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY, work_buffer, 0x10, new_devicekey_source_4x, 0x10); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_DEVICEKEY, KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY, work_buffer, 0x10); + clear_aes_keyslot(KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY); + set_aes_keyslot_flags(KEYSLOT_SWITCH_DEVICEKEY, 0xFF); + } + + se_initialize_rng(KEYSLOT_SWITCH_DEVICEKEY); + + /* Generate random data, transform with device key to get RNG key. */ + se_generate_random(KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_RNGKEY, KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); + set_aes_keyslot_flags(KEYSLOT_SWITCH_RNGKEY, 0xFF); + + /* Repeat for Session key. */ + se_generate_random(KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_SESSIONKEY, KEYSLOT_SWITCH_DEVICEKEY, work_buffer, 0x10); + set_aes_keyslot_flags(KEYSLOT_SWITCH_SESSIONKEY, 0xFF); + + /* TODO: Create Test Vector, to validate keyslot data is unchanged post warmboot. */ +} + +static void setup_boot_config(void) { + /* Load boot config only if dev unit. */ + if (configitem_is_retail()) { + bootconfig_clear(); + } else { + flush_dcache_range((uint8_t *)NX_BOOTLOADER_BOOTCONFIG_POINTER, (uint8_t *)NX_BOOTLOADER_BOOTCONFIG_POINTER + sizeof(bootconfig_t)); + bootconfig_load_and_verify((bootconfig_t *)NX_BOOTLOADER_BOOTCONFIG_POINTER); + } +} + +static bool rsa2048_pss_verify(const void *signature, size_t signature_size, const void *modulus, size_t modulus_size, const void *data, size_t data_size) { + uint8_t message[RSA_2048_BYTES]; + uint8_t h_buf[0x24]; + + /* Hardcode RSA with keyslot 0. */ + const uint8_t public_exponent[4] = {0x00, 0x01, 0x00, 0x01}; + set_rsa_keyslot(0, modulus, modulus_size, public_exponent, sizeof(public_exponent)); + se_synchronous_exp_mod(0, message, sizeof(message), signature, signature_size); + + /* Validate sanity byte. */ + if (message[RSA_2048_BYTES - 1] != 0xBC) { + return false; + } + + /* Copy Salt into MGF1 Hash Buffer. */ + memset(h_buf, 0, sizeof(h_buf)); + memcpy(h_buf, message + RSA_2048_BYTES - 0x20 - 0x1, 0x20); + + /* Decrypt maskedDB (via inline MGF1). */ + uint8_t seed = 0; + uint8_t mgf1_buf[0x20]; + for (unsigned int ofs = 0; ofs < RSA_2048_BYTES - 0x20 - 1; ofs += 0x20) { + h_buf[sizeof(h_buf) - 1] = seed++; + flush_dcache_range(h_buf, h_buf + sizeof(h_buf)); + se_calculate_sha256(mgf1_buf, h_buf, sizeof(h_buf)); + for (unsigned int i = ofs; i < ofs + 0x20 && i < RSA_2048_BYTES - 0x20 - 1; i++) { + message[i] ^= mgf1_buf[i - ofs]; + } + } + + /* Constant lmask for rsa-2048-pss. */ + message[0] &= 0x7F; + + /* Validate DB is of the form 0000...0001. */ + for (unsigned int i = 0; i < RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1; i++) { + if (message[i] != 0) { + return false; + } + } + if (message[RSA_2048_BYTES - 0x20 - 0x20 - 1 - 1] != 1) { + return false; + } + + /* Check hash correctness. */ + uint8_t validate_buf[8 + 0x20 + 0x20]; + uint8_t validate_hash[0x20]; + + memset(validate_buf, 0, sizeof(validate_buf)); + flush_dcache_range((uint8_t *)data, (uint8_t *)data + data_size); + se_calculate_sha256(&validate_buf[8], data, data_size); + memcpy(&validate_buf[0x28], &message[RSA_2048_BYTES - 0x20 - 0x20 - 1], 0x20); + flush_dcache_range(validate_buf, validate_buf + sizeof(validate_buf)); + se_calculate_sha256(validate_hash, validate_buf, sizeof(validate_buf)); + return memcmp(h_buf, validate_hash, 0x20) == 0; +} + +static void package2_crypt_ctr(unsigned int master_key_rev, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size) { + /* Derive package2 key. */ + const uint8_t package2_key_source[0x10] = {0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7}; + flush_dcache_range((uint8_t *)dst, (uint8_t *)dst + dst_size); + flush_dcache_range((uint8_t *)src, (uint8_t *)src + src_size); + unsigned int keyslot = mkey_get_keyslot(master_key_rev); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_PACKAGE2KEY, keyslot, package2_key_source, 0x10); + + /* Perform Encryption. */ + se_aes_ctr_crypt(KEYSLOT_SWITCH_PACKAGE2KEY, dst, dst_size, src, src_size, ctr, ctr_size); +} + + +static void verify_header_signature(package2_header_t *header) { + const uint8_t *modulus; + + if (configitem_is_retail()) { + const uint8_t package2_modulus_retail[0x100] = { + 0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59, + 0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE, + 0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5, + 0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74, + 0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48, + 0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE, + 0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32, + 0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A, + 0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B, + 0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3, + 0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9, + 0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47, + 0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D, + 0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2, + 0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF, + 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F + }; + modulus = package2_modulus_retail; + } else { + const uint8_t package2_modulus_dev[0x100] = { + 0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99, + 0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7, + 0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6, + 0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38, + 0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83, + 0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0, + 0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6, + 0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50, + 0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7, + 0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42, + 0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E, + 0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8, + 0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7, + 0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4, + 0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46, + 0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B + }; + modulus = package2_modulus_dev; + } + + /* This is normally only allowed on dev units, but we'll allow it anywhere. */ + if (bootconfig_is_package2_unsigned() == 0 && rsa2048_pss_verify(header->signature, 0x100, modulus, 0x100, header->encrypted_header, 0x100) == 0) { + generic_panic(); + } +} + +static bool validate_package2_metadata(package2_meta_t *metadata) { + if (metadata->magic != MAGIC_PK21) { + return false; + } + + /* Package2 size, version number is stored XORed in header CTR. */ + /* Nintendo, what the fuck? */ + uint32_t package_size = metadata->ctr_dwords[0] ^ metadata->ctr_dwords[2] ^ metadata->ctr_dwords[3]; + uint8_t header_version = (uint8_t)((metadata->ctr_dwords[1] ^ (metadata->ctr_dwords[1] >> 16) ^ (metadata->ctr_dwords[1] >> 24)) & 0xFF); + + /* Ensure package isn't too big or too small. */ + if (package_size <= sizeof(package2_header_t) || package_size > PACKAGE2_SIZE_MAX - sizeof(package2_header_t)) { + return false; + } + + /* Validate that we're working with a header we know how to handle. */ + if (header_version > MASTERKEY_REVISION_MAX) { + return false; + } + + /* Require aligned entrypoint. */ + if (metadata->entrypoint & 3) { + return false; + } + + /* Validate section size sanity. */ + if (metadata->section_sizes[0] + metadata->section_sizes[1] + metadata->section_sizes[2] + sizeof(package2_header_t) != package_size) { + return false; + } + + bool entrypoint_found = false; + + /* Header has space for 4 sections, but only 3 are validated/potentially loaded on hardware. */ + for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { + /* Validate section size alignment. */ + if (metadata->section_sizes[section] & 3) { + return false; + } + + /* Validate section does not overflow. */ + if (check_32bit_additive_overflow(metadata->section_offsets[section], metadata->section_sizes[section])) { + return false; + } + + /* Check for entrypoint presence. */ + uint32_t section_end = metadata->section_offsets[section] + metadata->section_sizes[section]; + if (metadata->section_offsets[section] <= metadata->entrypoint && metadata->entrypoint < section_end) { + entrypoint_found = true; + } + + /* Ensure no overlap with later sections. */ + for (unsigned int later_section = section + 1; later_section < PACKAGE2_SECTION_MAX; later_section++) { + uint32_t later_section_end = metadata->section_offsets[later_section] + metadata->section_sizes[later_section]; + if (overlaps(metadata->section_offsets[section], section_end, metadata->section_offsets[later_section], later_section_end)) { + return false; + } + } + + /* Validate section hashes. */ + void *section_data = (void *)((uint8_t *)NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS + sizeof(package2_header_t) + metadata->section_offsets[section]); + uint8_t calculated_hash[0x20]; + se_calculate_sha256(calculated_hash, section_data, metadata->section_sizes[section]); + if (memcmp(calculated_hash, metadata->section_hashes[section], sizeof(metadata->section_hashes[section])) != 0) { + return false; + } + } + + /* Ensure that entrypoint is present in one of our sections. */ + if (!entrypoint_found) { + return false; + } + + /* Perform version checks. */ + /* We will be compatible with all package2s released before current, but not newer ones. */ + if (metadata->version_max >= PACKAGE2_MINVER_THEORETICAL && metadata->version_min < PACKAGE2_MAXVER_400_CURRENT) { + return false; + } + + return true; +} + +/* Decrypts package2 header, and returns the master key revision required. */ +static uint32_t decrypt_and_validate_header(package2_header_t *header) { + package2_meta_t metadata; + + if (bootconfig_is_package2_plaintext() == 0) { + uint32_t mkey_rev; + + /* Try to decrypt for all possible master keys. */ + for (mkey_rev = 0; mkey_rev < MASTERKEY_REVISION_MAX; mkey_rev++) { + package2_crypt_ctr(mkey_rev, &metadata, sizeof(package2_meta_t), &header->metadata, sizeof(package2_meta_t), header->metadata.ctr, sizeof(header->metadata.ctr)); + /* Copy the ctr (which stores information) into the decrypted metadata. */ + memcpy(metadata.ctr, header->metadata.ctr, sizeof(header->metadata.ctr)); + /* See if this is the correct key. */ + if (validate_package2_metadata(&metadata)) { + header->metadata = metadata; + break; + } + } + + /* Ensure we successfully decrypted the header. */ + generic_panic(); + } + return 0; +} + +static void load_package2_sections(package2_meta_t *metadata, uint32_t master_key_rev) { + /* By default, copy data directly from where NX_BOOTLOADER puts it. */ + void *load_buf = NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS; + + /* Check whether any of our sections overlap this region. If they do, we must relocate and copy from elsewhere. */ + bool needs_relocation = false; + for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { + uint64_t section_start = DRAM_BASE_PHYSICAL + (uint64_t)metadata->section_offsets[section]; + uint64_t section_end = section_start + (uint64_t)metadata->section_sizes[section]; + if (overlaps(section_start, section_end, (uint64_t)(NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS), (uint64_t)(NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS) + PACKAGE2_SIZE_MAX)) { + needs_relocation = true; + } + } + if (needs_relocation) { + /* This code should *always* succeed in finding a carveout within seven loops, */ + /* due to the section size limit, and section number limit. */ + /* However, Nintendo tries panics after 8 loops if a safe section is not found. */ + /* This should never be the case, mathematically. */ + /* We will replicate this behavior. */ + bool found_safe_carveout = false; + uint64_t potential_base_start = DRAM_BASE_PHYSICAL; + uint64_t potential_base_end = potential_base_start + PACKAGE2_SIZE_MAX; + for (unsigned int i = 0; i < 8; i++) { + int is_safe = 1; + for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { + uint64_t section_start = DRAM_BASE_PHYSICAL + (uint64_t)metadata->section_offsets[section]; + uint64_t section_end = section_start + (uint64_t)metadata->section_sizes[section]; + if (overlaps(section_start, section_end, potential_base_start, potential_base_end)) { + is_safe = 0; + } + } + found_safe_carveout |= is_safe; + if (found_safe_carveout) { + break; + } + potential_base_start += PACKAGE2_SIZE_MAX; + potential_base_end += PACKAGE2_SIZE_MAX; + } + if (!found_safe_carveout) { + generic_panic(); + } + /* Relocate to new carveout. */ + memcpy((void *)potential_base_start, load_buf, PACKAGE2_SIZE_MAX); + memset(load_buf, 0, PACKAGE2_SIZE_MAX); + load_buf = (void *)potential_base_start; + } + + /* Copy each section to its appropriate location, decrypting if necessary. */ + for (unsigned int section = 0; section < PACKAGE2_SECTION_MAX; section++) { + if (metadata->section_sizes[section] == 0) { + continue; + } + + void *dst_start = (void *)(DRAM_BASE_PHYSICAL + (uint64_t)metadata->section_offsets[section]); + void *src_start = load_buf + sizeof(package2_header_t) + metadata->section_offsets[section]; + size_t size = (size_t)metadata->section_sizes[section]; + + if (bootconfig_is_package2_plaintext()) { + memcpy(dst_start, src_start, size); + } else { + package2_crypt_ctr(master_key_rev, dst_start, size, src_start, size, metadata->section_ctrs[section], 0x10); + } + } + + /* Clear the encrypted package2 from memory. */ + memset(load_buf, 0, PACKAGE2_SIZE_MAX); +} + +uintptr_t get_pk2ldr_stack_address(void) { + return tzram_get_segment_address(TZRAM_SEGMENT_ID_PK2LDR) + 0x2000; +} + +/* This function is called during coldboot init, and validates a package2. */ +/* This package2 is read into memory by a concurrent BPMP bootloader. */ +void load_package2(void) { + /* Setup the Security Engine. */ + setup_se(); + + /* TODO: bootup_misc_mmio(). */ + /* This func will also be called on warmboot. */ + /* And will verify stored SE Test Vector, clear keyslots, */ + /* Generate an SRK, set the warmboot firmware location, */ + /* Configure the GPU uCode carveout, configure the Kernel default carveouts, */ + /* Initialize the PMC secure scratch registers, initialize MISC registers, */ + /* And assign "se_operation_completed" to Interrupt 0x5A. */ + + /* TODO: Read and save BOOTREASON stored by NX_BOOTLOADER at 0x1F009FE00 */ + + /* Initialize cache'd random bytes for kernel. */ + randomcache_init(); + + /* TODO: memclear the initial copy of Exosphere running in IRAM (relocated to TZRAM by earlier code). */ + + /* Let NX Bootloader know that we're running. */ + MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE = 1; + + /* Synchronize with NX BOOTLOADER. */ + if (MAILBOX_NX_BOOTLOADER_SETUP_STATE == NX_BOOTLOADER_STATE_INIT) { + while (MAILBOX_NX_BOOTLOADER_SETUP_STATE < NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG) { + wait(1); + } + } + + /* Load Boot Config into global. */ + setup_boot_config(); + + /* Synchronize with NX BOOTLOADER. */ + if (MAILBOX_NX_BOOTLOADER_SETUP_STATE == NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG) { + while (MAILBOX_NX_BOOTLOADER_SETUP_STATE < NX_BOOTLOADER_STATE_LOADED_PACKAGE2) { + wait(1); + } + } + + /* Load header from NX_BOOTLOADER-initialized DRAM. */ + package2_header_t header; + flush_dcache_range((uint8_t *)NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, (uint8_t *)NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS + sizeof(header)); + memcpy(&header, NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS, sizeof(header)); + flush_dcache_range((uint8_t *)&header, (uint8_t *)&header + sizeof(header)); + + /* Perform signature checks. */ + verify_header_signature(&header); + + /* Decrypt header, get key revision required. */ + uint32_t package2_mkey_rev = decrypt_and_validate_header(&header); + + /* Load Package2 Sections. */ + load_package2_sections(&header.metadata, package2_mkey_rev); + + /* Clean up cache. */ + flush_dcache_all(); + invalidate_icache_inner_shareable(); + + /* Set CORE0 entrypoint for Package2. */ + set_core_entrypoint_and_context_id(0, DRAM_BASE_PHYSICAL + header.metadata.entrypoint, 0); + + /* Synchronize with NX BOOTLOADER. */ + if (MAILBOX_NX_BOOTLOADER_SETUP_STATE == NX_BOOTLOADER_STATE_LOADED_PACKAGE2) { + while (MAILBOX_NX_BOOTLOADER_SETUP_STATE < NX_BOOTLOADER_STATE_FINISHED) { + wait(1); + } + } + + /* TODO: MISC register 0x1F0098C00 |= 0x2000; */ + + /* TODO: Update SCR_EL3 depending on value in Bootconfig. */ +} diff --git a/exosphere/src/package2.h b/exosphere/src/package2.h new file mode 100644 index 0000000..f32fa8d --- /dev/null +++ b/exosphere/src/package2.h @@ -0,0 +1,78 @@ +#ifndef EXOSPHERE_PACKAGE2_H +#define EXOSPHERE_PACKAGE2_H + +/* This is code responsible for validating a package2. Actual file reading is done by bootloader. */ + +#include +#include "bootconfig.h" +#include "memory_map.h" + +/* Physaddr 0x40002EF8 */ +#define MAILBOX_NX_BOOTLOADER_BASE (mmio_get_device_address(MMIO_DEVID_NXBOOTLOADER_MAILBOX)) + +#define MAILBOX_NX_BOOTLOADER_SETUP_STATE (*((volatile uint32_t *)(MAILBOX_NX_BOOTLOADER_BASE + 0xEF8ULL))) + +#define NX_BOOTLOADER_STATE_INIT 0 +#define NX_BOOTLOADER_STATE_MOVED_BOOTCONFIG 1 +#define NX_BOOTLOADER_STATE_LOADED_PACKAGE2 2 +#define NX_BOOTLOADER_STATE_FINISHED 3 + +/* Physaddr 0x40002EFC */ +#define MAILBOX_NX_BOOTLOADER_IS_SECMON_AWAKE (*((volatile uint32_t *)(MAILBOX_NX_BOOTLOADER_BASE + 0xEFCULL))) + +#define NX_BOOTLOADER_BOOTCONFIG_POINTER ((void *)(0x4003D000ULL)) + +#define NX_BOOTLOADER_PACKAGE2_LOAD_ADDRESS ((void *)(0xA9800000ULL)) + +#define DRAM_BASE_PHYSICAL (0x80000000ULL) + +#define MAGIC_PK21 (0x31324B50) +#define PACKAGE2_SIZE_MAX 0x7FC000 +#define PACKAGE2_SECTION_MAX 0x3 + +#define PACKAGE2_MINVER_THEORETICAL 0x0 +#define PACKAGE2_MAXVER_100 0x2 +#define PACKAGE2_MAXVER_200 0x3 +#define PACKAGE2_MAXVER_300 0x4 +#define PACKAGE2_MAXVER_302 0x5 +#define PACKAGE2_MAXVER_400_CURRENT 0x6 + +#define PACKAGE2_MINVER_100 0x3 +#define PACKAGE2_MINVER_200 0x4 +#define PACKAGE2_MINVER_300 0x5 +#define PACKAGE2_MINVER_302 0x6 +#define PACKAGE2_MINVER_400_CURRENT 0x7 + + +#pragma pack(push, 1) +typedef struct { + union { + uint8_t ctr[0x10]; + uint32_t ctr_dwords[0x4]; + }; + uint8_t section_ctrs[4][0x10]; + uint32_t magic; + uint32_t entrypoint; + uint32_t _0x58; + uint8_t version_max; /* Must be > TZ value. */ + uint8_t version_min; /* Must be < TZ value. */ + uint16_t _0x5E; + uint32_t section_sizes[4]; + uint32_t section_offsets[4]; + uint8_t section_hashes[4][0x20]; +} package2_meta_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint8_t signature[0x100]; + union { + package2_meta_t metadata; + uint8_t encrypted_header[0x100]; + }; +} package2_header_t; +#pragma pack(pop) + +void load_package2(void); + +#endif diff --git a/exosphere/src/pmc.h b/exosphere/src/pmc.h new file mode 100644 index 0000000..1ffb1be --- /dev/null +++ b/exosphere/src/pmc.h @@ -0,0 +1,14 @@ +#ifndef EXOSPHERE_PMC_H +#define EXOSPHERE_PMC_H + +#include +#include "memory_map.h" + +/* Exosphere register definitions for the Tegra X1 PMC. */ + +#define PMC_BASE (mmio_get_device_address(MMIO_DEVID_RTC_PMC) + 0x400ULL) + +#define APBDEV_PMC_PWRGATE_TOGGLE_0 (*((volatile uint32_t *)(PMC_BASE + 0x30))) +#define APBDEV_PMC_PWRGATE_STATUS_0 (*((volatile uint32_t *)(PMC_BASE + 0x38))) + +#endif diff --git a/exosphere/src/randomcache.c b/exosphere/src/randomcache.c new file mode 100644 index 0000000..4d80a5f --- /dev/null +++ b/exosphere/src/randomcache.c @@ -0,0 +1,73 @@ +#include +#include + +#include "utils.h" +#include "randomcache.h" +#include "se.h" +#include "cache.h" + +/* TrustZone maintains a cache of random for the kernel. */ +/* So that requests can still be serviced even when a */ +/* usermode SMC is in progress. */ + +static uint8_t g_random_cache[0x400]; +static unsigned int g_random_cache_low = 0; +static unsigned int g_random_cache_high = 0x3FF; + + +void randomcache_refill_segment(unsigned int offset, unsigned int size) { + if (offset + size >= 0x400) { + size = 0x400 - offset; + } + + flush_dcache_range(&g_random_cache[offset], &g_random_cache[offset + size]); + se_generate_random(KEYSLOT_SWITCH_RNGKEY, &g_random_cache[offset], size); + flush_dcache_range(&g_random_cache[offset], &g_random_cache[offset + size]); + +} + +void randomcache_init(void) { + randomcache_refill_segment(0, 0x400); + g_random_cache_low = 0; + g_random_cache_high = 0x3FF; +} + +void randomcache_refill(void) { + unsigned int high_plus_one = (g_random_cache_high + 1) & 0x3FF; + if (g_random_cache_low != high_plus_one) { + /* Only refill if there's data to refill. */ + if (g_random_cache_low < high_plus_one) { + /* NOTE: There is a bug in official code that causes this to not work properly. */ + /* In particular, official code checks whether high_plus_one == 0x400. */ + /* However, because high_plus_one is &= 0x3FF'd, this can never be the case. */ + /* We will implement according to Nintendo's intention, and not include their bug. */ + /* This should have no impact on actual observable results, anyway, since this data is random anyway... */ + + if (g_random_cache_high != 0x3FF) { /* This is if (true) in Nintendo's code due to the above bug. */ + randomcache_refill_segment(high_plus_one, 0x400 - high_plus_one); + g_random_cache_high = (g_random_cache_high + 0x400 - high_plus_one) & 0x3FF; + } + + if (g_random_cache_low > 0) { + randomcache_refill_segment(0, g_random_cache_low); + g_random_cache_high = (g_random_cache_high + g_random_cache_low) & 0x3FF; + } + } else { /* g_random_cache_low > high_plus_one */ + randomcache_refill_segment(high_plus_one, g_random_cache_low - high_plus_one); + g_random_cache_high = g_random_cache_low - 1; + } + } +} + +void randomcache_getbytes(void *dst, size_t num_bytes) { + unsigned int low = g_random_cache_low; + + memcpy(dst, &g_random_cache[low], num_bytes); + + unsigned int new_low = low + num_bytes; + if (new_low + 0x38 > 0x3FF) { + new_low = 0; + } + + g_random_cache_low = new_low; +} diff --git a/exosphere/src/randomcache.h b/exosphere/src/randomcache.h new file mode 100644 index 0000000..7d0ddc8 --- /dev/null +++ b/exosphere/src/randomcache.h @@ -0,0 +1,13 @@ +#ifndef EXOSPHERE_RANDOM_CACHE_H +#define EXOSPHERE_RANDOM_CACHE_H + +#include + +/* This method must be called on startup. */ +void randomcache_init(void); +void randomcache_refill(void); + +void randomcache_getbytes(void *dst, size_t num_bytes); + + +#endif \ No newline at end of file diff --git a/exosphere/src/se.c b/exosphere/src/se.c new file mode 100644 index 0000000..d9caebf --- /dev/null +++ b/exosphere/src/se.c @@ -0,0 +1,596 @@ +#include + +#include "utils.h" +#include "interrupt.h" +#include "se.h" +#include "memory_map.h" +#include "cache.h" +#include "se.h" + +/* Macro for the SE registers. */ +#define SECURITY_ENGINE ((volatile security_engine_t *)(mmio_get_device_address(MMIO_DEVID_SE))) + +void trigger_se_rsa_op(void *buf, size_t size); +void trigger_se_blocking_op(unsigned int op, void *dst, size_t dst_size, const void *src, size_t src_size); + +/* Globals for driver. */ +unsigned int (*g_se_callback)(void); + +unsigned int g_se_modulus_sizes[KEYSLOT_RSA_MAX]; +unsigned int g_se_exp_sizes[KEYSLOT_RSA_MAX]; + + +/* Initialize a SE linked list. */ +void ll_init(se_ll_t *ll, void *buffer, size_t size) { + ll->num_entries = 0; /* 1 Entry. */ + + if (buffer != NULL) { + ll->addr_info.address = (uint32_t) get_physical_address(buffer); + ll->addr_info.size = (uint32_t) size; + } else { + ll->addr_info.address = 0; + ll->addr_info.size = 0; + } + + flush_dcache_range((uint8_t *)ll, (uint8_t *)ll + sizeof(*ll)); +} + +void set_security_engine_callback(unsigned int (*callback)(void)) { + if (callback == NULL || g_se_callback != NULL) { + generic_panic(); + } + + g_se_callback = callback; +} + +/* Fires on Security Engine operation completion. */ +void se_operation_completed(void) { + SECURITY_ENGINE->INT_ENABLE_REG = 0; + if (g_se_callback != NULL) { + g_se_callback(); + g_se_callback = NULL; + } +} + + +void se_check_for_error(void) { + if (SECURITY_ENGINE->INT_STATUS_REG & 0x10000 || SECURITY_ENGINE->FLAGS_REG & 3 || SECURITY_ENGINE->ERR_STATUS_REG) { + generic_panic(); + } +} + +void se_trigger_interrupt(void) { + intr_set_pending(INTERRUPT_ID_USER_SECURITY_ENGINE); +} + +void se_verify_flags_cleared(void) { + if (SECURITY_ENGINE->FLAGS_REG & 3) { + generic_panic(); + } +} + +void se_clear_interrupts(void) { + /* TODO */ +} + +/* Set the flags for an AES keyslot. */ +void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags) { + if (keyslot >= KEYSLOT_AES_MAX) { + generic_panic(); + } + + /* Misc flags. */ + if (flags & ~0x80) { + SECURITY_ENGINE->AES_KEYSLOT_FLAGS[keyslot] = ~flags; + } + + /* Disable keyslot reads. */ + if (flags & 0x80) { + SECURITY_ENGINE->AES_KEY_READ_DISABLE_REG &= ~(1 << keyslot); + } +} + +/* Set the flags for an RSA keyslot. */ +void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags) { + if (keyslot >= KEYSLOT_RSA_MAX) { + generic_panic(); + } + + /* Misc flags. */ + if (flags & ~0x80) { + /* TODO: Why are flags assigned this way? */ + SECURITY_ENGINE->RSA_KEYSLOT_FLAGS[keyslot] = (((flags >> 4) & 4) | (flags & 3)) ^ 7; + } + + /* Disable keyslot reads. */ + if (flags & 0x80) { + SECURITY_ENGINE->RSA_KEY_READ_DISABLE_REG &= ~(1 << keyslot); + } +} + +void clear_aes_keyslot(unsigned int keyslot) { + if (keyslot >= KEYSLOT_AES_MAX) { + generic_panic(); + } + + /* Zero out the whole keyslot and IV. */ + for (unsigned int i = 0; i < 0x10; i++) { + SECURITY_ENGINE->AES_KEYTABLE_ADDR = (keyslot << 4) | i; + SECURITY_ENGINE->AES_KEYTABLE_DATA = 0; + } +} + +void clear_rsa_keyslot(unsigned int keyslot) { + if (keyslot >= KEYSLOT_RSA_MAX) { + generic_panic(); + } + + /* Zero out the whole keyslot. */ + for (unsigned int i = 0; i < 0x40; i++) { + /* Select Keyslot Modulus[i] */ + SECURITY_ENGINE->RSA_KEYTABLE_ADDR = (keyslot << 7) | i | 0x40; + SECURITY_ENGINE->RSA_KEYTABLE_DATA = 0; + } + for (unsigned int i = 0; i < 0x40; i++) { + /* Select Keyslot Expontent[i] */ + SECURITY_ENGINE->RSA_KEYTABLE_ADDR = (keyslot << 7) | i; + SECURITY_ENGINE->RSA_KEYTABLE_DATA = 0; + } +} + +void set_aes_keyslot(unsigned int keyslot, const void *key, size_t key_size) { + if (keyslot >= KEYSLOT_AES_MAX || key_size > KEYSIZE_AES_MAX) { + generic_panic(); + } + + for (size_t i = 0; i < (key_size >> 2); i++) { + SECURITY_ENGINE->AES_KEYTABLE_ADDR = (keyslot << 4) | i; + SECURITY_ENGINE->AES_KEYTABLE_DATA = read32le(key, 4 * i); + } +} + +void set_rsa_keyslot(unsigned int keyslot, const void *modulus, size_t modulus_size, const void *exponent, size_t exp_size) { + if (keyslot >= KEYSLOT_RSA_MAX || modulus_size > KEYSIZE_RSA_MAX || exp_size > KEYSIZE_RSA_MAX) { + generic_panic(); + } + + for (size_t i = 0; i < (modulus_size >> 2); i++) { + SECURITY_ENGINE->RSA_KEYTABLE_ADDR = (keyslot << 7) | 0x40 | i; + SECURITY_ENGINE->RSA_KEYTABLE_DATA = read32be(modulus, 4 * i); + } + + for (size_t i = 0; i < (exp_size >> 2); i++) { + SECURITY_ENGINE->RSA_KEYTABLE_ADDR = (keyslot << 7) | i; + SECURITY_ENGINE->RSA_KEYTABLE_DATA = read32be(exponent, 4 * i); + } + + g_se_modulus_sizes[keyslot] = modulus_size; + g_se_exp_sizes[keyslot] = exp_size; +} + +void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size) { + if (keyslot >= KEYSLOT_AES_MAX || iv_size > 0x10) { + generic_panic(); + } + + for (size_t i = 0; i < (iv_size >> 2); i++) { + SECURITY_ENGINE->AES_KEYTABLE_ADDR = (keyslot << 4) | 8 | i; + SECURITY_ENGINE->AES_KEYTABLE_DATA = read32le(iv, 4 * i); + } +} + +void clear_aes_keyslot_iv(unsigned int keyslot) { + if (keyslot >= KEYSLOT_AES_MAX) { + generic_panic(); + } + + for (size_t i = 0; i < (0x10 >> 2); i++) { + SECURITY_ENGINE->AES_KEYTABLE_ADDR = (keyslot << 4) | 8; + SECURITY_ENGINE->AES_KEYTABLE_DATA = 0; + } +} + +void set_se_ctr(const void *ctr) { + for (unsigned int i = 0; i < 4; i++) { + SECURITY_ENGINE->CRYPTO_CTR_REG[i] = read32le(ctr, i * 4); + } +} + +void decrypt_data_into_keyslot(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size) { + if (keyslot_dst >= KEYSLOT_AES_MAX || keyslot_src >= KEYSIZE_AES_MAX || wrapped_key_size > KEYSIZE_AES_MAX) { + generic_panic(); + } + + SECURITY_ENGINE->CONFIG_REG = (ALG_AES_DEC | DST_KEYTAB); + SECURITY_ENGINE->CRYPTO_REG = keyslot_src << 24; + SECURITY_ENGINE->BLOCK_COUNT_REG = 0; + SECURITY_ENGINE->CRYPTO_KEYTABLE_DST_REG = keyslot_dst << 8; + + flush_dcache_range(wrapped_key, (const uint8_t *)wrapped_key + wrapped_key_size); + /* TODO: trigger_se_aes_op(OP_START, NULL, 0, wrapped_key, wrapped_key_size); */ +} + +void se_aes_crypt_insecure_internal(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, unsigned int crypt_config, bool encrypt, unsigned int (*callback)(void)) { + if (keyslot >= KEYSLOT_AES_MAX) { + generic_panic(); + } + + if (size == 0) { + return; + } + + /* Setup Config register. */ + if (encrypt) { + SECURITY_ENGINE->CONFIG_REG = (ALG_AES_ENC | DST_MEMORY); + } else { + SECURITY_ENGINE->CONFIG_REG = (ALG_AES_DEC | DST_MEMORY); + } + + /* Setup Crypto register. */ + SECURITY_ENGINE->CRYPTO_REG = crypt_config | (keyslot << 24) | (encrypt << 8); + + /* Mark this encryption as insecure -- this makes the SE not a secure busmaster. */ + SECURITY_ENGINE->CRYPTO_REG |= 0x80000000; + + /* Appropriate number of blocks. */ + SECURITY_ENGINE->BLOCK_COUNT_REG = (size >> 4) - 1; + + /* Set the callback, for after the async operation. */ + set_security_engine_callback(callback); + + /* Enable SE Interrupt firing for async op. */ + SECURITY_ENGINE->INT_ENABLE_REG = 0x10; + + /* Setup Input/Output lists */ + SECURITY_ENGINE->IN_LL_ADDR_REG = in_ll_paddr; + SECURITY_ENGINE->OUT_LL_ADDR_REG = out_ll_paddr; + + /* Set registers for operation. */ + SECURITY_ENGINE->ERR_STATUS_REG = SECURITY_ENGINE->ERR_STATUS_REG; + SECURITY_ENGINE->INT_STATUS_REG = SECURITY_ENGINE->INT_STATUS_REG; + SECURITY_ENGINE->OPERATION_REG = 1; + + /* Ensure writes go through. */ + __asm__ __volatile__ ("dsb ish" : : : "memory"); +} + +void se_aes_ctr_crypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *ctr, unsigned int (*callback)(void)) { + /* Unknown what this write does, but official code writes it for CTR mode. */ + SECURITY_ENGINE->_0x80C = 1; + set_se_ctr(ctr); + se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x81E, true, callback); +} + +void se_aes_cbc_encrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)) { + set_aes_keyslot_iv(keyslot, iv, 0x10); + se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x44, true, callback); +} + +void se_aes_cbc_decrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)) { + set_aes_keyslot_iv(keyslot, iv, 0x10); + se_aes_crypt_insecure_internal(keyslot, out_ll_paddr, in_ll_paddr, size, 0x66, false, callback); +} + + +void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*callback)(void)) { + uint8_t stack_buf[KEYSIZE_RSA_MAX]; + + if (keyslot >= KEYSLOT_RSA_MAX || size > KEYSIZE_RSA_MAX) { + generic_panic(); + } + + /* Endian swap the input. */ + for (size_t i = size; i > 0; i--) { + stack_buf[i] = *((uint8_t *)buf + size - i); + } + + + SECURITY_ENGINE->CONFIG_REG = (ALG_RSA | DST_RSAREG); + SECURITY_ENGINE->RSA_CONFIG = keyslot << 24; + SECURITY_ENGINE->RSA_KEY_SIZE_REG = (g_se_modulus_sizes[keyslot] >> 6) - 1; + SECURITY_ENGINE->RSA_EXP_SIZE_REG = g_se_exp_sizes[keyslot] >> 2; + + set_security_engine_callback(callback); + + /* Enable SE Interrupt firing for async op. */ + SECURITY_ENGINE->INT_ENABLE_REG = 0x10; + + flush_dcache_range(stack_buf, stack_buf + KEYSIZE_RSA_MAX); + trigger_se_rsa_op(stack_buf, size); + + while (!(SECURITY_ENGINE->INT_STATUS_REG & 2)) { /* Wait a while */ } +} + +void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { + uint8_t stack_buf[KEYSIZE_RSA_MAX]; + + if (keyslot >= KEYSLOT_RSA_MAX || src_size > KEYSIZE_RSA_MAX || dst_size > KEYSIZE_RSA_MAX) { + generic_panic(); + } + + /* Endian swap the input. */ + for (size_t i = src_size; i > 0; i--) { + stack_buf[i] = *((uint8_t *)src + src_size - i); + } + + SECURITY_ENGINE->CONFIG_REG = (ALG_RSA | DST_RSAREG); + SECURITY_ENGINE->RSA_CONFIG = keyslot << 24; + SECURITY_ENGINE->RSA_KEY_SIZE_REG = (g_se_modulus_sizes[keyslot] >> 6) - 1; + SECURITY_ENGINE->RSA_EXP_SIZE_REG = g_se_exp_sizes[keyslot] >> 2; + + + flush_dcache_range(stack_buf, stack_buf + KEYSIZE_RSA_MAX); + trigger_se_blocking_op(1, NULL, 0, stack_buf, src_size); + se_get_exp_mod_output(dst, dst_size); +} + +void se_get_exp_mod_output(void *buf, size_t size) { + size_t num_dwords = (size >> 2); + if (num_dwords < 1) { + return; + } + + uint32_t *p_out = ((uint32_t *)buf) + num_dwords - 1; + uint32_t offset = 0; + + /* Copy endian swapped output. */ + while (num_dwords) { + *p_out = read32be(SECURITY_ENGINE->RSA_OUTPUT, offset); + offset += 4; + p_out--; + num_dwords--; + } +} + +void trigger_se_rsa_op(void *buf, size_t size) { + se_ll_t in_ll; + ll_init(&in_ll, (void *)buf, size); + + /* Set the input LL. */ + SECURITY_ENGINE->IN_LL_ADDR_REG = get_physical_address(&in_ll); + + /* Set registers for operation. */ + SECURITY_ENGINE->ERR_STATUS_REG = SECURITY_ENGINE->ERR_STATUS_REG; + SECURITY_ENGINE->INT_STATUS_REG = SECURITY_ENGINE->INT_STATUS_REG; + SECURITY_ENGINE->OPERATION_REG = 1; + + /* Ensure writes go through. */ + __asm__ __volatile__ ("dsb ish" : : : "memory"); +} + +void trigger_se_blocking_op(unsigned int op, void *dst, size_t dst_size, const void *src, size_t src_size) { + se_ll_t in_ll; + se_ll_t out_ll; + + ll_init(&in_ll, (void *)src, src_size); + ll_init(&out_ll, dst, dst_size); + + /* Set the LLs. */ + SECURITY_ENGINE->IN_LL_ADDR_REG = get_physical_address(&in_ll); + SECURITY_ENGINE->OUT_LL_ADDR_REG = get_physical_address(&out_ll); + + /* Set registers for operation. */ + SECURITY_ENGINE->ERR_STATUS_REG = SECURITY_ENGINE->ERR_STATUS_REG; + SECURITY_ENGINE->INT_STATUS_REG = SECURITY_ENGINE->INT_STATUS_REG; + SECURITY_ENGINE->OPERATION_REG = op; + + while (!(SECURITY_ENGINE->INT_STATUS_REG & 0x10)) { /* Wait a while */ } + se_check_for_error(); +} + + +/* Secure AES Functionality. */ +void se_perform_aes_block_operation(void *dst, size_t dst_size, const void *src, size_t src_size) { + uint8_t block[0x10]; + + if (src_size > sizeof(block) || dst_size > sizeof(block)) { + generic_panic(); + } + + /* Load src data into block. */ + memset(block, 0, sizeof(block)); + memcpy(block, src, src_size); + flush_dcache_range(block, block + sizeof(block)); + + /* Trigger AES operation. */ + SECURITY_ENGINE->BLOCK_COUNT_REG = 0; + trigger_se_blocking_op(1, block, sizeof(block), block, sizeof(block)); + + /* Copy output data into dst. */ + flush_dcache_range(block, block + sizeof(block)); + memcpy(dst, block, dst_size); +} + +void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size) { + if (keyslot >= KEYSLOT_AES_MAX || ctr_size != 0x10) { + generic_panic(); + } + + unsigned int num_blocks = src_size >> 4; + + /* Unknown what this write does, but official code writes it for CTR mode. */ + SECURITY_ENGINE->_0x80C = 1; + SECURITY_ENGINE->CONFIG_REG = (ALG_AES_ENC | DST_MEMORY); + SECURITY_ENGINE->CRYPTO_REG = (keyslot << 24) | 0x91E; + set_se_ctr(ctr); + + /* Handle any aligned blocks. */ + size_t aligned_size = (size_t)num_blocks << 4; + if (aligned_size) { + SECURITY_ENGINE->BLOCK_COUNT_REG = num_blocks - 1; + trigger_se_blocking_op(1, dst, dst_size, src, aligned_size); + } + + /* Handle final, unaligned block. */ + if (aligned_size < dst_size && aligned_size < src_size) { + size_t last_block_size = dst_size - aligned_size; + if (src_size < dst_size) { + last_block_size = src_size - aligned_size; + } + se_perform_aes_block_operation(dst + aligned_size, last_block_size, (uint8_t *)src + aligned_size, src_size - aligned_size); + } +} + +void se_aes_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int config_high) { + if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { + generic_panic(); + } + + /* Set configuration high (256-bit vs 128-bit) based on parameter. */ + SECURITY_ENGINE->CONFIG_REG = (ALG_AES_ENC | DST_MEMORY) | (config_high << 16); + SECURITY_ENGINE->CRYPTO_REG = keyslot << 24; + se_perform_aes_block_operation(dst, 0x10, src, 0x10); + +} + +void se_aes_128_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { + se_aes_ecb_encrypt_block(keyslot, dst, dst_size, src, src_size, 0); +} + +void se_aes_256_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { + se_aes_ecb_encrypt_block(keyslot, dst, dst_size, src, src_size, 0x202); +} + + +void se_aes_ecb_decrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size) { + if (keyslot >= KEYSLOT_AES_MAX || dst_size != 0x10 || src_size != 0x10) { + generic_panic(); + } + + SECURITY_ENGINE->CONFIG_REG = (ALG_AES_DEC | DST_MEMORY); + SECURITY_ENGINE->CRYPTO_REG = keyslot << 24; + se_perform_aes_block_operation(dst, 0x10, src, 0x10); +} + +void shift_left_xor_rb(uint8_t *key) { + uint8_t prev_high_bit = 0; + for (unsigned int i = 0; i < 0x10; i++) { + uint8_t cur_byte = key[0xF - i]; + key[0xF - i] = (cur_byte << 1) | (prev_high_bit); + prev_high_bit = cur_byte >> 7; + } + if (prev_high_bit) { + key[0xF] ^= 0x87; + } +} + +void se_compute_aes_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size, unsigned int config_high) { + if (keyslot >= KEYSLOT_AES_MAX) { + generic_panic(); + } + + /* Generate the derived key, to be XOR'd with final output block. */ + uint8_t derived_key[0x10]; + memset(derived_key, 0, sizeof(derived_key)); + se_aes_128_ecb_encrypt_block(keyslot, derived_key, sizeof(derived_key), derived_key, sizeof(derived_key)); + shift_left_xor_rb(derived_key); + if (data_size & 0xF) { + shift_left_xor_rb(derived_key); + } + + SECURITY_ENGINE->CONFIG_REG = (ALG_AES_ENC | DST_HASHREG) | (config_high << 16); + SECURITY_ENGINE->CRYPTO_REG = (keyslot << 24) | (0x145); + clear_aes_keyslot_iv(keyslot); + + unsigned int num_blocks = (data_size + 0xF) >> 4; + /* Handle aligned blocks. */ + if (num_blocks > 1) { + SECURITY_ENGINE->BLOCK_COUNT_REG = num_blocks - 2; + trigger_se_blocking_op(1, NULL, 0, data, data_size); + SECURITY_ENGINE->CRYPTO_REG |= 0x80; + } + + /* Create final block. */ + uint8_t last_block[0x10]; + memset(last_block, 0, sizeof(last_block)); + if (data_size & 0xF) { + + memcpy(last_block, data + (data_size & ~0xF), data_size & 0xF); + last_block[data_size & 0xF] = 0x80; /* Last block = data || 100...0 */ + } else if (data_size >= 0x10) { + memcpy(last_block, data + data_size - 0x10, 0x10); + } + + for (unsigned int i = 0; i < 0x10; i++) { + last_block[i] ^= derived_key[i]; + } + + /* Perform last operation. */ + flush_dcache_range(last_block, last_block + sizeof(last_block)); + trigger_se_blocking_op(1, NULL, 0, last_block, sizeof(last_block)); + + /* Copy output CMAC. */ + for (unsigned int i = 0; i < (cmac_size >> 2); i++) { + ((uint32_t *)cmac)[i] = read32le(SECURITY_ENGINE->HASH_RESULT_REG, i << 2); + } +} + +void se_compute_aes_128_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size) { + se_compute_aes_cmac(keyslot, cmac, cmac_size, data, data_size, 0); +} +void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size) { + se_compute_aes_cmac(keyslot, cmac, cmac_size, data, data_size, 0x202); +} + +/* SHA256 Implementation. */ +void se_calculate_sha256(void *dst, const void *src, size_t src_size) { + /* Setup config for SHA256, size = BITS(src_size) */ + SECURITY_ENGINE->CONFIG_REG = (ENCMODE_SHA256 | ALG_SHA | DST_HASHREG); + SECURITY_ENGINE->SHA_CONFIG_REG = 1; + SECURITY_ENGINE->SHA_MSG_LENGTH_REG = (unsigned int)(src_size << 3); + SECURITY_ENGINE->_0x20C = 0; + SECURITY_ENGINE->_0x210 = 0; + SECURITY_ENGINE->SHA_MSG_LEFT_REG = 0; + SECURITY_ENGINE->_0x218 = (unsigned int)(src_size << 3); + SECURITY_ENGINE->_0x21C = 0; + SECURITY_ENGINE->_0x220 = 0; + SECURITY_ENGINE->_0x224 = 0; + + /* Trigger the operation. */ + trigger_se_blocking_op(1, NULL, 0, src, src_size); + + /* Copy output hash. */ + for (unsigned int i = 0; i < (0x20 >> 2); i++) { + ((uint32_t *)dst)[i] = read32be(SECURITY_ENGINE->HASH_RESULT_REG, i << 2); + } +} + +/* RNG API */ +void se_initialize_rng(unsigned int keyslot) { + if (keyslot >= KEYSLOT_AES_MAX) { + generic_panic(); + } + + /* To initialize the RNG, we'll perform an RNG operation into an output buffer. */ + /* This will be discarded, when done. */ + uint8_t output_buf[0x10]; + + SECURITY_ENGINE->RNG_SRC_CONFIG_REG = 3; /* Entropy enable + Entropy lock enable */ + SECURITY_ENGINE->RNG_RESEED_INTERVAL_REG = 70001; + SECURITY_ENGINE->CONFIG_REG = (ALG_RNG | DST_MEMORY); + SECURITY_ENGINE->CRYPTO_REG = (keyslot << 24) | 0x108; + SECURITY_ENGINE->RNG_CONFIG_REG = 5; + SECURITY_ENGINE->BLOCK_COUNT_REG = 0; + trigger_se_blocking_op(1, output_buf, 0x10, NULL, 0); +} + +void se_generate_random(unsigned int keyslot, void *dst, size_t size) { + if (keyslot >= KEYSLOT_AES_MAX) { + generic_panic(); + } + + uint32_t num_blocks = size >> 4; + size_t aligned_size = num_blocks << 4; + SECURITY_ENGINE->CONFIG_REG = (ALG_RNG | DST_MEMORY); + SECURITY_ENGINE->CRYPTO_REG = (keyslot << 24) | 0x108; + SECURITY_ENGINE->RNG_CONFIG_REG = 4; + + if (num_blocks >= 1) { + SECURITY_ENGINE->BLOCK_COUNT_REG = num_blocks - 1; + trigger_se_blocking_op(1, dst, aligned_size, NULL, 0); + } + if (size > aligned_size) { + se_perform_aes_block_operation(dst + aligned_size, size - aligned_size, NULL, 0); + } + +} diff --git a/exosphere/src/se.h b/exosphere/src/se.h new file mode 100644 index 0000000..3b0fe77 --- /dev/null +++ b/exosphere/src/se.h @@ -0,0 +1,193 @@ +#ifndef EXOSPHERE_SE_H +#define EXOSPHERE_SE_H + +#include +#include + +#include "memory_map.h" + +/* Exosphere driver for the Tegra X1 security engine. */ + +#define KEYSLOT_SWITCH_PACKAGE2KEY 0x8 +#define KEYSLOT_SWITCH_TEMPKEY 0x9 +#define KEYSLOT_SWITCH_SESSIONKEY 0xA +#define KEYSLOT_SWITCH_RNGKEY 0xB +#define KEYSLOT_SWITCH_MASTERKEY 0xC +#define KEYSLOT_SWITCH_DEVICEKEY 0xD + +/* This keyslot was added in 4.0.0. */ +#define KEYSLOT_SWITCH_4XNEWDEVICEKEYGENKEY 0xD +#define KEYSLOT_SWITCH_4XNEWCONSOLEKEYGENKEY 0xE +#define KEYSLOT_SWITCH_4XOLDDEVICEKEY 0xF + +#define KEYSLOT_AES_MAX 0x10 +#define KEYSLOT_RSA_MAX 0x2 + +#define KEYSIZE_AES_MAX 0x20 +#define KEYSIZE_RSA_MAX 0x100 + +#define ALG_SHIFT (12) +#define ALG_DEC_SHIFT (8) +#define ALG_NOP (0 << ALG_SHIFT) +#define ALG_AES_ENC (1 << ALG_SHIFT) +#define ALG_AES_DEC ((1 << ALG_DEC_SHIFT) | ALG_NOP) +#define ALG_RNG (2 << ALG_SHIFT) +#define ALG_SHA (3 << ALG_SHIFT) +#define ALG_RSA (4 << ALG_SHIFT) + +#define DST_SHIFT (2) +#define DST_MEMORY (0 << DST_SHIFT) +#define DST_HASHREG (1 << DST_SHIFT) +#define DST_KEYTAB (2 << DST_SHIFT) +#define DST_SRK (3 << DST_SHIFT) +#define DST_RSAREG (4 << DST_SHIFT) + +#define ENCMODE_SHIFT (24) +#define DECMODE_SHIFT (16) +#define ENCMODE_SHA256 (5 << ENCMODE_SHIFT) + +#define HASH_DISABLE (0x0) +#define HASH_ENABLE (0x1) + +#define OP_ABORT 0 +#define OP_START 1 +#define OP_RESTART 2 +#define OP_CTX_SAVE 3 +#define OP_RESTART_IN 4 + +#define RSA_2048_BYTES 0x100 + +typedef struct security_engine { + unsigned int _0x0; + unsigned int _0x4; + unsigned int OPERATION_REG; + unsigned int INT_ENABLE_REG; + unsigned int INT_STATUS_REG; + unsigned int CONFIG_REG; + unsigned int IN_LL_ADDR_REG; + unsigned int _0x1C; + unsigned int _0x20; + unsigned int OUT_LL_ADDR_REG; + unsigned int _0x28; + unsigned int _0x2C; + unsigned char HASH_RESULT_REG[0x20]; + unsigned char _0x50[0x1B0]; + unsigned int SHA_CONFIG_REG; + unsigned int SHA_MSG_LENGTH_REG; + unsigned int _0x20C; + unsigned int _0x210; + unsigned int SHA_MSG_LEFT_REG; + unsigned int _0x218; + unsigned int _0x21C; + unsigned int _0x220; + unsigned int _0x224; + unsigned char _0x228[0x5C]; + unsigned int AES_KEY_READ_DISABLE_REG; + unsigned int AES_KEYSLOT_FLAGS[0x10]; + unsigned char _0x2C4[0x3C]; + unsigned int _0x300; + unsigned int CRYPTO_REG; + unsigned int CRYPTO_CTR_REG[4]; + unsigned int BLOCK_COUNT_REG; + unsigned int AES_KEYTABLE_ADDR; + unsigned int AES_KEYTABLE_DATA; + unsigned int _0x324; + unsigned int _0x328; + unsigned int _0x32C; + unsigned int CRYPTO_KEYTABLE_DST_REG; + unsigned char _0x334[0xC]; + unsigned int RNG_CONFIG_REG; + unsigned int RNG_SRC_CONFIG_REG; + unsigned int RNG_RESEED_INTERVAL_REG; + unsigned char _0x34C[0xB4]; + unsigned int RSA_CONFIG; + unsigned int RSA_KEY_SIZE_REG; + unsigned int RSA_EXP_SIZE_REG; + unsigned int RSA_KEY_READ_DISABLE_REG; + unsigned int RSA_KEYSLOT_FLAGS[2]; + unsigned int _0x418; + unsigned int _0x41C; + unsigned int RSA_KEYTABLE_ADDR; + unsigned int RSA_KEYTABLE_DATA; + unsigned char RSA_OUTPUT[0x100]; + unsigned char _0x528[0x2D8]; + unsigned int FLAGS_REG; + unsigned int ERR_STATUS_REG; + unsigned int _0x808; + unsigned int _0x80C; + unsigned int _0x810; + unsigned int _0x814; + unsigned int _0x818; + unsigned int _0x81C; + unsigned char _0x820[0x17E0]; +} security_engine_t; + +typedef struct { + uint32_t address; + uint32_t size; +} se_addr_info_t; + +typedef struct { + uint32_t num_entries; /* Set to total entries - 1 */ + se_addr_info_t addr_info; /* This should really be an array...but for our use case it works. */ +} se_ll_t; + +/* TODO: Define constants for the C driver. */ + + +/* WIP, API subject to change. */ + + +static inline volatile security_engine_t *get_security_engine_address(void) { + return (volatile security_engine_t *)(mmio_get_device_address(MMIO_DEVID_SE)); +} + +/* This function MUST be registered to fire on the appropriate interrupt. */ +void se_operation_completed(void); + +void se_check_for_error(void); +void se_trigger_interrupt(void); +void se_clear_interrupts(void); /* TODO */ + +void se_verify_flags_cleared(void); + +void set_aes_keyslot_flags(unsigned int keyslot, unsigned int flags); +void set_rsa_keyslot_flags(unsigned int keyslot, unsigned int flags); +void clear_aes_keyslot(unsigned int keyslot); +void clear_rsa_keyslot(unsigned int keyslot); + +void set_aes_keyslot(unsigned int keyslot, const void *key, size_t key_size); +void decrypt_data_into_keyslot(unsigned int keyslot_dst, unsigned int keyslot_src, const void *wrapped_key, size_t wrapped_key_size); +void set_rsa_keyslot(unsigned int keyslot, const void *modulus, size_t modulus_size, const void *exponent, size_t exp_size); +void set_aes_keyslot_iv(unsigned int keyslot, const void *iv, size_t iv_size); +void set_se_ctr(const void *ctr); + + +/* Insecure AES API */ +void se_aes_ctr_crypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *ctr, unsigned int (*callback)(void)); +void se_aes_cbc_encrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)); +void se_aes_cbc_decrypt_insecure(unsigned int keyslot, uint32_t out_ll_paddr, uint32_t in_ll_paddr, size_t size, const void *iv, unsigned int (*callback)(void)); + +/* Secure AES API */ +void se_compute_aes_128_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size); +void se_compute_aes_256_cmac(unsigned int keyslot, void *cmac, size_t cmac_size, const void *data, size_t data_size); +void se_aes_128_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); +void se_aes_256_ecb_encrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); +void se_aes_ctr_crypt(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size, const void *ctr, size_t ctr_size); +void se_aes_ecb_decrypt_block(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); + +/* Hash API */ +void se_calculate_sha256(void *dst, const void *src, size_t src_size); + +/* RSA API */ +void se_exp_mod(unsigned int keyslot, void *buf, size_t size, unsigned int (*callback)(void)); +void se_get_exp_mod_output(void *buf, size_t size); +void se_synchronous_exp_mod(unsigned int keyslot, void *dst, size_t dst_size, const void *src, size_t src_size); + +/* RNG API */ +void se_initialize_rng(unsigned int keyslot); +void se_generate_random(unsigned int keyslot, void *dst, size_t size); + +/* TODO: SE context save API. */ + +#endif /* EXOSPHERE_SE_H */ diff --git a/exosphere/src/sealedkeys.c b/exosphere/src/sealedkeys.c new file mode 100644 index 0000000..450915c --- /dev/null +++ b/exosphere/src/sealedkeys.c @@ -0,0 +1,63 @@ +#include + +#include "utils.h" +#include "sealedkeys.h" +#include "se.h" + +const uint8_t g_titlekey_seal_key_source[0x10] = { + 0xCB, 0xB7, 0x6E, 0x38, 0xA1, 0xCB, 0x77, 0x0F, 0xB2, 0xA5, 0xB2, 0x9D, 0xD8, 0x56, 0x9F, 0x76 +}; + + +const uint8_t g_seal_key_sources[CRYPTOUSECASE_MAX][0x10] = { + {0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6}, + {0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06}, + {0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63}, + {0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D} +}; + +void seal_key_internal(void *dst, const void *src, const uint8_t *seal_key_source) { + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_SESSIONKEY, seal_key_source, 0x10); + se_aes_128_ecb_encrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10); +} + +void unseal_key_internal(unsigned int keyslot, const void *src, const uint8_t *seal_key_source) { + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, KEYSLOT_SWITCH_SESSIONKEY, seal_key_source, 0x10); + decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, src, 0x10); +} + + +void seal_titlekey(void *dst, size_t dst_size, const void *src, size_t src_size) { + if (dst_size != 0x10 || src_size != 0x10) { + generic_panic(); + } + + seal_key_internal(dst, src, g_titlekey_seal_key_source); + +} + +void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size) { + if (src_size != 0x10) { + generic_panic(); + } + + unseal_key_internal(keyslot, src, g_titlekey_seal_key_source); +} + + +void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int usecase) { + if (usecase >= CRYPTOUSECASE_MAX || dst_size != 0x10 || src_size != 0x10) { + generic_panic(); + } + + + seal_key_internal(dst, src, g_seal_key_sources[usecase]); +} + +void unseal_key(unsigned int keyslot, const void *src, size_t src_size, unsigned int usecase) { + if (usecase >= CRYPTOUSECASE_MAX || src_size != 0x10) { + generic_panic(); + } + + unseal_key_internal(keyslot, src, g_seal_key_sources[usecase]); +} \ No newline at end of file diff --git a/exosphere/src/sealedkeys.h b/exosphere/src/sealedkeys.h new file mode 100644 index 0000000..78226c7 --- /dev/null +++ b/exosphere/src/sealedkeys.h @@ -0,0 +1,21 @@ +#ifndef EXOSPHERE_SEALED_KEYS_H +#define EXOSPHERE_SEALED_KEYS_H + +#include + +/* Key sealing/unsealing functionality. */ + +#define CRYPTOUSECASE_AES 0 +#define CRYPTOUSECASE_RSAPRIVATE 1 +#define CRYPTOUSECASE_SECUREEXPMOD 2 +#define CRYPTOUSECASE_RSAOAEP 3 + +#define CRYPTOUSECASE_MAX 4 + +void seal_titlekey(void *dst, size_t dst_size, const void *src, size_t src_size); +void unseal_titlekey(unsigned int keyslot, const void *src, size_t src_size); + +void seal_key(void *dst, size_t dst_size, const void *src, size_t src_size, unsigned int usecase); +void unseal_key(unsigned int keyslot, const void *src, size_t src_size, unsigned int usecase); + +#endif \ No newline at end of file diff --git a/exosphere/src/smc_api.c b/exosphere/src/smc_api.c new file mode 100644 index 0000000..f282d0d --- /dev/null +++ b/exosphere/src/smc_api.c @@ -0,0 +1,562 @@ +#include + +#include "utils.h" +#include "memory_map.h" + +#include "configitem.h" +#include "cpu_context.h" +#include "lock.h" +#include "masterkey.h" +#include "mc.h" +#include "memory_map.h" +#include "pmc.h" +#include "randomcache.h" +#include "sealedkeys.h" +#include "smc_api.h" +#include "smc_user.h" +#include "se.h" +#include "userpage.h" +#include "titlekey.h" + +#define SMC_USER_HANDLERS 0x13 +#define SMC_PRIV_HANDLERS 0x9 + +/* User SMC prototypes */ +uint32_t smc_set_config(smc_args_t *args); +uint32_t smc_get_config(smc_args_t *args); +uint32_t smc_check_status(smc_args_t *args); +uint32_t smc_get_result(smc_args_t *args); +uint32_t smc_exp_mod(smc_args_t *args); +uint32_t smc_get_random_bytes_for_user(smc_args_t *args); +uint32_t smc_generate_aes_kek(smc_args_t *args); +uint32_t smc_load_aes_key(smc_args_t *args); +uint32_t smc_crypt_aes(smc_args_t *args); +uint32_t smc_generate_specific_aes_key(smc_args_t *args); +uint32_t smc_compute_cmac(smc_args_t *args); +uint32_t smc_load_rsa_oaep_key(smc_args_t *args); +uint32_t smc_decrypt_rsa_private_key(smc_args_t *args); +uint32_t smc_load_secure_exp_mod_key(smc_args_t *args); +uint32_t smc_secure_exp_mod(smc_args_t *args); +uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args); +uint32_t smc_load_titlekey(smc_args_t *args); +uint32_t smc_unwrap_aes_wrapped_titlekey(smc_args_t *args); + +/* Privileged SMC prototypes */ +uint32_t smc_cpu_suspend(smc_args_t *args); +uint32_t smc_cpu_off(smc_args_t *args); +uint32_t smc_cpu_on(smc_args_t *args); +/* uint32_t smc_get_config(smc_args_t *args); */ +uint32_t smc_get_random_bytes_for_priv(smc_args_t *args); +uint32_t smc_panic(smc_args_t *args); +uint32_t smc_configure_carveout(smc_args_t *args); +uint32_t smc_read_write_register(smc_args_t *args); + +typedef struct { + uint32_t id; + uint32_t (*handler)(smc_args_t *args); +} smc_table_entry_t; + +typedef struct { + smc_table_entry_t *handlers; + uint32_t num_handlers; +} smc_table_t; + +smc_table_entry_t g_smc_user_table[SMC_USER_HANDLERS] = { + {0, NULL}, + {0xC3000401, smc_set_config}, + {0xC3000002, smc_get_config}, + {0xC3000003, smc_check_status}, + {0xC3000404, smc_get_result}, + {0xC3000E05, smc_exp_mod}, + {0xC3000006, smc_get_random_bytes_for_user}, + {0xC3000007, smc_generate_aes_kek}, + {0xC3000008, smc_load_aes_key}, + {0xC3000009, smc_crypt_aes}, + {0xC300000A, smc_generate_specific_aes_key}, + {0xC300040B, smc_compute_cmac}, + {0xC300100C, smc_load_rsa_oaep_key}, + {0xC300100D, smc_decrypt_rsa_private_key}, + {0xC300100E, smc_load_secure_exp_mod_key}, + {0xC300060F, smc_secure_exp_mod}, + {0xC3000610, smc_unwrap_rsa_oaep_wrapped_titlekey}, + {0xC3000011, smc_load_titlekey}, + {0xC3000012, smc_unwrap_aes_wrapped_titlekey} +}; + +smc_table_entry_t g_smc_priv_table[SMC_PRIV_HANDLERS] = { + {0, NULL}, + {0xC4000001, smc_cpu_suspend}, + {0x84000002, smc_cpu_off}, + {0xC4000003, smc_cpu_on}, + {0xC3000004, smc_get_config}, /* NOTE: Same function as for USER */ + {0xC3000005, smc_get_random_bytes_for_priv}, + {0xC3000006, smc_panic}, + {0xC3000007, smc_configure_carveout}, + {0xC3000008, smc_read_write_register} +}; + +smc_table_t g_smc_tables[2] = { + { /* SMC_HANDLER_USER */ + g_smc_user_table, + SMC_USER_HANDLERS + }, + { /* SMC_HANDLER_PRIV */ + g_smc_priv_table, + SMC_PRIV_HANDLERS + } +}; + +bool g_is_user_smc_in_progress = false; +bool g_is_priv_smc_in_progress = false; + +uintptr_t get_smc_core012_stack_address(void) { + return tzram_get_segment_address(TZRAM_SEGMENT_ID_CORE012_STACK) + 0x1000; +} + +uintptr_t get_exception_entry_stack_address(unsigned int core_id) { + /* For core3, this is also the smc stack */ + if (core_id == 3) { + return tzram_get_segment_address(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x1000; + } + else { + return tzram_get_segment_address(TZRAM_SEGEMENT_ID_SECMON_EVT) + 0x80 * (core_id + 1); + } +} + +/* Privileged SMC lock must be available to exceptions.s. */ +void set_priv_smc_in_progress(void) { + lock_acquire(&g_is_priv_smc_in_progress); +} +void clear_priv_smc_in_progress(void) { + lock_release(&g_is_priv_smc_in_progress); +} + +uint32_t (*g_smc_callback)(void *, uint64_t) = NULL; +uint64_t g_smc_callback_key = 0; + +uint64_t try_set_smc_callback(uint32_t (*callback)(void *, uint64_t)) { + uint64_t key; + /* TODO: Atomics... */ + if (g_smc_callback_key) { + return 0; + } + + se_generate_random(KEYSLOT_SWITCH_RNGKEY, &key, sizeof(uint64_t)); + g_smc_callback_key = key; + g_smc_callback = callback; + return key; +} + +void clear_smc_callback(uint64_t key) { + /* TODO: Atomics... */ + if (g_smc_callback_key == key) { + g_smc_callback_key = 0; + } +} + +void call_smc_handler(uint32_t handler_id, smc_args_t *args) { + unsigned char smc_id; + unsigned int result; + unsigned int (*smc_handler)(smc_args_t *args); + + /* Validate top-level handler. */ + if (handler_id != SMC_HANDLER_USER && handler_id != SMC_HANDLER_PRIV) { + generic_panic(); + } + + /* Validate core is appropriate for handler. */ + if (handler_id == SMC_HANDLER_USER && get_core_id() != 3) { + /* USER SMCs must be called via svcCallSecureMonitor on core 3 (where spl runs) */ + generic_panic(); + } + + /* Validate sub-handler index */ + if ((smc_id = (unsigned char)args->X[0]) >= g_smc_tables[handler_id].num_handlers) { + generic_panic(); + } + + /* Validate sub-handler */ + if (g_smc_tables[handler_id].handlers[smc_id].id != args->X[0]) { + generic_panic(); + } + + /* Validate handler. */ + if ((smc_handler = g_smc_tables[handler_id].handlers[smc_id].handler) == NULL) { + generic_panic(); + } + + /* Call function. */ + args->X[0] = smc_handler(args); + (void)result; /* FIXME: result unused */ +} + +uint32_t smc_wrapper_sync(smc_args_t *args, uint32_t (*handler)(smc_args_t *)) { + uint32_t result; + if (!lock_try_acquire(&g_is_user_smc_in_progress)) { + return 3; + } + result = handler(args); + lock_release(&g_is_user_smc_in_progress); + return result; +} + +uint32_t smc_wrapper_async(smc_args_t *args, uint32_t (*handler)(smc_args_t *), uint32_t (*callback)(void *, uint64_t)) { + uint32_t result; + uint64_t key; + if (!lock_try_acquire(&g_is_user_smc_in_progress)) { + return 3; + } + if ((key = try_set_smc_callback(callback)) != 0) { + result = handler(args); + if (result == 0) { + /* Pass the status check key back to userland. */ + args->X[1] = key; + /* Early return, leaving g_is_user_smc_in_progress locked */ + return result; + } else { + /* No status to check. */ + clear_smc_callback(key); + } + } else { + /* smcCheckStatus needs to be called. */ + result = 3; + } + lock_release(&g_is_user_smc_in_progress); + return result; +} + +uint32_t smc_set_config(smc_args_t *args) { + /* Actual value presumed in X3 on hardware. */ + return configitem_set((enum ConfigItem)args->X[1], args->X[3]); +} + +uint32_t smc_get_config(smc_args_t *args) { + uint64_t out_item = 0; + uint32_t result; + result = configitem_get((enum ConfigItem)args->X[1], &out_item); + args->X[1] = out_item; + return result; +} + +uint32_t smc_check_status(smc_args_t *args) { + if (g_smc_callback_key == 0) { + return 4; + } + + if (args->X[1] != g_smc_callback_key) { + return 5; + } + + args->X[1] = g_smc_callback(NULL, 0); + + g_smc_callback_key = 0; + return 0; +} + +uint32_t smc_get_result(smc_args_t *args) { + uint32_t status; + unsigned char result_buf[0x400]; + upage_ref_t page_ref; + + void *user_address = (void *)args->X[2]; + + if (g_smc_callback_key == 0) { + return 4; + } + + if (args->X[1] != g_smc_callback_key) { + return 5; + } + + /* Check result size */ + if (args->X[3] > 0x400) { + return 2; + } + + args->X[1] = g_smc_callback(result_buf, args->X[3]); + g_smc_callback_key = 0; + + /* Initialize page reference. */ + if (upage_init(&page_ref, user_address) == 0) { + return 2; + } + + /* Copy result output back to user. */ + if (secure_copy_to_user(&page_ref, user_address, result_buf, (size_t)args->X[3]) == 0) { + return 2; + } + + return 0; + (void)status; /* FIXME: status unused */ +} + +uint32_t smc_exp_mod_get_result(void *buf, uint64_t size) { + if (get_exp_mod_done() != 1) { + return 3; + } + + if (size != 0x100) { + return 2; + } + + se_get_exp_mod_output(buf, 0x100); + + /* smc_exp_mod is done now. */ + lock_release(&g_is_user_smc_in_progress); + return 0; +} + +uint32_t smc_exp_mod(smc_args_t *args) { + return smc_wrapper_async(args, user_exp_mod, smc_exp_mod_get_result); +} + +uint32_t smc_get_random_bytes_for_user(smc_args_t *args) { + return smc_wrapper_sync(args, user_get_random_bytes); +} + +uint32_t smc_generate_aes_kek(smc_args_t *args) { + return smc_wrapper_sync(args, user_generate_aes_kek); +} + +uint32_t smc_load_aes_key(smc_args_t *args) { + return smc_wrapper_sync(args, user_load_aes_key); +} + +uint32_t smc_crypt_aes_status_check(void *buf, uint64_t size) { + /* Buf and size are unused. */ + if (get_crypt_aes_done() != 1) { + return 3; + } + /* smc_crypt_aes is done now. */ + lock_release(&g_is_user_smc_in_progress); + return 0; +} + +uint32_t smc_crypt_aes(smc_args_t *args) { + return smc_wrapper_async(args, user_crypt_aes, smc_crypt_aes_status_check); +} + +uint32_t smc_generate_specific_aes_key(smc_args_t *args) { + return smc_wrapper_sync(args, user_generate_specific_aes_key); +} + +uint32_t smc_compute_cmac(smc_args_t *args) { + return smc_wrapper_sync(args, user_compute_cmac); +} + +uint32_t smc_load_rsa_oaep_key(smc_args_t *args) { + return smc_wrapper_sync(args, user_load_rsa_oaep_key); +} + +uint32_t smc_decrypt_rsa_private_key(smc_args_t *args) { + return smc_wrapper_sync(args, user_decrypt_rsa_private_key); +} + +uint32_t smc_load_secure_exp_mod_key(smc_args_t *args) { + return smc_wrapper_sync(args, user_load_secure_exp_mod_key); +} + +uint32_t smc_secure_exp_mod(smc_args_t *args) { + return smc_wrapper_async(args, user_secure_exp_mod, smc_exp_mod_get_result); +} + +uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey_get_result(void *buf, uint64_t size) { + uint64_t *p_sealed_key = (uint64_t *)buf; + uint8_t rsa_wrapped_titlekey[0x100]; + uint8_t aes_wrapped_titlekey[0x10]; + uint8_t titlekey[0x10]; + uint64_t sealed_titlekey[2]; + if (get_exp_mod_done() != 1) { + return 3; + } + + if (size != 0x10) { + return 2; + } + + se_get_exp_mod_output(rsa_wrapped_titlekey, 0x100); + if (tkey_rsa_oaep_unwrap(aes_wrapped_titlekey, 0x10, rsa_wrapped_titlekey, 0x100) != 0x10) { + /* Failed to extract RSA OAEP wrapped key. */ + lock_release(&g_is_user_smc_in_progress); + return 2; + } + + tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10); + seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10); + + p_sealed_key[0] = sealed_titlekey[0]; + p_sealed_key[1] = sealed_titlekey[1]; + + /* smc_unwrap_rsa_oaep_wrapped_titlekey is done now. */ + lock_release(&g_is_user_smc_in_progress); + return 0; +} + +uint32_t smc_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { + return smc_wrapper_async(args, user_unwrap_rsa_oaep_wrapped_titlekey, smc_unwrap_rsa_oaep_wrapped_titlekey_get_result); +} + +uint32_t smc_load_titlekey(smc_args_t *args) { + return smc_wrapper_sync(args, user_load_titlekey); +} + +uint32_t smc_unwrap_aes_wrapped_titlekey(smc_args_t *args) { + return smc_wrapper_sync(args, user_unwrap_aes_wrapped_titlekey); +} + +uint32_t smc_cpu_on(smc_args_t *args) { + return cpu_on((uint32_t)args->X[1], args->X[2], args->X[3]); +} + +uint32_t smc_cpu_off(smc_args_t *args) { + return cpu_off(); +} + +/* Wrapper for cpu_suspend */ +uint32_t cpu_suspend_wrapper(smc_args_t *args) { + return cpu_suspend(args->X[1], args->X[2], args->X[3]); +} + +uint32_t smc_cpu_suspend(smc_args_t *args) { + return smc_wrapper_sync(args, cpu_suspend_wrapper); +} + +uint32_t smc_get_random_bytes_for_priv(smc_args_t *args) { + /* This is an interesting SMC. */ + /* The kernel must NEVER be unable to get random bytes, if it needs them */ + /* As such: */ + + uint32_t result; + + if (!lock_try_acquire(&g_is_user_smc_in_progress)) { + if (args->X[1] > 0x38) { + return 2; + } + /* Retrieve bytes from the cache. */ + size_t num_bytes = (size_t)args->X[1]; + randomcache_getbytes(&args->X[1], num_bytes); + result = 0; + } else { + /* If the kernel isn't denied service by a usermode SMC, generate fresh random bytes. */ + result = user_get_random_bytes(args); + /* Also, refill our cache while we have the chance in case we get denied later. */ + randomcache_refill(); + lock_release(&g_is_user_smc_in_progress); + } + return result; +} + +uint32_t smc_read_write_register(smc_args_t *args) { + uint64_t address = args->X[1]; + uint32_t mask = (uint32_t)(args->X[2]); + uint32_t value = (uint32_t)(args->X[3]); + volatile uint32_t *p_mmio = NULL; + /* Address must be aligned. */ + if (address & 3) { + return 2; + } + /* Check for PMC registers. */ + if (0x7000E400 <= address && address <= 0x7000EFFF) { + const uint8_t pmc_whitelist[0x28] = { + 0xB9, 0xF9, 0x07, 0x00, 0x00, 0x00, 0x80, 0x03, + 0x00, 0x00, 0x00, 0x17, 0x00, 0xC4, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00 + }; + /* Offset = Address - PMC_BASE */ + uint32_t offset = (uint32_t)(address - 0x7000E400); + uint32_t wl_ind = (offset >> 5); + /* If address is whitelisted, allow write. */ + if (wl_ind < sizeof(pmc_whitelist) && (pmc_whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7)))) { + p_mmio = (volatile uint32_t *)(PMC_BASE + offset); + } else { + return 2; + } + } else if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT && mmio_get_device_pa(MMIO_DEVID_MC) <= address && + address < mmio_get_device_pa(MMIO_DEVID_MC) + 0x1000) { + /* Memory Controller RW supported only on 4.0.0+ */ + const uint8_t mc_whitelist[0x68] = { + 0x9F, 0x31, 0x30, 0x00, 0xF0, 0xFF, 0xF7, 0x01, + 0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x40, 0x73, 0x3E, 0x2F, 0x00, 0x00, 0x6E, + 0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04, + 0x80, 0x1F, 0x08, 0x80, 0x03, 0x00, 0x0E, 0x00, + 0x08, 0x00, 0xE0, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x31, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F, + 0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00 + }; + uint32_t offset = (uint32_t)(address - 0x70019000); + uint32_t wl_ind = (offset >> 5); + /* If address is whitelisted, allow write. */ + if (wl_ind < sizeof(mc_whitelist) && (mc_whitelist[wl_ind] & (1 << ((offset >> 2) & 0x7)))) { + p_mmio = (volatile uint32_t *)(mmio_get_device_address(MMIO_DEVID_MC) + offset); + } else { + /* These addresses are not allowed by the whitelist. */ + /* They correspond to SMMU DISABLE for the BPMP, and for APB-DMA. */ + /* However, smcReadWriteRegister returns 0 for these addresses despite not actually performing the write. */ + /* This is "probably" to fuck with hackers who got access to smcReadWriteRegister and are trying to get */ + /* control of the BPMP for jamais vu etc., since there's no other reason to return 0 despite failure. */ + if (address == 0x7001923C || address == 0x70019298) { + return 0; + } + return 2; + } + } + + /* Perform actual write. */ + if (p_mmio != NULL) { + uint32_t old_value; + /* Write whole value. */ + if (mask == 0xFFFFFFFF) { + old_value = 0; + } else { + old_value = *p_mmio; + } + if (mask) { + *p_mmio = (old_value & ~mask) | (value & mask); + } + /* Return old value. */ + args->X[1] = old_value; + return 0; + } + + return 2; +} + + +uint32_t smc_configure_carveout(smc_args_t *args) { + if (args->X[0] > 1) { + return 2; + } + + unsigned int carveout_id = (unsigned int)args->X[1]; + uint64_t address = args->X[2]; + uint64_t size = args->X[3]; + + /* Ensure carveout isn't too big. */ + if (size > KERNEL_CARVEOUT_SIZE_MAX) { + return 2; + } + + /* Configuration is one-shot, and cannot be done multiple times. */ + static bool configured_carveouts[2] = {false, false}; + if (configured_carveouts[carveout_id]) { + return 2; + } + + configure_kernel_carveout(carveout_id + 4, address, size); + configured_carveouts[carveout_id] = true; + return 0; +} + +uint32_t smc_panic(smc_args_t *args) { + (void)args; + return 0; + /* TODO */ +} diff --git a/exosphere/src/smc_api.h b/exosphere/src/smc_api.h new file mode 100644 index 0000000..affc683 --- /dev/null +++ b/exosphere/src/smc_api.h @@ -0,0 +1,21 @@ +#ifndef EXOSPHERE_SMC_API_H +#define EXOSPHERE_SMC_API_H + +#include + +#define SMC_HANDLER_USER 0 +#define SMC_HANDLER_PRIV 1 + +typedef struct { + uint64_t X[8]; +} smc_args_t; + +void set_priv_smc_in_progress(void); +void clear_priv_smc_in_progress(void); + +uintptr_t get_smc_core012_stack_address(void); +uintptr_t get_exception_entry_stack_address(unsigned int core_id); + +void call_smc_handler(unsigned int handler_id, smc_args_t *args); + +#endif diff --git a/exosphere/src/smc_user.c b/exosphere/src/smc_user.c new file mode 100644 index 0000000..512f438 --- /dev/null +++ b/exosphere/src/smc_user.c @@ -0,0 +1,617 @@ +#include +#include +#include + +#include "utils.h" +#include "cache.h" +#include "configitem.h" +#include "gcm.h" +#include "masterkey.h" +#include "smc_api.h" +#include "smc_user.h" +#include "se.h" +#include "fuse.h" +#include "sealedkeys.h" +#include "userpage.h" +#include "titlekey.h" + +/* Globals. */ +static bool g_crypt_aes_done = false; +static bool g_exp_mod_done = false; + +static uint8_t g_secure_exp_mod_exponent[0x100]; +static uint8_t g_rsa_oaep_exponent[0x100]; + + +void set_exp_mod_done(bool done) { + g_exp_mod_done = done; +} + +bool get_exp_mod_done(void) { + return g_exp_mod_done; +} + +uint32_t exp_mod_done_handler(void) { + set_exp_mod_done(true); + + se_trigger_interrupt(); + + return 0; +} + +uint32_t user_exp_mod(smc_args_t *args) { + uint8_t modulus[0x100]; + uint8_t exponent[0x100]; + uint8_t input[0x100]; + + upage_ref_t page_ref; + + /* Validate size. */ + if (args->X[4] == 0 || args->X[4] > 0x100 || (args->X[4] & 3) != 0) { + return 2; + } + + size_t exponent_size = (size_t)args->X[4]; + + void *user_input = (void *)args->X[1]; + void *user_exponent = (void *)args->X[2]; + void *user_modulus = (void *)args->X[3]; + + /* Copy user data into secure memory. */ + if (upage_init(&page_ref, user_input) == 0) { + return 2; + } + if (user_copy_to_secure(&page_ref, input, user_input, 0x100) == 0) { + return 2; + } + if (user_copy_to_secure(&page_ref, exponent, user_exponent, exponent_size) == 0) { + return 2; + } + if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { + return 2; + } + + set_exp_mod_done(false); + /* Hardcode RSA keyslot 0. */ + set_rsa_keyslot(0, modulus, 0x100, exponent, exponent_size); + se_exp_mod(0, input, 0x100, exp_mod_done_handler); + + return 0; +} + +uint32_t user_get_random_bytes(smc_args_t *args) { + uint8_t random_bytes[0x40]; + if (args->X[1] > 0x38) { + return 2; + } + + size_t size = (size_t)args->X[1]; + + flush_dcache_range(random_bytes, random_bytes + size); + se_generate_random(KEYSLOT_SWITCH_RNGKEY, random_bytes, size); + flush_dcache_range(random_bytes, random_bytes + size); + + memcpy(&args->X[1], random_bytes, size); + return 0; +} + +uint32_t user_generate_aes_kek(smc_args_t *args) { + uint64_t wrapped_kek[2]; + uint8_t kek_source[0x10]; + uint64_t kek[2]; + uint64_t sealed_kek[2]; + + wrapped_kek[0] = args->X[1]; + wrapped_kek[1] = args->X[2]; + + unsigned int master_key_rev = (unsigned int)args->X[3]; + + if (master_key_rev > 0) { + master_key_rev -= 1; /* GenerateAesKek offsets by one. */ + } + + if (master_key_rev >= MASTERKEY_REVISION_MAX) { + return 2; + } + + uint64_t packed_options = args->X[4]; + if (packed_options > 0xFF) { + return 2; + } + + /* Switched the output based on how the system was booted. */ + uint8_t mask_id = (uint8_t)((packed_options >> 1) & 3); + + /* Switches the output based on how it will be used. */ + uint8_t usecase = (uint8_t)((packed_options >> 5) & 3); + + /* Switched the output based on whether it should be console unique. */ + bool is_personalized = (int)(packed_options & 1); + + bool is_recovery_boot = configitem_is_recovery_boot(); + + /* Mask 2 is only allowed when booted from recovery. */ + if (mask_id == 2 && !is_recovery_boot) { + return 2; + } + /* Mask 1 is only allowed when booted normally. */ + if (mask_id == 1 && is_recovery_boot) { + return 2; + } + + /* Masks 0, 3 are allowed all the time. */ + + const uint8_t kek_seeds[4][0x10] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74}, + {0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C}, + {0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB} + }; + const uint8_t kek_masks[4][0x10] = { + {0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9}, + {0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77}, + {0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81}, + {0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B} + }; + + /* Create kek source. */ + for (unsigned int i = 0; i < 0x10; i++) { + kek_source[i] = kek_seeds[usecase][i] ^ kek_masks[mask_id][i]; + } + + unsigned int keyslot; + if (is_personalized) { + /* Behavior changed in 4.0.0. */ + if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + if (master_key_rev >= 1) { + keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ + } else { + keyslot = KEYSLOT_SWITCH_4XOLDDEVICEKEY; /* Old device key, 4.x. */ + } + } else { + keyslot = KEYSLOT_SWITCH_DEVICEKEY; + } + } else { + keyslot = mkey_get_keyslot(master_key_rev); + } + + /* Derive kek. */ + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, kek_source, 0x10); + se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, kek, 0x10, wrapped_kek, 0x10); + + + /* Seal kek. */ + seal_key(sealed_kek, 0x10, kek, 0x10, usecase); + + args->X[1] = sealed_kek[0]; + args->X[2] = sealed_kek[1]; + + return 0; +} + +uint32_t user_load_aes_key(smc_args_t *args) { + uint64_t sealed_kek[2]; + uint64_t wrapped_key[2]; + + uint32_t keyslot = (uint32_t)args->X[1]; + if (keyslot > 3) { + return 2; + } + + /* Copy keydata */ + sealed_kek[0] = args->X[2]; + sealed_kek[1] = args->X[3]; + wrapped_key[0] = args->X[4]; + wrapped_key[1] = args->X[5]; + + /* Unseal the kek. */ + unseal_key(KEYSLOT_SWITCH_TEMPKEY, sealed_kek, 0x10, CRYPTOUSECASE_AES); + + /* Unwrap the key. */ + decrypt_data_into_keyslot(keyslot, KEYSLOT_SWITCH_TEMPKEY, wrapped_key, 0x10); + return 0; +} + + +void set_crypt_aes_done(bool done) { + g_crypt_aes_done = done; +} + +bool get_crypt_aes_done(void) { + return g_crypt_aes_done; +} + +uint32_t crypt_aes_done_handler(void) { + se_check_for_error(); + + set_crypt_aes_done(true); + + se_trigger_interrupt(); + + return 0; +} + +uint32_t user_crypt_aes(smc_args_t *args) { + uint32_t keyslot = args->X[1] & 3; + uint32_t mode = (args->X[1] >> 4) & 3; + + uint64_t iv_ctr[2]; + iv_ctr[0] = args->X[2]; + iv_ctr[1] = args->X[3]; + + uint32_t in_ll_paddr = (uint32_t)(args->X[4]); + uint32_t out_ll_paddr = (uint32_t)(args->X[5]); + + size_t size = args->X[6]; + if (size & 0xF) { + generic_panic(); + } + + set_crypt_aes_done(false); + + uint64_t result = 0; + switch (mode) { + case 0: /* CBC Encryption */ + se_aes_cbc_encrypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler); + result = 0; + break; + case 1: /* CBC Decryption */ + se_aes_cbc_decrypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler); + result = 0; + break; + case 2: /* CTR "Encryption" */ + se_aes_ctr_crypt_insecure(keyslot, out_ll_paddr, in_ll_paddr, size, iv_ctr, crypt_aes_done_handler); + result = 0; + break; + case 3: + default: + result = 1; + break; + } + + return result; +} + +uint32_t user_generate_specific_aes_key(smc_args_t *args) { + uint64_t wrapped_key[2]; + uint8_t key[0x10]; + unsigned int master_key_rev; + bool should_mask; + + wrapped_key[0] = args->X[1]; + wrapped_key[1] = args->X[2]; + if (args->X[4] > MASTERKEY_REVISION_MAX) { + return 2; + } + master_key_rev = (unsigned int)(args->X[4]); + if (args->X[3] > 1) { + return 2; + } + should_mask = args->X[3] != 0; + + unsigned int keyslot; + + /* Behavior changed in 4.0.0. */ + if (mkey_get_revision() >= MASTERKEY_REVISION_400_CURRENT) { + if (master_key_rev >= 2) { + keyslot = KEYSLOT_SWITCH_DEVICEKEY; /* New device key, 4.x. */ + } else { + keyslot = KEYSLOT_SWITCH_4XOLDDEVICEKEY; /* Old device key, 4.x. */ + } + } else { + keyslot = KEYSLOT_SWITCH_DEVICEKEY; + } + + if (fuse_get_bootrom_patch_version() < 0x7F) { + /* On dev units, use a fixed "all-zeroes" seed. */ + /* Yes, this data really is all-zero in actual TrustZone .rodata. */ + uint8_t dev_specific_aes_key_source[0x10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t dev_specific_aes_key_ctr[0x10] = {0x3C, 0xD5, 0x92, 0xEC, 0x68, 0x31, 0x4A, 0x06, 0xD4, 0x1B, 0x0C, 0xD9, 0xF6, 0x2E, 0xD9, 0xE9}; + uint8_t dev_specific_aes_key_mask[0x10] = {0xAC, 0xCA, 0x9A, 0xCA, 0xFF, 0x2E, 0xB9, 0x22, 0xCC, 0x1F, 0x4F, 0xAD, 0xDD, 0x77, 0x21, 0x1E}; + + flush_dcache_range(key, key + 0x10); + se_aes_ctr_crypt(keyslot, key, 0x10, dev_specific_aes_key_source, 0x10, dev_specific_aes_key_ctr, 0x10); + flush_dcache_range(key, key + 0x10); + + if (should_mask) { + for (unsigned int i = 0; i < 0x10; i++) { + key[i] ^= dev_specific_aes_key_mask[i]; + } + } + } else { + /* On retail, standard kek->key decryption. */ + uint8_t retail_specific_aes_key_source[0x10] = {0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95}; + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, keyslot, retail_specific_aes_key_source, 0x10); + se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, key, 0x10, wrapped_key, 0x10); + } + + + args->X[1] = key[0]; + args->X[2] = key[1]; + return 0; +} + +uint32_t user_compute_cmac(smc_args_t *args) { + uint32_t keyslot = (uint32_t)args->X[1]; + void *user_address = (void *)args->X[2]; + size_t size = (size_t)args->X[3]; + + uint8_t user_data[0x400]; + uint64_t result_cmac[2]; + upage_ref_t page_ref; + + /* Validate keyslot and size. */ + if (keyslot > 3 || args->X[3] > 0x400) { + return 2; + } + + if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { + return 2; + } + + flush_dcache_range(user_data, user_data + size); + se_compute_aes_128_cmac(keyslot, result_cmac, 0x10, user_data, size); + + /* Copy CMAC out. */ + args->X[1] = result_cmac[0]; + args->X[2] = result_cmac[1]; + + return 0; +} + +uint32_t user_load_rsa_oaep_key(smc_args_t *args) { + uint64_t sealed_kek[2]; + uint64_t wrapped_key[2]; + bool is_personalized; + + uint8_t user_data[0x400]; + void *user_address; + size_t size; + upage_ref_t page_ref; + + + /* Copy keydata */ + sealed_kek[0] = args->X[1]; + sealed_kek[1] = args->X[2]; + if (args->X[3] > 1) { + return 2; + } + is_personalized = args->X[3] != 0; + user_address = (void *)args->X[4]; + size = (size_t)args->X[5]; + wrapped_key[0] = args->X[6]; + wrapped_key[1] = args->X[7]; + + if (is_personalized && size != 0x240) { + return 2; + } + if (!is_personalized && (size != 0x220 || fuse_get_bootrom_patch_version() >= 0x7F)) { + return 2; + } + + if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { + return 2; + } + + /* Ensure that our private key is 0x100 bytes. */ + if (gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAOAEP, is_personalized) < 0x100) { + return 2; + } + + memcpy(g_rsa_oaep_exponent, user_data, 0x100); + return 0; +} + +uint32_t user_decrypt_rsa_private_key(smc_args_t *args) { + uint64_t sealed_kek[2]; + uint64_t wrapped_key[2]; + bool is_personalized; + + uint8_t user_data[0x400]; + void *user_address; + size_t size; + upage_ref_t page_ref; + + + /* Copy keydata */ + sealed_kek[0] = args->X[1]; + sealed_kek[1] = args->X[2]; + if (args->X[3] > 1) { + return 2; + } + is_personalized = args->X[3] != 0; + user_address = (void *)args->X[4]; + size = (size_t)args->X[5]; + wrapped_key[0] = args->X[6]; + wrapped_key[1] = args->X[7]; + + if (size > 0x240) { + return 2; + } + + if (is_personalized && size < 0x31) { + return 2; + } + if (!is_personalized && (size < 0x11 || fuse_get_bootrom_patch_version() >= 0x7F)) { + return 2; + } + + if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { + return 2; + } + + size_t out_size; + + if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_RSAPRIVATE, is_personalized)) == 0) { + return 2; + } + + if (secure_copy_to_user(&page_ref, user_address, user_data, size) == 0) { + return 2; + } + + args->X[1] = out_size; + return 0; +} + +uint32_t user_load_secure_exp_mod_key(smc_args_t *args) { + uint64_t sealed_kek[2]; + uint64_t wrapped_key[2]; + bool is_personalized; + + uint8_t user_data[0x400]; + void *user_address; + size_t size; + upage_ref_t page_ref; + + + /* Copy keydata */ + sealed_kek[0] = args->X[1]; + sealed_kek[1] = args->X[2]; + if (args->X[3] > 1) { + return 2; + } + is_personalized = args->X[3] != 0; + user_address = (void *)args->X[4]; + size = (size_t)args->X[5]; + wrapped_key[0] = args->X[6]; + wrapped_key[1] = args->X[7]; + + if (is_personalized && size != 0x130) { + return 2; + } + if (!is_personalized && (size != 0x110 || fuse_get_bootrom_patch_version() >= 0x7F)) { + return 2; + } + + if (upage_init(&page_ref, user_address) == 0 || user_copy_to_secure(&page_ref, user_data, user_address, size) == 0) { + return 2; + } + + size_t out_size; + + /* Ensure that our key is non-zero bytes. */ + if ((out_size = gcm_decrypt_key(user_data, size, user_data, size, sealed_kek, 0x10, wrapped_key, 0x10, CRYPTOUSECASE_SECUREEXPMOD, is_personalized)) == 0) { + return 2; + } + + /* Copy key to global. */ + if (out_size <= 0x100) { + memcpy(g_secure_exp_mod_exponent, user_data, out_size); + } else { + memcpy(g_secure_exp_mod_exponent, user_data, 0x100); + } + + return 0; +} + +uint32_t user_secure_exp_mod(smc_args_t *args) { + uint8_t modulus[0x100]; + uint8_t input[0x100]; + + upage_ref_t page_ref; + + void *user_input = (void *)args->X[1]; + void *user_modulus = (void *)args->X[2]; + + /* Copy user data into secure memory. */ + if (upage_init(&page_ref, user_input) == 0) { + return 2; + } + if (user_copy_to_secure(&page_ref, input, user_input, 0x100) == 0) { + return 2; + } + if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { + return 2; + } + + set_exp_mod_done(false); + /* Hardcode RSA keyslot 0. */ + set_rsa_keyslot(0, modulus, 0x100, g_secure_exp_mod_exponent, 0x100); + se_exp_mod(0, input, 0x100, exp_mod_done_handler); + + return 0; +} + +uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args) { + uint8_t modulus[0x100]; + uint8_t wrapped_key[0x100]; + + upage_ref_t page_ref; + + void *user_wrapped_key = (void *)args->X[1]; + void *user_modulus = (void *)args->X[2]; + unsigned int master_key_rev = (unsigned int)args->X[7]; + + if (master_key_rev >= MASTERKEY_REVISION_MAX) { + return 2; + } + + /* Copy user data into secure memory. */ + if (upage_init(&page_ref, user_wrapped_key) == 0) { + return 2; + } + if (user_copy_to_secure(&page_ref, wrapped_key, user_wrapped_key, 0x100) == 0) { + return 2; + } + if (user_copy_to_secure(&page_ref, modulus, user_modulus, 0x100) == 0) { + return 2; + } + + set_exp_mod_done(false); + + /* Expected label_hash occupies args->X[3] to args->X[6]. */ + tkey_set_expected_label_hash(&args->X[3]); + + tkey_set_master_key_rev(master_key_rev); + + /* Hardcode RSA keyslot 0. */ + set_rsa_keyslot(0, modulus, 0x100, g_rsa_oaep_exponent, 0x100); + se_exp_mod(0, wrapped_key, 0x100, exp_mod_done_handler); + + return 0; + +} + +uint32_t user_load_titlekey(smc_args_t *args) { + uint64_t sealed_titlekey[2]; + + uint32_t keyslot = (uint32_t)args->X[1]; + if (keyslot > 3) { + return 2; + } + + /* Copy keydata */ + sealed_titlekey[0] = args->X[2]; + sealed_titlekey[1] = args->X[3]; + + /* Unseal the key. */ + unseal_titlekey(keyslot, sealed_titlekey, 0x10); + return 0; + +} + +uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args) { + uint64_t aes_wrapped_titlekey[2]; + uint8_t titlekey[0x10]; + uint64_t sealed_titlekey[2]; + + aes_wrapped_titlekey[0] = args->X[1]; + aes_wrapped_titlekey[1] = args->X[2]; + unsigned int master_key_rev = (unsigned int)args->X[3]; + + + if (master_key_rev >= MASTERKEY_REVISION_MAX) { + return 2; + } + + tkey_set_master_key_rev(master_key_rev); + + tkey_aes_unwrap(titlekey, 0x10, aes_wrapped_titlekey, 0x10); + seal_titlekey(sealed_titlekey, 0x10, titlekey, 0x10); + + args->X[1] = sealed_titlekey[0]; + args->X[2] = sealed_titlekey[1]; + + return 0; /* FIXME: what should we return there */ +} diff --git a/exosphere/src/smc_user.h b/exosphere/src/smc_user.h new file mode 100644 index 0000000..58deda4 --- /dev/null +++ b/exosphere/src/smc_user.h @@ -0,0 +1,28 @@ +#ifndef EXOSPHERE_SMC_USER_H +#define EXOSPHERE_SMC_USER_H + +#include "smc_api.h" + +uint32_t user_exp_mod(smc_args_t *args); +uint32_t user_get_random_bytes(smc_args_t *args); +uint32_t user_generate_aes_kek(smc_args_t *args); +uint32_t user_load_aes_key(smc_args_t *args); +uint32_t user_crypt_aes(smc_args_t *args); +uint32_t user_generate_specific_aes_key(smc_args_t *args); +uint32_t user_compute_cmac(smc_args_t *args); +uint32_t user_load_rsa_oaep_key(smc_args_t *args); +uint32_t user_decrypt_rsa_private_key(smc_args_t *args); +uint32_t user_load_secure_exp_mod_key(smc_args_t *args); +uint32_t user_secure_exp_mod(smc_args_t *args); +uint32_t user_unwrap_rsa_oaep_wrapped_titlekey(smc_args_t *args); +uint32_t user_load_titlekey(smc_args_t *args); +uint32_t user_unwrap_aes_wrapped_titlekey(smc_args_t *args); + + +void set_crypt_aes_done(bool done); +bool get_crypt_aes_done(void); + +void set_exp_mod_done(bool done); +bool get_exp_mod_done(void); + +#endif \ No newline at end of file diff --git a/exosphere/src/start.s b/exosphere/src/start.s new file mode 100644 index 0000000..d1a611e --- /dev/null +++ b/exosphere/src/start.s @@ -0,0 +1,150 @@ +/* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */ +#define cpuactlr_el1 s3_1_c15_c2_0 +.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. */ + /* mask all interrupts */ + msr daifset, 0b1111 + + /* + 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 + + 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 +.rept 65 + nop /* guard against speculative excecution */ +.endr + + 2: + /* set the OS lock */ + mov x0, #1 + msr oslar_el1, x0 +.endm + +.align 6 +.section .text.cold.start, "ax", %progbits +.global __start_cold +__start_cold: + ERRATUM_INVALIDATE_BTB_AT_BOOT + + msr spsel, #0 + bl get_coldboot_crt0_stack_address /* should be optimized so it doesn't make function calls */ + mov sp, x0 + bl coldboot_init + ldr x16, =__jump_to_main_cold + br x16 + +.align 6 +.section .text.warm.start, "ax", %progbits +.global __start_warm +__start_warm: + ERRATUM_INVALIDATE_BTB_AT_BOOT + + /* For some reasons, Nintendo uses spsel, #1 here, causing issues if an exception occurs */ + msr spsel, #0 + bl get_warmboot_crt0_stack_address /* should be optimized so it doesn't make function calls */ + mov sp, x0 + bl warmboot_init + ldr x16, =__jump_to_main_warm + br x16 + +.section .text.__jump_to_main_cold, "ax", %progbits +__jump_to_main_cold: + bl __set_exception_entry_stack_pointer + + bl get_pk2ldr_stack_address + mov sp, x0 + bl load_package2 + + mov w0, #3 /* use core3 stack temporarily */ + bl get_exception_entry_stack_address + mov sp, x0 + b coldboot_main + +.section .text.__jump_to_main_warm, "ax", %progbits +__jump_to_main_warm: + /* Nintendo doesn't do that here, causing issues if an exception occurs */ + bl __set_exception_entry_stack_pointer + + bl get_pk2ldr_stack_address + mov sp, x0 + bl load_package2 + + mov w0, #3 /* use core0,1,2 stack bottom + 0x800 (VA of warmboot crt0 sp) temporarily */ + bl get_exception_entry_stack_address + add sp, x0, #0x800 + b warmboot_main + +.section .text.__set_exception_entry_stack, "ax", %progbits +.type __set_exception_entry_stack, %function +.global __set_exception_entry_stack +__set_exception_entry_stack_pointer: + /* If SPSel == 1 on entry, make sure your function doesn't use stack variables! */ + mov x16, lr + mrs x17, spsel + mrs x0, mpidr_el1 + and w0, w0, #3 + bl get_exception_entry_stack_address /* should be optimized so it doesn't make function calls */ + msr spsel, #1 + mov sp, x0 + msr spsel, x17 + mov lr, x16 + ret + +.section .text.__jump_to_lower_el, "ax", %progbits +.global __jump_to_lower_el +.type __jump_to_lower_el, %function +__jump_to_lower_el: + /* x0: arg (context ID), x1: entrypoint, w2: exception level */ + msr elr_el3, x1 + + mov w1, #((0b1111 << 6) | 1) /* DAIF set and SP = SP_ELx*/ + orr w1, w2, w2, lsl#2 + msr spsr_el3, x1 + + bl __set_exception_entry_stack_pointer + + isb + eret diff --git a/exosphere/src/timers.c b/exosphere/src/timers.c new file mode 100644 index 0000000..b339a40 --- /dev/null +++ b/exosphere/src/timers.c @@ -0,0 +1,8 @@ +#include "timers.h" + +void wait(uint32_t microseconds) { + uint32_t old_time = TIMERUS_CNTR_1US_0; + while (TIMERUS_CNTR_1US_0 - old_time <= microseconds) { + /* Spin-lock. */ + } +} \ No newline at end of file diff --git a/exosphere/src/timers.h b/exosphere/src/timers.h new file mode 100644 index 0000000..db9559c --- /dev/null +++ b/exosphere/src/timers.h @@ -0,0 +1,15 @@ +#ifndef EXOSPHERE_TIMERS_H +#define EXOSPHERE_TIMERS_H + +#include +#include "memory_map.h" + +/* Exosphere driver for the Tegra X1 Timers. */ + +#define TIMERS_BASE (mmio_get_device_address(MMIO_DEVID_TMRs_WDTs)) + +#define TIMERUS_CNTR_1US_0 (*((volatile uint32_t *)(TIMERS_BASE + 0x10))) + +void wait(uint32_t microseconds); + +#endif diff --git a/exosphere/src/titlekey.c b/exosphere/src/titlekey.c new file mode 100644 index 0000000..a55bd21 --- /dev/null +++ b/exosphere/src/titlekey.c @@ -0,0 +1,156 @@ +#include +#include + +#include "utils.h" +#include "cache.h" + +#include "titlekey.h" +#include "masterkey.h" +#include "se.h" + +uint64_t g_tkey_expected_label_hash[4]; +unsigned int g_tkey_master_key_rev = MASTERKEY_REVISION_MAX; + +/* Set the expected db prefix. */ +void tkey_set_expected_label_hash(uint64_t *label_hash) { + for (unsigned int i = 0; i < 4; i++) { + g_tkey_expected_label_hash[i] = label_hash[i]; + } +} + +void tkey_set_master_key_rev(unsigned int master_key_rev) { + if (master_key_rev >= MASTERKEY_REVISION_MAX) { + generic_panic(); + } +} + +/* Reference for MGF1 can be found here: https://en.wikipedia.org/wiki/Mask_generation_function#MGF1 */ +void calculate_mgf1_and_xor(void *masked, size_t masked_size, const void *seed, size_t seed_size) { + uint8_t cur_hash[0x20]; + uint8_t hash_buf[0xE4]; + if (seed_size >= 0xE0) { + generic_panic(); + } + + size_t hash_buf_size = seed_size + 4; + memcpy(hash_buf, seed, seed_size); + + uint32_t round = 0; + + uint8_t *p_out = (uint8_t *)masked; + + while (masked_size) { + size_t cur_size = masked_size; + if (cur_size > 0x20) { + cur_size = 0x20; + } + + hash_buf[seed_size + 0] = (uint8_t)((round >> 24) & 0xFF); + hash_buf[seed_size + 1] = (uint8_t)((round >> 16) & 0xFF); + hash_buf[seed_size + 2] = (uint8_t)((round >> 8) & 0xFF); + hash_buf[seed_size + 3] = (uint8_t)((round >> 0) & 0xFF); + round++; + + flush_dcache_range(hash_buf, hash_buf + hash_buf_size); + se_calculate_sha256(cur_hash, hash_buf, hash_buf_size); + + for (unsigned int i = 0; i < cur_size; i++) { + *p_out ^= cur_hash[i]; + p_out++; + } + + masked_size -= cur_size; + } +} + +size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_size) { + if (src_size != 0x100) { + generic_panic(); + } + + /* RSA Wrapped titlekeys use RSA-OAEP. */ + /* Message is of the form prefix || maskedSalt || maskedDB. */ + /* maskedSalt = salt ^ MGF1(maskedDB) */ + /* maskedDB = DB ^ MGF1(salt) */ + /* Salt is random and not validated in any way. */ + /* DB is of the form label_hash || 00....01 || wrapped_titlekey. */ + /* label_hash is, in practice, a constant in es .rodata. */ + /* I have no idea why Nintendo did this, it should be either nonconstant (in tik) or in tz .rodata. */ + + uint8_t *message = (uint8_t *)src; + + /* Prefix should always be zero. */ + if (*message != 0) { + return 0; + } + + + uint8_t *salt = message + 1; + uint8_t *db = message + 0x21; + + /* This will be passed to smc_unwrap_rsa_oaep_wrapped_titlekey. */ + uint8_t *expected_label_hash = (uint8_t *)(&g_tkey_expected_label_hash[0]); + + /* Unmask the salt. */ + calculate_mgf1_and_xor(salt, 0x20, db, 0xDF); + /* Unmask the DB. */ + calculate_mgf1_and_xor(db, 0xDF, salt, 0x20); + + /* Validate expected salt. */ + for (unsigned int i = 0; i < 0x20; i++) { + if (expected_label_hash[i] != db[i]) { + return 0; + } + } + + /* Don't validate salt from message[1:0x21] at all. */ + + /* Advance pointer to DB, since we've validated the salt prefix. */ + db += 0x20; + + /* DB must be of the form 0000...01 || wrapped_titlekey */ + if (*db != 0) { + return 0; + } + + /* Locate wrapped_titlekey inside DB. */ + size_t wrapped_key_offset_in_db = 0; + while (wrapped_key_offset_in_db < 0xBF) { + if (db[wrapped_key_offset_in_db] == 0) { + wrapped_key_offset_in_db++; + } else if (db[wrapped_key_offset_in_db] == 1) { + wrapped_key_offset_in_db++; + break; + } else { + /* Invalid wrapped titlekey prefix. */ + return 0; + } + } + + /* Validate size... */ + size_t wrapped_titlekey_size = 0xBF - wrapped_key_offset_in_db; + if (wrapped_titlekey_size > dst_size || wrapped_titlekey_size == 0) { + return 0; + } + + /* Extract the wrapped key. */ + memcpy(dst, &db[wrapped_key_offset_in_db], wrapped_titlekey_size); + return wrapped_key_offset_in_db; +} + +void tkey_aes_unwrap(void *dst, size_t dst_size, const void *src, size_t src_size) { + if (g_tkey_master_key_rev >= MASTERKEY_REVISION_MAX || dst_size != 0x10 || src_size != 0x10) { + generic_panic(); + } + + const uint8_t titlekek_source[0x10] = { + 0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B + }; + + /* Generate the appropriate titlekek into keyslot 9. */ + unsigned int master_keyslot = mkey_get_keyslot(g_tkey_master_key_rev); + decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, master_keyslot, titlekek_source, 0x10); + + /* Unwrap the titlekey using the titlekek. */ + se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10); +} diff --git a/exosphere/src/titlekey.h b/exosphere/src/titlekey.h new file mode 100644 index 0000000..a74949b --- /dev/null +++ b/exosphere/src/titlekey.h @@ -0,0 +1,13 @@ +#ifndef EXOSPHERE_TITLEKEY_H +#define EXOSPHERE_TITLEKEY_H + +#include + +void tkey_set_expected_label_hash(uint64_t *label_hash); +void tkey_set_master_key_rev(unsigned int master_key_rev); + +size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_size); + +void tkey_aes_unwrap(void *dst, size_t dst_size, const void *src, size_t src_size); + +#endif \ No newline at end of file diff --git a/exosphere/src/uart.c b/exosphere/src/uart.c new file mode 100644 index 0000000..409d04d --- /dev/null +++ b/exosphere/src/uart.c @@ -0,0 +1,36 @@ +#include "uart.h" + +void uart_initialize(uint16_t divider) { + /* Setup UART in 16450 mode. We assume the relevant UART clock has been enabled. */ + + /* Disable FIFO */ + UART_IIR_FCR_0 = 0x00; + + /* Set DLAB */ + UART_LCR_0 = 0x80; + UART_THR_DLAB_0_0 = (uint8_t)divider; + UART_IER_DLAB_0_0 = (uint8_t)(divider >> 8); + + /* 8N1 mode */ + UART_LCR_0 = 0x03; +} + +void uart_transmit_char(char ch) { + /* Wait for THR to be empty */ + while (!(UART_LSR_0 & 0x20)) {} + + UART_THR_DLAB_0_0 = ch; +} + +void uart_transmit_str(const char *str) { + while (*str) { + uart_transmit_char(*str++); + } +} + +void uart_transmit_hex(uint32_t value) { + for (unsigned int i = 0; i < 8; i++) { + uint32_t nibble = (value >> (28 - i * 4)) & 0xF; + uart_transmit_char("0123456789ABCDEF"[nibble]); + } +} \ No newline at end of file diff --git a/exosphere/src/uart.h b/exosphere/src/uart.h new file mode 100644 index 0000000..bba7dcd --- /dev/null +++ b/exosphere/src/uart.h @@ -0,0 +1,24 @@ +#ifndef EXOSPHERE_UART_H +#define EXOSPHERE_UART_H + +#include +#include "memory_map.h" + +/* Exosphere driver for the Tegra X1 UARTs. */ + +/* TODO: Should we bother with support UARTB-D? */ + +#define UARTA_BASE (mmio_get_device_address(MMIO_DEVID_UART_A)) + +#define UART_THR_DLAB_0_0 (*((volatile uint32_t *)(UARTA_BASE + 0x0))) +#define UART_IER_DLAB_0_0 (*((volatile uint32_t *)(UARTA_BASE + 0x4))) +#define UART_IIR_FCR_0 (*((volatile uint32_t *)(UARTA_BASE+ 0x8))) +#define UART_LCR_0 (*((volatile uint32_t *)(UARTA_BASE + 0xC))) +#define UART_LSR_0 (*((volatile uint32_t *)(UARTA_BASE + 0x14))) + +void uart_initialize(uint16_t divider); +void uart_transmit_char(char ch); +void uart_transmit_str(const char *str); +void uart_transmit_hex(uint32_t value); + +#endif diff --git a/exosphere/src/userpage.c b/exosphere/src/userpage.c new file mode 100644 index 0000000..d767a36 --- /dev/null +++ b/exosphere/src/userpage.c @@ -0,0 +1,71 @@ +#include + +#include "utils.h" +#include "userpage.h" +#include "memory_map.h" +#include "cache.h" + +static uintptr_t g_user_page_user_address = 0ULL; + +static inline uintptr_t get_page_for_address(void *address) { + return ((uintptr_t)(address)) & ~0xFFFULL; +} + +/* Create a user page reference for the desired address. */ +/* Returns 1 on success, 0 on failure. */ +bool upage_init(upage_ref_t *upage, void *user_address) { + upage->user_address = get_page_for_address(user_address); + upage->secure_monitor_address = 0ULL; + + if (g_user_page_user_address != 0ULL) { + /* Different physical address indicate SPL was rebooted, or another process got access to svcCallSecureMonitor. Panic. */ + if (g_user_page_user_address != upage->user_address) { + generic_panic(); + } + upage->secure_monitor_address = USER_PAGE_SECURE_MONITOR_ADDR; + } else { + /* Weakly validate SPL's physically random address is in DRAM. */ + if (upage->user_address >> 31) { + static const uint64_t userpage_attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_NORMAL; + uintptr_t *mmu_l3_tbl = (uintptr_t *)tzram_get_segment_address(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); + g_user_page_user_address = upage->user_address; + mmu_map_page(mmu_l3_tbl, USER_PAGE_SECURE_MONITOR_ADDR, upage->user_address, userpage_attributes); + tlb_invalidate_page_inner_shareable((void *)USER_PAGE_SECURE_MONITOR_ADDR); + upage->secure_monitor_address = USER_PAGE_SECURE_MONITOR_ADDR; + } + } + + return upage->secure_monitor_address != 0ULL; +} + +bool user_copy_to_secure(upage_ref_t *upage, void *secure_dst, void *user_src, size_t size) { + /* Fail if the page doesn't match. */ + if (get_page_for_address(user_src) != upage->user_address) { + return false; + } + + /* Fail if we go past the page boundary. */ + if (size != 0 && get_page_for_address(user_src + size - 1) != upage->user_address) { + return false; + } + + void *secure_src = (void *)(upage->secure_monitor_address + ((uintptr_t)user_src - upage->user_address)); + memcpy(secure_dst, secure_src, size); + return true; +} + +bool secure_copy_to_user(upage_ref_t *upage, void *user_dst, void *secure_src, size_t size) { + /* Fail if the page doesn't match. */ + if (get_page_for_address(user_dst) != upage->user_address) { + return false; + } + + /* Fail if we go past the page boundary. */ + if (size != 0 && get_page_for_address(user_dst + size - 1) != upage->user_address) { + return false; + } + + void *secure_dst = (void *)(upage->secure_monitor_address + ((uintptr_t)user_dst - upage->user_address)); + memcpy(secure_dst, secure_src, size); + return true; +} diff --git a/exosphere/src/userpage.h b/exosphere/src/userpage.h new file mode 100644 index 0000000..a522514 --- /dev/null +++ b/exosphere/src/userpage.h @@ -0,0 +1,19 @@ +#ifndef EXOSPHERE_USERPAGE_H +#define EXOSPHERE_USERPAGE_H + +#include "utils.h" +#include "memory_map.h" + +#define USER_PAGE_SECURE_MONITOR_ADDR (tzram_get_segment_address(TZRAM_SEGMENT_ID_USERPAGE)) + +typedef struct { + uintptr_t user_address; + uintptr_t secure_monitor_address; +} upage_ref_t; + +bool upage_init(upage_ref_t *user_page, void *user_address); + +bool user_copy_to_secure(upage_ref_t *user_page, void *secure_dst, void *user_src, size_t size); +bool secure_copy_to_user(upage_ref_t *user_page, void *user_dst, void *secure_src, size_t size); + +#endif diff --git a/exosphere/src/utils.c b/exosphere/src/utils.c new file mode 100644 index 0000000..2a8bbad --- /dev/null +++ b/exosphere/src/utils.c @@ -0,0 +1,18 @@ +#include "utils.h" + +void panic(uint32_t code) { + (void)code; /* TODO */ +} + +void generic_panic(void) { + /* TODO */ +} + +__attribute__((noinline)) bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be) +{ + if(as <= bs && bs <= ae) + return true; + if(bs <= as && as <= be) + return true; + return false; +} diff --git a/exosphere/src/utils.h b/exosphere/src/utils.h new file mode 100644 index 0000000..c731d27 --- /dev/null +++ b/exosphere/src/utils.h @@ -0,0 +1,44 @@ +#ifndef EXOSPHERE_UTILS_H +#define EXOSPHERE_UTILS_H + +#include +#include +#include + +#define BIT(x) (1u << (x)) +#define BITL(x) (1ull << (x)) + +void panic(uint32_t code); +void generic_panic(void); +bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be); + +static inline uintptr_t get_physical_address(const void *vaddr) { + uintptr_t PAR; + __asm__ __volatile__ ("at s1e3r, %0" :: "r"(vaddr)); + __asm__ __volatile__ ("mrs %0, par_el1" : "=r"(PAR)); + return (PAR & 1) ? 0ULL : (PAR & 0x00000FFFFFFFF000ULL) | ((uintptr_t)vaddr & 0xFFF); +} + +static inline uint32_t read32le(const volatile void *dword, size_t offset) { + return *(uint32_t *)((uintptr_t)dword + offset); +} + +static inline uint32_t read32be(const volatile void *dword, size_t offset) { + return __builtin_bswap32(read32le(dword, offset)); +} + +static inline uint64_t read64le(const volatile void *qword, size_t offset) { + return *(uint64_t *)((uintptr_t)qword + offset); +} + +static inline unsigned int get_core_id(void) { + uint64_t core_id; + __asm__ __volatile__ ("mrs %0, mpidr_el1" : "=r"(core_id)); + return (unsigned int)core_id & 3; +} + +static inline bool check_32bit_additive_overflow(uint32_t a, uint32_t b) { + return __builtin_add_overflow_p(a, b, (uint32_t)0); +} + +#endif diff --git a/exosphere/src/warmboot_init.c b/exosphere/src/warmboot_init.c new file mode 100644 index 0000000..e28005a --- /dev/null +++ b/exosphere/src/warmboot_init.c @@ -0,0 +1,16 @@ +#include "utils.h" +#include "memory_map.h" + +uintptr_t get_warmboot_crt0_stack_address(void); + +void flush_dcache_all_tzram_pa(void) { + /* TODO */ +} + +void invalidate_icache_all_tzram_pa(void) { + /* TODO */ +} + +uintptr_t get_warmboot_crt0_stack_address(void) { + return tzram_get_segment_pa(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x800; +} diff --git a/exosphere/src/warmboot_main.c b/exosphere/src/warmboot_main.c new file mode 100644 index 0000000..486bfa4 --- /dev/null +++ b/exosphere/src/warmboot_main.c @@ -0,0 +1,11 @@ +#include "utils.h" +#include "mmu.h" +#include "memory_map.h" + +extern void __jump_to_lower_el(uint64_t arg, uintptr_t ep, unsigned int el); + +void warmboot_main(void); + +void warmboot_main(void) { + /* TODO */ +} diff --git a/exosphere/start.s b/exosphere/start.s deleted file mode 100644 index aee471a..0000000 --- a/exosphere/start.s +++ /dev/null @@ -1,148 +0,0 @@ -.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. */ - /* mask all interrupts */ - msr daifset, 0b1111 - - /* - 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 _set_lock_and_sp - mov x0, xzr - msr oslar_el1, x0 - - 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. */ - _reset_wfi: - wfi - b _reset_wfi -.rept 65 - nop /* guard against speculative excecution */ -.endr - - _set_lock_and_sp: - /* set the OS lock */ - mov x0, #1 - msr oslar_el1, x0 -.endm - -.align 6 -.section .text.cold.start, "ax", %progbits -.global __start_cold -__start_cold: - ERRATUM_INVALIDATE_BTB_AT_BOOT - - msr spsel, #0 - bl get_coldboot_crt0_stack_address /* should be optimized so it doesn't make function calls */ - mov sp, x0 - bl coldboot_init - ldr x16, =__jump_to_main_cold - br x16 - -.align 6 -.section .text.warm.start, "ax", %progbits -.global __start_warm -__start_warm: - ERRATUM_INVALIDATE_BTB_AT_BOOT - - /* For some reasons, Nintendo uses spsel, #1 here, causing issues if an exception occurs */ - msr spsel, #0 - bl get_warmboot_crt0_stack_address /* should be optimized so it doesn't make function calls */ - mov sp, x0 - bl warmboot_init - ldr x16, =__jump_to_main_warm - br x16 - -.section .text.__jump_to_main_cold, "ax", %progbits -__jump_to_main_cold: - bl __set_exception_entry_stack_pointer - - bl get_pk2ldr_stack_address - mov sp, x0 - bl load_package2 - - mov w0, #3 /* use core3 stack temporarily */ - bl get_exception_entry_stack_address - mov sp, x0 - b coldboot_main - -.section .text.__jump_to_main_warm, "ax", %progbits -__jump_to_main_warm: - /* Nintendo doesn't do that here, causing issues if an exception occurs */ - bl __set_exception_entry_stack_pointer - - bl get_pk2ldr_stack_address - mov sp, x0 - bl load_package2 - - mov w0, #3 /* use core0,1,2 stack bottom + 0x800 (VA of warmboot crt0 sp) temporarily */ - bl get_exception_entry_stack_address - add sp, x0, #0x800 - b warmboot_main - -.section .text.__set_exception_entry_stack, "ax", %progbits -.type __set_exception_entry_stack, %function -.global __set_exception_entry_stack -__set_exception_entry_stack_pointer: - /* If SPSel == 1 on entry, make sure your function doesn't use stack variables! */ - mov x16, lr - mrs x17, spsel - msr x0, mpidr_el1 - and w0, w0, #3 - bl get_exception_entry_stack_address /* should be optimized so it doesn't make function calls */ - msr spsel, #1 - mov sp, x0 - msr spsel, x17 - mov lr, x16 - ret - -.section .text.__jump_to_lower_el, "ax", %progbits -.global __jump_to_lower_el -.type __jump_to_lower_el, %function -__jump_to_lower_el: - /* x0: arg (context ID), x1: entrypoint, w2: exception level */ - msr elr_el3, x1 - - mov x1, #((0b1111 << 6) | 1) /* DAIF set and SP = SP_ELx*/ - orr x1, w2, lsl#2 - msr spsr_el3, x1 - - bl __set_exception_entry_stack_pointer - - isb - eret diff --git a/exosphere/timers.c b/exosphere/timers.c deleted file mode 100644 index b339a40..0000000 --- a/exosphere/timers.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "timers.h" - -void wait(uint32_t microseconds) { - uint32_t old_time = TIMERUS_CNTR_1US_0; - while (TIMERUS_CNTR_1US_0 - old_time <= microseconds) { - /* Spin-lock. */ - } -} \ No newline at end of file diff --git a/exosphere/timers.h b/exosphere/timers.h deleted file mode 100644 index db9559c..0000000 --- a/exosphere/timers.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef EXOSPHERE_TIMERS_H -#define EXOSPHERE_TIMERS_H - -#include -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 Timers. */ - -#define TIMERS_BASE (mmio_get_device_address(MMIO_DEVID_TMRs_WDTs)) - -#define TIMERUS_CNTR_1US_0 (*((volatile uint32_t *)(TIMERS_BASE + 0x10))) - -void wait(uint32_t microseconds); - -#endif diff --git a/exosphere/titlekey.c b/exosphere/titlekey.c deleted file mode 100644 index 0873040..0000000 --- a/exosphere/titlekey.c +++ /dev/null @@ -1,155 +0,0 @@ -#include -#include - -#include "utils.h" - -#include "titlekey.h" -#include "masterkey.h" -#include "se.h" - -uint64_t g_tkey_expected_label_hash[4]; -unsigned int g_tkey_master_key_rev = MASTERKEY_REVISION_MAX; - -/* Set the expected db prefix. */ -void tkey_set_expected_label_hash(uint64_t *label_hash) { - for (unsigned int i = 0; i < 4; i++) { - g_tkey_expected_label_hash[i] = label_hash[i]; - } -} - -void tkey_set_master_key_rev(unsigned int master_key_rev) { - if (master_key_rev >= MASTERKEY_REVISION_MAX) { - generic_panic(); - } -} - -/* Reference for MGF1 can be found here: https://en.wikipedia.org/wiki/Mask_generation_function#MGF1 */ -void calculate_mgf1_and_xor(void *masked, size_t masked_size, const void *seed, size_t seed_size) { - uint8_t cur_hash[0x20]; - uint8_t hash_buf[0xE4]; - if (seed_size >= 0xE0) { - generic_panic(); - } - - size_t hash_buf_size = seed_size + 4; - memcpy(hash_buf, seed, seed_size); - - uint32_t round = 0; - - uint8_t *p_out = (uint8_t *)masked; - - while (masked_size) { - size_t cur_size = masked_size; - if (cur_size > 0x20) { - cur_size = 0x20; - } - - hash_buf[seed_size + 0] = (uint8_t)((round >> 24) & 0xFF); - hash_buf[seed_size + 1] = (uint8_t)((round >> 16) & 0xFF); - hash_buf[seed_size + 2] = (uint8_t)((round >> 8) & 0xFF); - hash_buf[seed_size + 3] = (uint8_t)((round >> 0) & 0xFF); - round++; - - cache_flush(hash_buf, hash_buf + hash_buf_size); - se_calculate_sha256(cur_hash, hash_buf, hash_buf_size); - - for (unsigned int i = 0; i < cur_size; i++) { - *p_out ^= cur_hash[i]; - p_out++; - } - - masked_size -= cur_size; - } -} - -size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_size) { - if (src_size != 0x100) { - generic_panic(); - } - - /* RSA Wrapped titlekeys use RSA-OAEP. */ - /* Message is of the form prefix || maskedSalt || maskedDB. */ - /* maskedSalt = salt ^ MGF1(maskedDB) */ - /* maskedDB = DB ^ MGF1(salt) */ - /* Salt is random and not validated in any way. */ - /* DB is of the form label_hash || 00....01 || wrapped_titlekey. */ - /* label_hash is, in practice, a constant in es .rodata. */ - /* I have no idea why Nintendo did this, it should be either nonconstant (in tik) or in tz .rodata. */ - - uint8_t *message = (uint8_t *)src; - - /* Prefix should always be zero. */ - if (*message != 0) { - return 0; - } - - - uint8_t *salt = message + 1; - uint8_t *db = message + 0x21; - - /* This will be passed to smc_unwrap_rsa_oaep_wrapped_titlekey. */ - uint8_t *expected_label_hash = (uint8_t *)(&g_tkey_expected_label_hash[0]); - - /* Unmask the salt. */ - calculate_mgf1_and_xor(salt, 0x20, db, 0xDF); - /* Unmask the DB. */ - calculate_mgf1_and_xor(db, 0xDF, salt, 0x20); - - /* Validate expected salt. */ - for (unsigned int i = 0; i < 0x20; i++) { - if (expected_label_hash[i] != db[i]) { - return 0; - } - } - - /* Don't validate salt from message[1:0x21] at all. */ - - /* Advance pointer to DB, since we've validated the salt prefix. */ - db += 0x20; - - /* DB must be of the form 0000...01 || wrapped_titlekey */ - if (*db != 0) { - return 0; - } - - /* Locate wrapped_titlekey inside DB. */ - size_t wrapped_key_offset_in_db = 0; - while (wrapped_key_offset_in_db < 0xBF) { - if (db[wrapped_key_offset_in_db] == 0) { - wrapped_key_offset_in_db++; - } else if (db[wrapped_key_offset_in_db] == 1) { - wrapped_key_offset_in_db++; - break; - } else { - /* Invalid wrapped titlekey prefix. */ - return 0; - } - } - - /* Validate size... */ - size_t wrapped_titlekey_size = 0xBF - wrapped_key_offset_in_db; - if (wrapped_titlekey_size > dst_size || wrapped_titlekey_size == 0) { - return 0; - } - - /* Extract the wrapped key. */ - memcpy(dst, &db[wrapped_key_offset_in_db], wrapped_titlekey_size); - return wrapped_key_offset_in_db; -} - -void tkey_aes_unwrap(void *dst, size_t dst_size, const void *src, size_t src_size) { - if (g_tkey_master_key_rev >= MASTERKEY_REVISION_MAX || dst_size != 0x10 || src_size != 0x10) { - generic_panic(); - } - - const uint8_t titlekek_source[0x10] = { - 0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B - }; - - /* Generate the appropriate titlekek into keyslot 9. */ - unsigned int master_keyslot = mkey_get_keyslot(g_tkey_master_key_rev); - decrypt_data_into_keyslot(KEYSLOT_SWITCH_TEMPKEY, master_keyslot, titlekek_source, 0x10); - - /* Unwrap the titlekey using the titlekek. */ - se_aes_ecb_decrypt_block(KEYSLOT_SWITCH_TEMPKEY, dst, 0x10, src, 0x10); -} diff --git a/exosphere/titlekey.h b/exosphere/titlekey.h deleted file mode 100644 index a74949b..0000000 --- a/exosphere/titlekey.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef EXOSPHERE_TITLEKEY_H -#define EXOSPHERE_TITLEKEY_H - -#include - -void tkey_set_expected_label_hash(uint64_t *label_hash); -void tkey_set_master_key_rev(unsigned int master_key_rev); - -size_t tkey_rsa_oaep_unwrap(void *dst, size_t dst_size, void *src, size_t src_size); - -void tkey_aes_unwrap(void *dst, size_t dst_size, const void *src, size_t src_size); - -#endif \ No newline at end of file diff --git a/exosphere/uart.c b/exosphere/uart.c deleted file mode 100644 index 409d04d..0000000 --- a/exosphere/uart.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "uart.h" - -void uart_initialize(uint16_t divider) { - /* Setup UART in 16450 mode. We assume the relevant UART clock has been enabled. */ - - /* Disable FIFO */ - UART_IIR_FCR_0 = 0x00; - - /* Set DLAB */ - UART_LCR_0 = 0x80; - UART_THR_DLAB_0_0 = (uint8_t)divider; - UART_IER_DLAB_0_0 = (uint8_t)(divider >> 8); - - /* 8N1 mode */ - UART_LCR_0 = 0x03; -} - -void uart_transmit_char(char ch) { - /* Wait for THR to be empty */ - while (!(UART_LSR_0 & 0x20)) {} - - UART_THR_DLAB_0_0 = ch; -} - -void uart_transmit_str(const char *str) { - while (*str) { - uart_transmit_char(*str++); - } -} - -void uart_transmit_hex(uint32_t value) { - for (unsigned int i = 0; i < 8; i++) { - uint32_t nibble = (value >> (28 - i * 4)) & 0xF; - uart_transmit_char("0123456789ABCDEF"[nibble]); - } -} \ No newline at end of file diff --git a/exosphere/uart.h b/exosphere/uart.h deleted file mode 100644 index bba7dcd..0000000 --- a/exosphere/uart.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef EXOSPHERE_UART_H -#define EXOSPHERE_UART_H - -#include -#include "memory_map.h" - -/* Exosphere driver for the Tegra X1 UARTs. */ - -/* TODO: Should we bother with support UARTB-D? */ - -#define UARTA_BASE (mmio_get_device_address(MMIO_DEVID_UART_A)) - -#define UART_THR_DLAB_0_0 (*((volatile uint32_t *)(UARTA_BASE + 0x0))) -#define UART_IER_DLAB_0_0 (*((volatile uint32_t *)(UARTA_BASE + 0x4))) -#define UART_IIR_FCR_0 (*((volatile uint32_t *)(UARTA_BASE+ 0x8))) -#define UART_LCR_0 (*((volatile uint32_t *)(UARTA_BASE + 0xC))) -#define UART_LSR_0 (*((volatile uint32_t *)(UARTA_BASE + 0x14))) - -void uart_initialize(uint16_t divider); -void uart_transmit_char(char ch); -void uart_transmit_str(const char *str); -void uart_transmit_hex(uint32_t value); - -#endif diff --git a/exosphere/userpage.c b/exosphere/userpage.c deleted file mode 100644 index a24d2d6..0000000 --- a/exosphere/userpage.c +++ /dev/null @@ -1,70 +0,0 @@ -#include - -#include "utils.h" -#include "userpage.h" -#include "memory_map.h" -#include "cache.h" - -static uintptr_t g_user_page_user_address = NULL; - -static inline uintptr_t get_page_for_address(void *address) { - return ((uintptr_t)(address)) & ~0xFFF; -} - -/* Create a user page reference for the desired address. */ -/* Returns 1 on success, 0 on failure. */ -bool upage_init(upage_ref_t *upage, void *user_address) { - upage->user_address = get_page_for_address(user_address); - upage->secure_monitor_address = 0ULL; - - if (g_user_page_user_address != NULL) { - /* Different physical address indicate SPL was rebooted, or another process got access to svcCallSecureMonitor. Panic. */ - if (g_user_page_user_address != upage->user_address) { - generic_panic(); - } - upage->secure_monitor_address = USER_PAGE_SECURE_MONITOR_ADDR; - } else { - /* Weakly validate SPL's physically random address is in DRAM. */ - if (upage->user_address >> 31) { - static const userpage_attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_NORMAL; - g_user_page_user_address = upage->user_address; - mmu_map_page(mmu_l3_tbl, USER_PAGE_SECURE_MONITOR_ADDR, upage->user_address, userpage_attributes); - tlb_invalidate_page_inner_shareable((void *)USER_PAGE_SECURE_MONITOR_ADDR); - upage->secure_monitor_address = USER_PAGE_SECURE_MONITOR_ADDR; - } - } - - return upage->secure_monitor_address != 0ULL; -} - -bool user_copy_to_secure(upage_ref_t *upage, void *secure_dst, void *user_src, size_t size) { - /* Fail if the page doesn't match. */ - if (get_page_for_address(user_src) != upage->user_address) { - return false; - } - - /* Fail if we go past the page boundary. */ - if (size != 0 && get_page_for_address(user_src + size - 1) != upage->user_address) { - return false; - } - - void *secure_src = (void *)(upage->secure_monitor_address + ((uintptr_t)user_src - upage->user_address)); - memcpy(secure_dst, secure_src, size); - return true; -} - -bool secure_copy_to_user(upage_ref_t *upage, void *user_dst, void *secure_src, size_t size) { - /* Fail if the page doesn't match. */ - if (get_page_for_address(user_dst) != upage->user_address) { - return false; - } - - /* Fail if we go past the page boundary. */ - if (size != 0 && get_page_for_address(user_dst + size - 1) != upage->user_address) { - return false; - } - - void *secure_dst = (void *)(upage->secure_monitor_address + ((uintptr_t)user_dst - upage->user_address)); - memcpy(secure_dst, secure_src, size); - return true; -} diff --git a/exosphere/userpage.h b/exosphere/userpage.h deleted file mode 100644 index a522514..0000000 --- a/exosphere/userpage.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef EXOSPHERE_USERPAGE_H -#define EXOSPHERE_USERPAGE_H - -#include "utils.h" -#include "memory_map.h" - -#define USER_PAGE_SECURE_MONITOR_ADDR (tzram_get_segment_address(TZRAM_SEGMENT_ID_USERPAGE)) - -typedef struct { - uintptr_t user_address; - uintptr_t secure_monitor_address; -} upage_ref_t; - -bool upage_init(upage_ref_t *user_page, void *user_address); - -bool user_copy_to_secure(upage_ref_t *user_page, void *secure_dst, void *user_src, size_t size); -bool secure_copy_to_user(upage_ref_t *user_page, void *user_dst, void *secure_src, size_t size); - -#endif diff --git a/exosphere/utils.h b/exosphere/utils.h deleted file mode 100644 index d7b471c..0000000 --- a/exosphere/utils.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef EXOSPHERE_UTILS_H -#define EXOSPHERE_UTILS_H - -#include -#include -#include - -#define BIT(x) (1u << (x)) -#define BITL(x) (1ull << (x)) - -void panic(uint32_t code); -void generic_panic(void); - -uint32_t get_physical_address(void *vaddr); - -static inline uint32_t read32le(const void *dword, size_t offset) { - return *(uint32_t *)((uintptr_t)dword + offset); -} - -static inline uint32_t read32be(const unsigned char *dword, size_t offset) { - return __builtin_bswap32(read32le(dword, offset)); -} - -static inline uint64_t read64le(const void *qword, size_t offset) { - return *(uint64_t *)((uintptr_t)dword + offset); -} - -static __attribute__((noinline)) bool check_32bit_additive_overflow(uint32_t a, uint32_t b) { - uint64_t x = (uint64_t)a + (uint64_t)b; - return x > (uint64_t)(UINT32_MAX); -} - -static __attribute__((noinline)) bool overlaps(uint64_t as, uint64_t ae, uint64_t bs, uint64_t be) -{ - if(as <= bs && bs <= ae) - return true; - if(bs <= as && as <= be) - return true; - return false; -} - - -static inline unsigned int get_core_id(void) { - uint64_t core_id; - __asm__ __volatile__ ("mrs %0, MPIDR_EL1" : "=r"(core_id)); - return (unsigned int)core_id & 3; -} - -#endif diff --git a/exosphere/warmboot_init.c b/exosphere/warmboot_init.c deleted file mode 100644 index ae7dcdd..0000000 --- a/exosphere/warmboot_init.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "utils.h" -#include "memory_map.h" - -uintptr_t get_warmboot_crt0_stack_address(void); - -void flush_dcache_all_tzram_pa(void) { - /* TODO */ -} - -void invalidate_icache_all_tzram_pa(void) { - /* TODO */ -} - -uintptr_t get_coldboot_crt0_stack_address(void) { - return tzram_get_segment_pa(TZRAM_SEGMENT_ID_CORE3_STACK) + 0x800; -} diff --git a/exosphere/warmboot_main.c b/exosphere/warmboot_main.c deleted file mode 100644 index 486bfa4..0000000 --- a/exosphere/warmboot_main.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "utils.h" -#include "mmu.h" -#include "memory_map.h" - -extern void __jump_to_lower_el(uint64_t arg, uintptr_t ep, unsigned int el); - -void warmboot_main(void); - -void warmboot_main(void) { - /* TODO */ -}