From c6f06e2c4065c41c509d0d9f43c0b4a8e12509d1 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 24 Jan 2019 08:12:10 -0800 Subject: [PATCH] exosphere: properly implement reboot-to-payload --- exosphere/Makefile | 13 ++- exosphere/rebootstub/Makefile | 154 ++++++++++++++++++++++++++++++ exosphere/rebootstub/linker.ld | 18 ++++ exosphere/rebootstub/linker.specs | 7 ++ exosphere/rebootstub/src/start.s | 45 +++++++++ exosphere/src/configitem.c | 29 +++++- exosphere/src/smc_ams.c | 4 +- exosphere/src/smc_ams.h | 3 + 8 files changed, 262 insertions(+), 11 deletions(-) create mode 100644 exosphere/rebootstub/Makefile create mode 100644 exosphere/rebootstub/linker.ld create mode 100644 exosphere/rebootstub/linker.specs create mode 100644 exosphere/rebootstub/src/start.s diff --git a/exosphere/Makefile b/exosphere/Makefile index cb06f6f5f..727e71791 100644 --- a/exosphere/Makefile +++ b/exosphere/Makefile @@ -76,14 +76,15 @@ export TOPDIR := $(CURDIR) export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ $(TOPDIR)/lp0fw \ - $(TOPDIR)/sc7fw + $(TOPDIR)/sc7fw \ + $(TOPDIR)/rebootstub export DEPSDIR := $(CURDIR)/$(BUILD) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) sc7fw.bin lp0fw.bin +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) sc7fw.bin lp0fw.bin rebootstub.bin #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C @@ -110,7 +111,7 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) -.PHONY: $(BUILD) build_sc7fw build_lp0fw clean all +.PHONY: $(BUILD) build_sc7fw build_lp0fw build_rebootstub clean all #--------------------------------------------------------------------------------- all: $(BUILD) @@ -121,7 +122,10 @@ check_sc7fw: check_lp0fw: @$(MAKE) -C lp0fw all -$(BUILD): check_sc7fw check_lp0fw +check_rebootstub: + @$(MAKE) -C lp0fw all + +$(BUILD): check_sc7fw check_lp0fw check_rebootstub @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile @@ -130,6 +134,7 @@ clean: @echo clean ... @$(MAKE) -C $(TOPDIR)/sc7fw clean @$(MAKE) -C $(TOPDIR)/lp0fw clean + @$(MAKE) -C $(TOPDIR)/rebootstub clean @rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf diff --git a/exosphere/rebootstub/Makefile b/exosphere/rebootstub/Makefile new file mode 100644 index 000000000..0eadd1314 --- /dev/null +++ b/exosphere/rebootstub/Makefile @@ -0,0 +1,154 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/base_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := src +DATA := data +INCLUDES := include + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork + +CFLAGS := \ + -g \ + -O2 \ + -ffunction-sections \ + -fdata-sections \ + -fomit-frame-pointer \ + -fno-inline \ + -std=gnu11 \ + -Werror \ + -Wall \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__BPMP__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).bin + +$(OUTPUT).bin : $(OUTPUT).elf + $(OBJCOPY) -S -O binary $< $@ + @echo built ... $(notdir $@) + +$(OUTPUT).elf : $(OFILES) + +%.elf: $(OFILES) + @echo linking $(notdir $@) + @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + @$(NM) -CSn $@ > $(notdir $*.lst) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/exosphere/rebootstub/linker.ld b/exosphere/rebootstub/linker.ld new file mode 100644 index 000000000..d2e018d4d --- /dev/null +++ b/exosphere/rebootstub/linker.ld @@ -0,0 +1,18 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +ENTRY(_start) +SECTIONS +{ + . = 0x4003F000; + + __start__ = ABSOLUTE(.); + + .text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); } + .rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); } + .bss : ALIGN(8) { __bss_start__ = .; *(.bss* COMMON); . = ALIGN(8); __bss_end__ = .; } + + . = ALIGN(4); + + __end__ = ABSOLUTE(.); +} \ No newline at end of file diff --git a/exosphere/rebootstub/linker.specs b/exosphere/rebootstub/linker.specs new file mode 100644 index 000000000..300990418 --- /dev/null +++ b/exosphere/rebootstub/linker.specs @@ -0,0 +1,7 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /linker.ld) --nmagic --gc-sections + +*startfile: +crti%O%s crtbegin%O%s diff --git a/exosphere/rebootstub/src/start.s b/exosphere/rebootstub/src/start.s new file mode 100644 index 000000000..155ab382a --- /dev/null +++ b/exosphere/rebootstub/src/start.s @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +.section .text.start +.align 4 +.global _start +_start: + b crt0 + +.global crt0 +.type crt0, %function +crt0: + @ clear all registers + ldr r0, =0x52425430 @ RBT0 + mov r1, #0x0 + mov r2, #0x0 + mov r3, #0x0 + mov r4, #0x0 + mov r5, #0x0 + mov r6, #0x0 + mov r7, #0x0 + mov r8, #0x0 + mov r9, #0x0 + mov r10, #0x0 + mov r11, #0x0 + mov r12, #0x0 + mov lr, #0x0 + ldr sp, =0x40010000 + ldr pc, =0x40010000 + + + diff --git a/exosphere/src/configitem.c b/exosphere/src/configitem.c index eb080ede2..e332311c8 100644 --- a/exosphere/src/configitem.c +++ b/exosphere/src/configitem.c @@ -26,6 +26,14 @@ #include "utils.h" #include "masterkey.h" #include "exocfg.h" +#include "smc_ams.h" +#include "arm.h" + +#define u8 uint8_t +#define u32 uint32_t +#include "rebootstub_bin.h" +#undef u8 +#undef u32 static bool g_battery_profile = false; static bool g_debugmode_override_user = false, g_debugmode_override_priv = false; @@ -48,11 +56,22 @@ uint32_t configitem_set(bool privileged, ConfigItem item, uint64_t value) { case REBOOT_KIND_TO_WB_PAYLOAD: /* Set reboot kind = warmboot. */ MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x450ull) = 0x1; - /* Patch bootrom to jump to payload. */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x630ull) = 0x0010171B; /* Return to bootrom IRAM initialization func. */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x634ull) = 0x4000FFA4; /* Overwrite bootrom return address on stack. */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x638ull) = 0x40010000; /* Return to start of payload. */ - MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x63Cull) = 0x4000FFB4; /* Overwrite bootrom return address on stack. */ + /* Patch SDRAM init to perform an SVC immediately after second write */ + MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x634ull) = 0x2E38DFFF; + MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x638ull) = 0x6001DC28; + /* Set SVC handler to jump to reboot stub in IRAM. */ + MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x520ull) = 0x4003F000; + MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_RTC_PMC) + 0x53Cull) = 0x6000F208; + + /* Copy reboot stub payload. */ + ams_map_irampage(0x4003F000); + for (unsigned int i = 0; i < rebootstub_bin_size; i += 4) { + MAKE_REG32(MMIO_GET_DEVICE_ADDRESS(MMIO_DEVID_AMS_IRAM_PAGE) + i) = read32le(rebootstub_bin, i); + } + ams_unmap_irampage(); + + /* Ensure stub is flushed. */ + flush_dcache_all(); break; default: return 2; diff --git a/exosphere/src/smc_ams.c b/exosphere/src/smc_ams.c index 08e220f3d..5c58cbda3 100644 --- a/exosphere/src/smc_ams.c +++ b/exosphere/src/smc_ams.c @@ -72,7 +72,7 @@ static void ams_unmap_userpage(void) { lock_release(&g_ams_userpage_mapped); } -static void ams_map_irampage(uintptr_t iram_address) { +void ams_map_irampage(uintptr_t iram_address) { lock_acquire(&g_ams_iram_page_mapped); static const uint64_t irampage_attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | ATTRIB_MEMTYPE_DEVICE; uintptr_t *mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); @@ -80,7 +80,7 @@ static void ams_map_irampage(uintptr_t iram_address) { tlb_invalidate_page_inner_shareable((void *)AMS_IRAM_PAGE_SECURE_MONITOR_ADDR); } -static void ams_unmap_irampage(void) { +void ams_unmap_irampage(void) { uintptr_t *mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE); mmu_unmap_page(mmu_l3_tbl, AMS_IRAM_PAGE_SECURE_MONITOR_ADDR); tlb_invalidate_page_inner_shareable((void *)AMS_IRAM_PAGE_SECURE_MONITOR_ADDR); diff --git a/exosphere/src/smc_ams.h b/exosphere/src/smc_ams.h index ed1cee156..8b74d3bd5 100644 --- a/exosphere/src/smc_ams.h +++ b/exosphere/src/smc_ams.h @@ -21,4 +21,7 @@ uint32_t ams_iram_copy(smc_args_t *args); +void ams_map_irampage(uintptr_t iram_address); +void ams_unmap_irampage(void); + #endif \ No newline at end of file