From bd9152215f90dae30caa1e8403c07bf690742412 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Wed, 17 Jul 2019 01:49:47 +0200 Subject: [PATCH] thermosphere: "write" placeholder code --- thermosphere/Makefile | 163 +++++ thermosphere/linker.ld | 190 ++++++ thermosphere/linker.specs | 4 + thermosphere/src/car.c | 82 +++ thermosphere/src/car.h | 49 ++ thermosphere/src/exceptions.s | 187 ++++++ thermosphere/src/misc.h | 36 ++ thermosphere/src/my_libc.c | 1141 +++++++++++++++++++++++++++++++++ thermosphere/src/start.s | 48 ++ thermosphere/src/timers.c | 25 + thermosphere/src/timers.h | 43 ++ thermosphere/src/types.h | 25 + thermosphere/src/uart.c | 81 +++ thermosphere/src/uart.h | 167 +++++ thermosphere/src/utils.c | 27 + thermosphere/src/utils.h | 107 ++++ 16 files changed, 2375 insertions(+) create mode 100644 thermosphere/Makefile create mode 100644 thermosphere/linker.ld create mode 100644 thermosphere/linker.specs create mode 100644 thermosphere/src/car.c create mode 100644 thermosphere/src/car.h create mode 100644 thermosphere/src/exceptions.s create mode 100644 thermosphere/src/misc.h create mode 100644 thermosphere/src/my_libc.c create mode 100644 thermosphere/src/start.s create mode 100644 thermosphere/src/timers.c create mode 100644 thermosphere/src/timers.h create mode 100644 thermosphere/src/types.h create mode 100644 thermosphere/src/uart.c create mode 100644 thermosphere/src/uart.h create mode 100644 thermosphere/src/utils.c create mode 100644 thermosphere/src/utils.h diff --git a/thermosphere/Makefile b/thermosphere/Makefile new file mode 100644 index 000000000..16cff99e3 --- /dev/null +++ b/thermosphere/Makefile @@ -0,0 +1,163 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/devkitA64/base_rules + +AMSBRANCH := $(shell git symbolic-ref --short HEAD) +AMSHASH = $(shell git rev-parse --short=16 HEAD) +AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD) + +ifneq (, $(strip $(shell git status --porcelain 2>/dev/null))) + AMSREV := $(AMSREV)-dirty +endif + +#--------------------------------------------------------------------------------- +# 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 ../common/include + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a -mtune=cortex-a57 -mgeneral-regs-only #<- important +DEFINES := -D__CCPLEX__ -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\" -DATMOSPHERE_RELEASE_VERSION_HASH="0x$(AMSHASH)" +CFLAGS := \ + -g \ + -Os \ + -ffunction-sections \ + -fdata-sections \ + -fomit-frame-pointer \ + -fno-asynchronous-unwind-tables \ + -fno-unwind-tables \ + -std=gnu11 \ + -Werror \ + -Wall \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(TOPDIR)/linker.specs -nostartfiles -nostdlib -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: + @echo linking $(notdir $@) + $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + @$(NM) -CSn $@ > $(notdir $*.lst) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h: %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/thermosphere/linker.ld b/thermosphere/linker.ld new file mode 100644 index 000000000..c007c3100 --- /dev/null +++ b/thermosphere/linker.ld @@ -0,0 +1,190 @@ +OUTPUT_ARCH(aarch64) +ENTRY(_start) + +MEMORY +{ + NULL : ORIGIN = 0, LENGTH = 0x1000 + main : ORIGIN = 0x80000000, LENGTH = 0xD000 - 0x1000 /* 0x1000 for stacks. */ +} + +SECTIONS +{ + PROVIDE(__start__ = 0x80000000); + . = __start__; + + .text : + { + . = ALIGN(8); + __main_start__ = ABSOLUTE(.); + *(.text.start*) + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + . = ALIGN(0x800); + __vectors_start__ = ABSOLUTE(.); + *(.vectors*); + . = ALIGN(8); + } >main + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } >main + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } >main + + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } >main + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + SORT(CONSTRUCTORS) + . = ALIGN(8); + } >main + + .got : { __got_start__ = ABSOLUTE(.); *(.got) *(.igot) } >main + .got.plt : { *(.got.plt) *(.igot.plt) __got_end__ = ABSOLUTE(.);} >main + + .preinit_array : + { + . = ALIGN(8); + PROVIDE (__preinit_array_start = ABSOLUTE(.)); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = ABSOLUTE(.)); + . = ALIGN(8); + } >main + + .init_array : + { + PROVIDE (__init_array_start = ABSOLUTE(.)); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = ABSOLUTE(.)); + } >main + + .fini_array : + { + . = ALIGN(8); + PROVIDE (__fini_array_start = ABSOLUTE(.)); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = ABSOLUTE(.)); + . = ALIGN(8); + } >main + + .ctors : + { + . = ALIGN(8); + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + . = ALIGN(8); + } >main + + .dtors ALIGN(8) : + { + . = ALIGN(8); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + . = ALIGN(8); + } >main + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + CONSTRUCTORS + . = ALIGN(8); + } >main + + + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } >main + .eh_frame : { KEEP (*(.eh_frame)) *(.eh_frame.*) } >main + .gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) } >main + .gnu_extab : { *(.gnu_extab*) } >main + .exception_ranges : { *(.exception_ranges .exception_ranges*) } >main + + .dynamic : { *(.dynamic) } >main + .interp : { *(.interp) } >main + .note.gnu.build-id : { *(.note.gnu.build-id) } >main + .hash : { *(.hash) } >main + .gnu.hash : { *(.gnu.hash) } >main + .gnu.version : { *(.gnu.version) } >main + .gnu.version_d : { *(.gnu.version_d) } >main + .gnu.version_r : { *(.gnu.version_r) } >main + .dynsym : { *(.dynsym) } >main + .dynstr : { *(.dynstr) } >main + .rela.dyn : { *(.rela.*); __main_end__ = ABSOLUTE(.);} >main + + .bss : + { + . = ALIGN(8); + __bss_start__ = ABSOLUTE(.); + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(8); + __end__ = ABSOLUTE(.); + } >main + + __end__ = ABSOLUTE(.) ; + + __stacks_top__ = ABSOLUTE(. + 0x1000); + + . = ALIGN(8); + + /* ================== + ==== Metadata ==== + ================== */ + + /* Discard sections that difficult post-processing */ + /DISCARD/ : { *(.group .comment .note) } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } +} diff --git a/thermosphere/linker.specs b/thermosphere/linker.specs new file mode 100644 index 000000000..fb0cb34bc --- /dev/null +++ b/thermosphere/linker.specs @@ -0,0 +1,4 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /linker.ld) --nmagic --gc-sections diff --git a/thermosphere/src/car.c b/thermosphere/src/car.c new file mode 100644 index 000000000..c7f4b75e1 --- /dev/null +++ b/thermosphere/src/car.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "utils.h" +#include "car.h" +#include "timers.h" + +static inline u32 get_special_clk_reg(CarDevice dev) { + switch (dev) { + case CARDEVICE_UARTA: return 0x178; + case CARDEVICE_UARTB: return 0x17C; + case CARDEVICE_I2C1: return 0x124; + case CARDEVICE_I2C5: return 0x128; + case CARDEVICE_ACTMON: return 0x3E8; + case CARDEVICE_BPMP: return 0; + default: return -1;// generic_panic(); + } +} + +static inline u32 get_special_clk_val(CarDevice dev) { + switch (dev) { + case CARDEVICE_UARTA: return 0; + case CARDEVICE_UARTB: return 0; + case CARDEVICE_I2C1: return (6 << 29); + case CARDEVICE_I2C5: return (6 << 29); + case CARDEVICE_ACTMON: return (6 << 29); + case CARDEVICE_BPMP: return 0; + default: return -1; //generic_panic(); + } +} + +static u32 g_clk_reg_offsets[NUM_CAR_BANKS] = {0x010, 0x014, 0x018, 0x360, 0x364, 0x280, 0x298}; +static u32 g_rst_reg_offsets[NUM_CAR_BANKS] = {0x004, 0x008, 0x00C, 0x358, 0x35C, 0x28C, 0x2A4}; + +void clk_enable(CarDevice dev) { + u32 special_reg; + if ((special_reg = get_special_clk_reg(dev))) { + MAKE_CAR_REG(special_reg) = get_special_clk_val(dev); + } + MAKE_CAR_REG(g_clk_reg_offsets[dev >> 5]) |= BIT(dev & 0x1F); +} + +void clk_disable(CarDevice dev) { + MAKE_CAR_REG(g_clk_reg_offsets[dev >> 5]) &= ~(BIT(dev & 0x1F)); +} + +void rst_enable(CarDevice dev) { + MAKE_CAR_REG(g_rst_reg_offsets[dev >> 5]) |= BIT(dev & 0x1F); +} + +void rst_disable(CarDevice dev) { + MAKE_CAR_REG(g_rst_reg_offsets[dev >> 5]) &= ~(BIT(dev & 0x1F)); +} + + +void clkrst_enable(CarDevice dev) { + clk_enable(dev); + rst_disable(dev); +} + +void clkrst_disable(CarDevice dev) { + rst_enable(dev); + clk_disable(dev); +} + +void clkrst_reboot(CarDevice dev) { + clkrst_disable(dev); + clkrst_enable(dev); +} diff --git a/thermosphere/src/car.h b/thermosphere/src/car.h new file mode 100644 index 000000000..28f0c450c --- /dev/null +++ b/thermosphere/src/car.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "utils.h" + +#define CAR_BASE 0x60006000ull + +#define MAKE_CAR_REG(n) MAKE_REG32(CAR_BASE + n) + +#define CLK_RST_CONTROLLER_MISC_CLK_ENB_0 MAKE_CAR_REG(0x048) +#define CLK_RST_CONTROLLER_RST_DEVICES_H_0 MAKE_CAR_REG(0x008) +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD_0 MAKE_CAR_REG(0x3A4) +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET_0 MAKE_CAR_REG(0x450) +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR_0 MAKE_CAR_REG(0x454) + +#define NUM_CAR_BANKS 7 + +typedef enum { + CARDEVICE_UARTA = 6, + CARDEVICE_UARTB = 7, + CARDEVICE_I2C1 = 12, + CARDEVICE_I2C5 = 47, + CARDEVICE_ACTMON = 119, + CARDEVICE_BPMP = 1 +} CarDevice; + +void clk_enable(CarDevice dev); +void clk_disable(CarDevice dev); +void rst_enable(CarDevice dev); +void rst_disable(CarDevice dev); + +void clkrst_enable(CarDevice dev); +void clkrst_disable(CarDevice dev); + +void clkrst_reboot(CarDevice dev); diff --git a/thermosphere/src/exceptions.s b/thermosphere/src/exceptions.s new file mode 100644 index 000000000..f7dcda342 --- /dev/null +++ b/thermosphere/src/exceptions.s @@ -0,0 +1,187 @@ +/* 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 + +.macro save_all_regs + sub sp, sp, #0x110 + stp x0, x1, [sp, #0x00] + stp x2, x3, [sp, #0x10] + stp x4, x5, [sp, #0x20] + stp x6, x7, [sp, #0x30] + stp x8, x9, [sp, #0x40] + stp x10, x11, [sp, #0x50] + stp x12, x13, [sp, #0x60] + stp x14, x15, [sp, #0x70] + stp x16, x17, [sp, #0x80] + stp x18, x19, [sp, #0x90] + stp x20, x21, [sp, #0xA0] + stp x22, x23, [sp, #0xB0] + stp x24, x25, [sp, #0xC0] + stp x26, x27, [sp, #0xD0] + stp x28, x29, [sp, #0xE0] + + mrs x20, sp_el1 + mrs x21, elr_el2 + mrs x22, spsr_el2 + + stp x30, x20, [sp, #0xF0] + stp x21, x22, [sp, #0x100] +.endm + +/* Actual Vectors for Exosphere. */ +.global exosphere_vectors +vector_base exosphere_vectors + +/* Current EL, SP0 */ +.global unknown_exception +unknown_exception: +vector_entry synch_sp0 + b . + 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 + save_all_regs + + mov x0, sp + mrs x1, esr_el2 + + bl . // FIXME! + + b _restore_all_regs + check_vector_size synch_a64 + +vector_entry irq_a64 + b unknown_exception + check_vector_size irq_a64 + +vector_entry fiq_a64 + b unknown_exception + check_vector_size fiq_a64 + +vector_entry serror_a64 + b unknown_exception + check_vector_size serror_a64 + + +/* 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. */ +_restore_all_regs: + ldp x30, x20, [sp, #0xF0] + ldp x21, x22, [sp, #0x100] + + msr sp_el1, x20 + msr elr_el2, x21 + msr spsr_el2, x22 + + ldp x0, x1, [sp, #0x00] + ldp x2, x3, [sp, #0x10] + ldp x4, x5, [sp, #0x20] + ldp x6, x7, [sp, #0x30] + ldp x8, x9, [sp, #0x40] + ldp x10, x11, [sp, #0x50] + ldp x12, x13, [sp, #0x60] + ldp x14, x15, [sp, #0x70] + ldp x16, x17, [sp, #0x80] + ldp x18, x19, [sp, #0x90] + ldp x20, x21, [sp, #0xA0] + ldp x22, x23, [sp, #0xB0] + ldp x24, x25, [sp, #0xC0] + ldp x26, x27, [sp, #0xD0] + ldp x28, x29, [sp, #0xE0] + + add sp, sp, #0x110 + eret + +vector_entry serror_a32 + b unknown_exception + check_vector_size serror_a32 diff --git a/thermosphere/src/misc.h b/thermosphere/src/misc.h new file mode 100644 index 000000000..5e5b463c3 --- /dev/null +++ b/thermosphere/src/misc.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "utils.h" + +#define MISC_BASE 0x70000000ull + +#define MAKE_MISC_REG(n) MAKE_REG32(MISC_BASE + n) + + +#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 MAKE_MISC_REG(0x0C00) +#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0 MAKE_MISC_REG(0x0C04) +#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG2_0 MAKE_MISC_REG(0x0C08) + +#define PINMUX_AUX_GEN1_I2C_SCL_0 MAKE_MISC_REG(0x30BC) +#define PINMUX_AUX_GEN1_I2C_SDA_0 MAKE_MISC_REG(0x30C0) + +#define PINMUX_AUX_UARTn_TX_0(n) MAKE_MISC_REG(0x30E4 + 0x10 * (n)) +#define PINMUX_AUX_UARTn_RX_0(n) MAKE_MISC_REG(0x30E8 + 0x10 * (n)) +#define PINMUX_AUX_UARTn_RTS_0(n) MAKE_MISC_REG(0x30EC + 0x10 * (n)) +#define PINMUX_AUX_UARTn_CTS_0(n) MAKE_MISC_REG(0x30F0 + 0x10 * (n)) diff --git a/thermosphere/src/my_libc.c b/thermosphere/src/my_libc.c new file mode 100644 index 000000000..2117f2483 --- /dev/null +++ b/thermosphere/src/my_libc.c @@ -0,0 +1,1141 @@ +/* Note: copied from newlib */ +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* + * Copyright (C) 2004 CodeSourcery, LLC + * + * Permission to use, copy, modify, and distribute this file + * for any purpose is hereby granted without fee, provided that + * the above copyright notice and this notice appears in all + * copies. + * + * This file is distributed WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* Handle ELF .{pre_init,init,fini}_array sections. */ +#include + +#ifndef HAVE_INITFINI_ARRAY +#define HAVE_INITFINI_ARRAY +#endif + +#undef HAVE_INIT_FINI + +#ifdef HAVE_INITFINI_ARRAY + +/* These magic symbols are provided by the linker. */ +extern void (*__preinit_array_start []) (void) __attribute__((weak)); +extern void (*__preinit_array_end []) (void) __attribute__((weak)); +extern void (*__init_array_start []) (void) __attribute__((weak)); +extern void (*__init_array_end []) (void) __attribute__((weak)); + +#ifdef HAVE_INIT_FINI +extern void _init (void); +#endif + +/* Iterate over all the init routines. */ +void +__libc_init_array (void) +{ + size_t count; + size_t i; + + count = __preinit_array_end - __preinit_array_start; + for (i = 0; i < count; i++) + __preinit_array_start[i] (); + +#ifdef HAVE_INIT_FINI + _init (); +#endif + + count = __init_array_end - __init_array_start; + for (i = 0; i < count; i++) + __init_array_start[i] (); +} +#endif + +#ifdef HAVE_INITFINI_ARRAY +extern void (*__fini_array_start []) (void) __attribute__((weak)); +extern void (*__fini_array_end []) (void) __attribute__((weak)); + +#ifdef HAVE_INIT_FINI +extern void _fini (void); +#endif + +/* Run all the cleanup routines. */ +void +__libc_fini_array (void) +{ + size_t count; + size_t i; + + count = __fini_array_end - __fini_array_start; + for (i = count; i > 0; i--) + __fini_array_start[i-1] (); + +#ifdef HAVE_INIT_FINI + _fini (); +#endif +} +#endif + +/* +FUNCTION + <>---move possibly overlapping memory +INDEX + memmove +SYNOPSIS + #include + void *memmove(void *<[dst]>, const void *<[src]>, size_t <[length]>); +DESCRIPTION + This function moves <[length]> characters from the block of + memory starting at <<*<[src]>>> to the memory starting at + <<*<[dst]>>>. <> reproduces the characters correctly + at <<*<[dst]>>> even if the two areas overlap. +RETURNS + The function returns <[dst]> as passed. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + memmove ansi pure +*/ + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the 4X unrolled loop. */ +#define BIGBLOCKSIZE (sizeof (long) << 2) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LITTLEBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#undef TOO_SMALL +#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) + +/*SUPPRESS 20*/ +void * +//__inhibit_loop_to_libcall +memmove (void *dst_void, + const void *src_void, + size_t length) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dst = dst_void; + const char *src = src_void; + + if (src < dst && dst < src + length) + { + /* Have to copy backwards */ + src += length; + dst += length; + while (length--) + { + *--dst = *--src; + } + } + else + { + while (length--) + { + *dst++ = *src++; + } + } + + return dst_void; +#else + char *dst = dst_void; + const char *src = src_void; + long *aligned_dst; + const long *aligned_src; + + if (src < dst && dst < src + length) + { + /* Destructive overlap...have to copy backwards */ + src += length; + dst += length; + while (length--) + { + *--dst = *--src; + } + } + else + { + /* Use optimizing algorithm for a non-destructive copy to closely + match memcpy. If the size is small or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(length) && !UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* Copy 4X long words at a time if possible. */ + while (length >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + length -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (length >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + length -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (length--) + { + *dst++ = *src++; + } + } + + return dst_void; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---copy memory regions +SYNOPSIS + #include + void* memcpy(void *restrict <[out]>, const void *restrict <[in]>, + size_t <[n]>); +DESCRIPTION + This function copies <[n]> bytes from the memory region + pointed to by <[in]> to the memory region pointed to by + <[out]>. + If the regions overlap, the behavior is undefined. +RETURNS + <> returns a pointer to the first byte of the <[out]> + region. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + memcpy ansi pure + */ + +void * +memcpy (void * dst0, + const void * __restrict src0, + size_t len0) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dst = (char *) dst0; + char *src = (char *) src0; + + void *save = dst0; + + while (len0--) + { + *dst++ = *src++; + } + + return save; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If the size is small, or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(len0) && !UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* Copy 4X long words at a time if possible. */ + while (len0 >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + len0 -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (len0 >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + len0 -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (len0--) + *dst++ = *src++; + + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---set an area of memory +INDEX + memset +SYNOPSIS + #include + void *memset(void *<[dst]>, int <[c]>, size_t <[length]>); +DESCRIPTION + This function converts the argument <[c]> into an unsigned + char and fills the first <[length]> characters of the array + pointed to by <[dst]> to the value. +RETURNS + <> returns the value of <[dst]>. +PORTABILITY +<> is ANSI C. + <> requires no supporting OS subroutines. +QUICKREF + memset ansi pure +*/ + +#include + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define LBLOCKSIZE (sizeof(long)) +#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +void * +memset (void *m, + int c, + size_t n) +{ + char *s = (char *) m; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned int i; + unsigned long buffer; + unsigned long *aligned_addr; + unsigned int d = c & 0xff; /* To avoid sign extension, copy C to an + unsigned variable. */ + + while (UNALIGNED (s)) + { + if (n--) + *s++ = (char) c; + else + return m; + } + + if (!TOO_SMALL (n)) + { + /* If we get this far, we know that n is large and s is word-aligned. */ + aligned_addr = (unsigned long *) s; + + /* Store D into each char sized location in BUFFER so that + we can set large blocks quickly. */ + buffer = (d << 8) | d; + buffer |= (buffer << 16); + for (i = 32; i < LBLOCKSIZE * 8; i <<= 1) + buffer = (buffer << i) | buffer; + + /* Unroll the loop. */ + while (n >= LBLOCKSIZE*4) + { + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + n -= 4*LBLOCKSIZE; + } + + while (n >= LBLOCKSIZE) + { + *aligned_addr++ = buffer; + n -= LBLOCKSIZE; + } + /* Pick up the remainder with a bytewise loop. */ + s = (char*)aligned_addr; + } + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (n--) + *s++ = (char) c; + + return m; +} + +/* +FUNCTION + <>---find character in memory +INDEX + memchr +SYNOPSIS + #include + void *memchr(const void *<[src]>, int <[c]>, size_t <[length]>); +DESCRIPTION + This function searches memory starting at <<*<[src]>>> for the + character <[c]>. The search only ends with the first + occurrence of <[c]>, or after <[length]> characters; in + particular, <> does not terminate the search. +RETURNS + If the character <[c]> is found within <[length]> characters + of <<*<[src]>>>, a pointer to the character is returned. If + <[c]> is not found, then <> is returned. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + memchr ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X) ((long)X & (sizeof (long) - 1)) + +/* How many bytes are loaded each iteration of the word copy loop. */ +#define LBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the bytewise iterator. */ +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +#if LONG_MAX == 2147483647L +#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080) +#else +#if LONG_MAX == 9223372036854775807L +/* Nonzero if X (a long int) contains a NULL byte. */ +#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080) +#else +#error long int is not a 32bit or 64bit type. +#endif +#endif + +#ifndef DETECTNULL +#error long int is not a 32bit or 64bit byte +#endif + +/* DETECTCHAR returns nonzero if (long)X contains the byte used + to fill (long)MASK. */ +#define DETECTCHAR(X,MASK) (DETECTNULL(X ^ MASK)) + +void * +memchr (const void *src_void, + int c, + size_t length) +{ + const unsigned char *src = (const unsigned char *) src_void; + unsigned char d = c; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned long *asrc; + unsigned long mask; + unsigned int i; + + while (UNALIGNED (src)) + { + if (!length--) + return NULL; + if (*src == d) + return (void *) src; + src++; + } + + if (!TOO_SMALL (length)) + { + /* If we get this far, we know that length is large and src is + word-aligned. */ + /* The fast code reads the source one word at a time and only + performs the bytewise search on word-sized segments if they + contain the search character, which is detected by XORing + the word-sized segment with a word-sized block of the search + character and then detecting for the presence of NUL in the + result. */ + asrc = (unsigned long *) src; + mask = d << 8 | d; + mask = mask << 16 | mask; + for (i = 32; i < LBLOCKSIZE * 8; i <<= 1) + mask = (mask << i) | mask; + + while (length >= LBLOCKSIZE) + { + if (DETECTCHAR (*asrc, mask)) + break; + length -= LBLOCKSIZE; + asrc++; + } + + /* If there are fewer than LBLOCKSIZE characters left, + then we resort to the bytewise loop. */ + + src = (unsigned char *) asrc; + } + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (length--) + { + if (*src == d) + return (void *) src; + src++; + } + + return NULL; +} + +/* +FUNCTION + <>---compare two memory areas +INDEX + memcmp +SYNOPSIS + #include + int memcmp(const void *<[s1]>, const void *<[s2]>, size_t <[n]>); +DESCRIPTION + This function compares not more than <[n]> characters of the + object pointed to by <[s1]> with the object pointed to by <[s2]>. +RETURNS + The function returns an integer greater than, equal to or + less than zero according to whether the object pointed to by + <[s1]> is greater than, equal to or less than the object + pointed to by <[s2]>. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + memcmp ansi pure +*/ + + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + +int +memcmp (const void *m1, + const void *m2, + size_t n) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + unsigned char *s1 = (unsigned char *) m1; + unsigned char *s2 = (unsigned char *) m2; + + while (n--) + { + if (*s1 != *s2) + { + return *s1 - *s2; + } + s1++; + s2++; + } + return 0; +#else + unsigned char *s1 = (unsigned char *) m1; + unsigned char *s2 = (unsigned char *) m2; + unsigned long *a1; + unsigned long *a2; + + /* If the size is too small, or either pointer is unaligned, + then we punt to the byte compare loop. Hopefully this will + not turn up in inner loops. */ + if (!TOO_SMALL(n) && !UNALIGNED(s1,s2)) + { + /* Otherwise, load and compare the blocks of memory one + word at a time. */ + a1 = (unsigned long*) s1; + a2 = (unsigned long*) s2; + while (n >= LBLOCKSIZE) + { + if (*a1 != *a2) + break; + a1++; + a2++; + n -= LBLOCKSIZE; + } + + /* check m mod LBLOCKSIZE remaining characters */ + + s1 = (unsigned char*)a1; + s2 = (unsigned char*)a2; + } + + while (n--) + { + if (*s1 != *s2) + return *s1 - *s2; + s1++; + s2++; + } + + return 0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---search for character in string +INDEX + strchr +SYNOPSIS + #include + char * strchr(const char *<[string]>, int <[c]>); +DESCRIPTION + This function finds the first occurence of <[c]> (converted to + a char) in the string pointed to by <[string]> (including the + terminating null character). +RETURNS + Returns a pointer to the located character, or a null pointer + if <[c]> does not occur in <[string]>. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strchr ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + + +/* Nonzero if X is not aligned on a "long" boundary. */ +#define UNALIGNED(X) ((long)X & (sizeof (long) - 1)) + +/* How many bytes are loaded each iteration of the word copy loop. */ +#define LBLOCKSIZE (sizeof (long)) + +char * +strchr (const char *s1, + int i) +{ + const unsigned char *s = (const unsigned char *)s1; + unsigned char c = i; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned long mask,j; + unsigned long *aligned_addr; + + /* Special case for finding 0. */ + if (!c) + { + while (UNALIGNED (s)) + { + if (!*s) + return (char *) s; + s++; + } + /* Operate a word at a time. */ + aligned_addr = (unsigned long *) s; + while (!DETECTNULL (*aligned_addr)) + aligned_addr++; + /* Found the end of string. */ + s = (const unsigned char *) aligned_addr; + while (*s) + s++; + return (char *) s; + } + + /* All other bytes. Align the pointer, then search a long at a time. */ + while (UNALIGNED (s)) + { + if (!*s) + return NULL; + if (*s == c) + return (char *) s; + s++; + } + + mask = c; + for (j = 8; j < LBLOCKSIZE * 8; j <<= 1) + mask = (mask << j) | mask; + + aligned_addr = (unsigned long *) s; + while (!DETECTNULL (*aligned_addr) && !DETECTCHAR (*aligned_addr, mask)) + aligned_addr++; + + /* The block of bytes currently pointed to by aligned_addr + contains either a null or the target char, or both. We + catch it using the bytewise search. */ + + s = (unsigned char *) aligned_addr; + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (*s && *s != c) + s++; + if (*s == c) + return (char *)s; + return NULL; +} + +/* +FUNCTION + <>---character string compare + +INDEX + strcmp +SYNOPSIS + #include + int strcmp(const char *<[a]>, const char *<[b]>); +DESCRIPTION + <> compares the string at <[a]> to + the string at <[b]>. +RETURNS + If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>, + <> returns a number greater than zero. If the two + strings match, <> returns zero. If <<*<[a]>>> + sorts lexicographically before <<*<[b]>>>, <> returns a + number less than zero. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strcmp ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +int +strcmp (const char *s1, + const char *s2) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + while (*s1 != '\0' && *s1 == *s2) + { + s1++; + s2++; + } + + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#else + unsigned long *a1; + unsigned long *a2; + + /* If s1 or s2 are unaligned, then compare bytes. */ + if (!UNALIGNED (s1, s2)) + { + /* If s1 and s2 are word-aligned, compare them a word at a time. */ + a1 = (unsigned long*)s1; + a2 = (unsigned long*)s2; + while (*a1 == *a2) + { + /* To get here, *a1 == *a2, thus if we find a null in *a1, + then the strings must be equal, so return zero. */ + if (DETECTNULL (*a1)) + return 0; + + a1++; + a2++; + } + + /* A difference was detected in last few bytes of s1, so search bytewise */ + s1 = (char*)a1; + s2 = (char*)a2; + } + + while (*s1 != '\0' && *s1 == *s2) + { + s1++; + s2++; + } + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---copy string +INDEX + strcpy +SYNOPSIS + #include + char *strcpy(char *<[dst]>, const char *<[src]>); +DESCRIPTION + <> copies the string pointed to by <[src]> + (including the terminating null character) to the array + pointed to by <[dst]>. +RETURNS + This function returns the initial value of <[dst]>. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strcpy ansi pure +*/ + +/*SUPPRESS 560*/ +/*SUPPRESS 530*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +char* +strcpy (char *dst0, + const char *src0) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *s = dst0; + + while ((*dst0++ = *src0++)) + ; + + return s; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If SRC or DEST is unaligned, then copy bytes. */ + if (!UNALIGNED (src, dst)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* SRC and DEST are both "long int" aligned, try to do "long int" + sized copies. */ + while (!DETECTNULL(*aligned_src)) + { + *aligned_dst++ = *aligned_src++; + } + + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while ((*dst++ = *src++)) + ; + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---character string length +INDEX + strlen +SYNOPSIS + #include + size_t strlen(const char *<[str]>); +DESCRIPTION + The <> function works out the length of the string + starting at <<*<[str]>>> by counting chararacters until it + reaches a <> character. +RETURNS + <> returns the character count. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strlen ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define LBLOCKSIZE (sizeof (long)) +#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1)) +size_t +strlen (const char *str) +{ + const char *start = str; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + unsigned long *aligned_addr; + + /* Align the pointer, so we can search a word at a time. */ + while (UNALIGNED (str)) + { + if (!*str) + return str - start; + str++; + } + + /* If the string is word-aligned, we can check for the presence of + a null in each word-sized block. */ + aligned_addr = (unsigned long *)str; + while (!DETECTNULL (*aligned_addr)) + aligned_addr++; + + /* Once a null is detected, we check each byte in that block for a + precise position of the null. */ + str = (char *) aligned_addr; + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (*str) + str++; + return str - start; +} + +/* +FUNCTION + <>---character string compare + +INDEX + strncmp +SYNOPSIS + #include + int strncmp(const char *<[a]>, const char * <[b]>, size_t <[length]>); +DESCRIPTION + <> compares up to <[length]> characters + from the string at <[a]> to the string at <[b]>. +RETURNS + If <<*<[a]>>> sorts lexicographically after <<*<[b]>>>, + <> returns a number greater than zero. If the two + strings are equivalent, <> returns zero. If <<*<[a]>>> + sorts lexicographically before <<*<[b]>>>, <> returns a + number less than zero. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strncmp ansi pure +*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +int +strncmp (const char *s1, + const char *s2, + size_t n) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + if (n == 0) + return 0; + + while (n-- != 0 && *s1 == *s2) + { + if (n == 0 || *s1 == '\0') + break; + s1++; + s2++; + } + + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#else + unsigned long *a1; + unsigned long *a2; + + if (n == 0) + return 0; + + /* If s1 or s2 are unaligned, then compare bytes. */ + if (!UNALIGNED (s1, s2)) + { + /* If s1 and s2 are word-aligned, compare them a word at a time. */ + a1 = (unsigned long*)s1; + a2 = (unsigned long*)s2; + while (n >= sizeof (long) && *a1 == *a2) + { + n -= sizeof (long); + + /* If we've run out of bytes or hit a null, return zero + since we already know *a1 == *a2. */ + if (n == 0 || DETECTNULL (*a1)) + return 0; + + a1++; + a2++; + } + + /* A difference was detected in last few bytes of s1, so search bytewise */ + s1 = (char*)a1; + s2 = (char*)a2; + } + + while (n-- > 0 && *s1 == *s2) + { + /* If we've run out of bytes or hit a null, return zero + since we already know *s1 == *s2. */ + if (n == 0 || *s1 == '\0') + return 0; + s1++; + s2++; + } + return (*(unsigned char *) s1) - (*(unsigned char *) s2); +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---counted copy string +INDEX + strncpy +SYNOPSIS + #include + char *strncpy(char *restrict <[dst]>, const char *restrict <[src]>, + size_t <[length]>); +DESCRIPTION + <> copies not more than <[length]> characters from the + the string pointed to by <[src]> (including the terminating + null character) to the array pointed to by <[dst]>. If the + string pointed to by <[src]> is shorter than <[length]> + characters, null characters are appended to the destination + array until a total of <[length]> characters have been + written. +RETURNS + This function returns the initial value of <[dst]>. +PORTABILITY +<> is ANSI C. +<> requires no supporting OS subroutines. +QUICKREF + strncpy ansi pure +*/ + +/*SUPPRESS 560*/ +/*SUPPRESS 530*/ + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL + +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +#define TOO_SMALL(LEN) ((LEN) < sizeof (long)) + +char * +strncpy (char *__restrict dst0, + const char *__restrict src0, + size_t count) +{ +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) + char *dscan; + const char *sscan; + + dscan = dst0; + sscan = src0; + while (count > 0) + { + --count; + if ((*dscan++ = *sscan++) == '\0') + break; + } + while (count-- > 0) + *dscan++ = '\0'; + + return dst0; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If SRC and DEST is aligned and count large enough, then copy words. */ + if (!UNALIGNED (src, dst) && !TOO_SMALL (count)) + { + aligned_dst = (long*)dst; + aligned_src = (long*)src; + + /* SRC and DEST are both "long int" aligned, try to do "long int" + sized copies. */ + while (count >= sizeof (long int) && !DETECTNULL(*aligned_src)) + { + count -= sizeof (long int); + *aligned_dst++ = *aligned_src++; + } + + dst = (char*)aligned_dst; + src = (char*)aligned_src; + } + + while (count > 0) + { + --count; + if ((*dst++ = *src++) == '\0') + break; + } + + while (count-- > 0) + *dst++ = '\0'; + + return dst0; +#endif /* not PREFER_SIZE_OVER_SPEED */ +} + +/* +FUNCTION + <>---character string length + +INDEX + strnlen +SYNOPSIS + #include + size_t strnlen(const char *<[str]>, size_t <[n]>); +DESCRIPTION + The <> function works out the length of the string + starting at <<*<[str]>>> by counting chararacters until it + reaches a NUL character or the maximum: <[n]> number of + characters have been inspected. +RETURNS + <> returns the character count or <[n]>. +PORTABILITY +<> is a GNU extension. +<> requires no supporting OS subroutines. +*/ + +size_t +strnlen (const char *str, + size_t n) +{ + const char *start = str; + + while (n-- > 0 && *str) + str++; + + return str - start; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/thermosphere/src/start.s b/thermosphere/src/start.s new file mode 100644 index 000000000..4248f5d00 --- /dev/null +++ b/thermosphere/src/start.s @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */ +#define cpuactlr_el1 s3_1_c15_c2_0 +#define cpuectlr_el1 s3_1_c15_c2_1 + +.section .text.start, "ax", %progbits +.align 3 +.global _start +.type _start, %function + +_start: + // Disable interrupts + msr daifset, 0b1111 + + // Set VBAR + ldr x8, =__vectors_start__ + msr vbar_el2, x8 + + // Set tmp stack + ldr x8, =__stacks_top__ + mov sp, x8 + + // Don't call init array to save space? + // Clear BSS + ldr x0, =__bss_start__ + mov w1, #0 + ldr x2, =__end__ + sub x2, x2, x0 + bl memset + + b . // FIXME! + +.pool diff --git a/thermosphere/src/timers.c b/thermosphere/src/timers.c new file mode 100644 index 000000000..2eea628a2 --- /dev/null +++ b/thermosphere/src/timers.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "utils.h" +#include "timers.h" + +void wait(u32 microseconds) { + u32 old_time = TIMERUS_CNTR_1US_0; + while (TIMERUS_CNTR_1US_0 - old_time <= microseconds) { + /* Spin-lock. */ + } +} diff --git a/thermosphere/src/timers.h b/thermosphere/src/timers.h new file mode 100644 index 000000000..8dfad4d07 --- /dev/null +++ b/thermosphere/src/timers.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "utils.h" + +#define TIMERS_BASE 0x60005000ull +#define MAKE_TIMERS_REG(n) MAKE_REG32(TIMERS_BASE + n) + +#define TIMERUS_CNTR_1US_0 MAKE_TIMERS_REG(0x10) +#define SHARED_INTR_STATUS_0 MAKE_TIMERS_REG(0x1A0) +#define SHARED_TIMER_SECURE_CFG_0 MAKE_TIMERS_REG(0x1A4) + +typedef struct { + u32 CONFIG; + u32 STATUS; + u32 COMMAND; + u32 PATTERN; +} watchdog_timers_t; + +#define GET_WDT(n) ((volatile watchdog_timers_t *)(TIMERS_BASE + 0x100 + 0x20 * n)) +#define WDT_REBOOT_PATTERN 0xC45A +#define GET_WDT_REBOOT_CFG_REG(n) MAKE_REG32(TIMERS_BASE + 0x60 + 0x8 * n) + +void wait(u32 microseconds); + +static inline u32 get_time(void) { + return TIMERUS_CNTR_1US_0; +} diff --git a/thermosphere/src/types.h b/thermosphere/src/types.h new file mode 100644 index 000000000..68abc2021 --- /dev/null +++ b/thermosphere/src/types.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +typedef uint8_t u8; ///< 8-bit unsigned integer +typedef uint16_t u16; ///< 16-bit unsigned integer +typedef uint32_t u32; ///< 32-bit unsigned integer +typedef uint64_t u64; ///< 64-bit unsigned integer + +typedef int8_t s8; ///< 8-bit signed integer +typedef int16_t s16; ///< 16-bit signed integer +typedef int32_t s32; ///< 32-bit signed integer +typedef int64_t s64; ///< 64-bit signed integer + +typedef volatile u8 vu8; ///< 8-bit volatile unsigned integer. +typedef volatile u16 vu16; ///< 16-bit volatile unsigned integer. +typedef volatile u32 vu32; ///< 32-bit volatile unsigned integer. +typedef volatile u64 vu64; ///< 64-bit volatile unsigned integer. + +typedef volatile s8 vs8; ///< 8-bit volatile signed integer. +typedef volatile s16 vs16; ///< 16-bit volatile signed integer. +typedef volatile s32 vs32; ///< 32-bit volatile signed integer. +typedef volatile s64 vs64; ///< 64-bit volatile signed integer. diff --git a/thermosphere/src/uart.c b/thermosphere/src/uart.c new file mode 100644 index 000000000..83c237ff5 --- /dev/null +++ b/thermosphere/src/uart.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "timers.h" +#include "uart.h" +#include "misc.h" + +void uart_select(UartDevice dev) { + unsigned int id = (unsigned int)dev; + PINMUX_AUX_UARTn_TX_0(id) = 0; /* UART */ + PINMUX_AUX_UARTn_RX_0(id) = 0x48; /* UART, enable, pull up */ + PINMUX_AUX_UARTn_RTS_0(id) = 0; /* UART */ + PINMUX_AUX_UARTn_CTS_0(id) = 0x44; /* UART, enable, pull down */ +} + +void uart_init(UartDevice dev, u32 baud) { + volatile uart_t *uart = get_uart_device(dev); + + /* Set baud rate. */ + u32 rate = (8 * baud + 408000000) / (16 * baud); + uart->UART_LCR = UART_LCR_DLAB; /* Enable DLAB. */ + uart->UART_THR_DLAB = (u8)rate; /* Divisor latch LSB. */ + uart->UART_IER_DLAB = (u8)(rate >> 8); /* Divisor latch MSB. */ + uart->UART_LCR = 0; /* Diable DLAB. */ + + /* Setup UART in fifo mode. */ + uart->UART_IER_DLAB = 0; + uart->UART_IIR_FCR = UART_FCR_FCR_EN_FIFO | UART_FCR_RX_CLR | UART_FCR_TX_CLR; /* Enable and clear TX and RX FIFOs. */ + uart->UART_LSR; + wait(3 * ((baud + 999999) / baud)); + uart->UART_LCR = UART_LCR_WD_LENGTH_8; /* Set word length 8. */ + uart->UART_MCR = 0; + uart->UART_MSR = 0; + uart->UART_IRDA_CSR = 0; + uart->UART_RX_FIFO_CFG = 1; /* Set RX_FIFO trigger level */ + uart->UART_MIE = 0; + uart->UART_ASR = 0; +} + +/* This function blocks until the UART device (dev) is in the desired state (status). Make sure the desired state can be reached! */ +void uart_wait_idle(UartDevice dev, UartVendorStatus status) { + while (!(get_uart_device(dev)->UART_VENDOR_STATUS & status)) { + /* Wait */ + } +} + +void uart_send(UartDevice dev, const void *buf, size_t len) { + volatile uart_t *uart = get_uart_device(dev); + + for (size_t i = 0; i < len; i++) { + while (uart->UART_LSR & UART_LSR_TX_FIFO_FULL) { + /* Wait until the TX FIFO isn't full */ + } + uart->UART_THR_DLAB = *((const u8 *)buf + i); + } +} + +void uart_recv(UartDevice dev, void *buf, size_t len) { + volatile uart_t *uart = get_uart_device(dev); + + for (size_t i = 0; i < len; i++) { + while (uart->UART_LSR & UART_LSR_RX_FIFO_EMPTY) { + /* Wait until the RX FIFO isn't empty */ + } + *((u8 *)buf + i) = uart->UART_THR_DLAB; + } +} diff --git a/thermosphere/src/uart.h b/thermosphere/src/uart.h new file mode 100644 index 000000000..3d459c0cd --- /dev/null +++ b/thermosphere/src/uart.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "utils.h" + +#define UART_BASE 0x70006000ull + +#define BAUD_115200 115200 + +/* Exosphère: add the clkreset values for UART C,D,E */ +typedef enum { + UART_A = 0, + UART_B = 1, + UART_C = 2, + UART_D = 3, + UART_E = 4, +} UartDevice; + +/* 36.3.12 UART_VENDOR_STATUS_0_0 */ +typedef enum { + UART_VENDOR_STATE_TX_IDLE = 1 << 0, + UART_VENDOR_STATE_RX_IDLE = 1 << 1, + + /* This bit is set to 1 when a read is issued to an empty FIFO and gets cleared on register read (sticky bit until read) + 0 = NO_UNDERRUN + 1 = UNDERRUN + */ + UART_VENDOR_STATE_RX_UNDERRUN = 1 << 2, + + /* This bit is set to 1 when write data is issued to the TX FIFO when it is already full and gets cleared on register read (sticky bit until read) + 0 = NO_OVERRUN + 1 = OVERRUN + */ + UART_VENDOR_STATE_TX_OVERRUN = 1 << 3, + + UART_VENDOR_STATE_RX_FIFO_COUNTER = 0b111111 << 16, /* reflects number of current entries in RX FIFO */ + UART_VENDOR_STATE_TX_FIFO_COUNTER = 0b111111 << 24 /* reflects number of current entries in TX FIFO */ +} UartVendorStatus; + +/* 36.3.6 UART_LSR_0 */ +typedef enum { + UART_LSR_RDR = 1 << 0, /* Receiver Data Ready */ + UART_LSR_OVRF = 1 << 1, /* Receiver Overrun Error */ + UART_LSR_PERR = 1 << 2, /* Parity Error */ + UART_LSR_FERR = 1 << 3, /* Framing Error */ + UART_LSR_BRK = 1 << 4, /* BREAK condition detected on line */ + UART_LSR_THRE = 1 << 5, /* Transmit Holding Register is Empty -- OK to write data */ + UART_LSR_TMTY = 1 << 6, /* Transmit Shift Register empty status */ + UART_LSR_FIFOE = 1 << 7, /* Receive FIFO Error */ + UART_LSR_TX_FIFO_FULL = 1 << 8, /* Transmitter FIFO full status */ + UART_LSR_RX_FIFO_EMPTY = 1 << 9, /* Receiver FIFO empty status */ +} UartLineStatus; + +/* 36.3.4 UART_LCR_0 */ +typedef enum { + UART_LCR_WD_LENGTH_5 = 0, /* word length 5 */ + UART_LCR_WD_LENGTH_6 = 1, /* word length 6 */ + UART_LCR_WD_LENGTH_7 = 2, /* word length 7 */ + UART_LCR_WD_LENGTH_8 = 3, /* word length 8 */ + + /* STOP: + 0 = Transmit 1 stop bit + 1 = Transmit 2 stop bits (receiver always checks for 1 stop bit) + */ + UART_LCR_STOP = 1 << 2, + UART_LCR_PAR = 1 << 3, /* Parity enabled */ + UART_LCR_EVEN = 1 << 4, /* Even parity format. There will always be an even number of 1s in the binary representation (PAR = 1) */ + UART_LCR_SET_P = 1 << 5, /* Set (force) parity to value in LCR[4] */ + UART_LCR_SET_B = 1 << 6, /* Set BREAK condition -- Transmitter sends all zeroes to indicate BREAK */ + UART_LCR_DLAB = 1 << 7, /* Divisor Latch Access Bit (set to allow programming of the DLH, DLM Divisors) */ +} UartLineControl; + +/* 36.3.3 UART_IIR_FCR_0 */ +typedef enum { + UART_FCR_FCR_EN_FIFO = 1 << 0, /* Enable the transmit and receive FIFOs. This bit should be enabled */ + UART_FCR_RX_CLR = 1 << 1, /* Clears the contents of the receive FIFO and resets its counter logic to 0 (the receive shift register is not cleared or altered). This bit returns to 0 after clearing the FIFOs */ + UART_FCR_TX_CLR = 1 << 2, /* Clears the contents of the transmit FIFO and resets its counter logic to 0 (the transmit shift register is not cleared or altered). This bit returns to 0 after clearing the FIFOs */ + + /* DMA: + 0 = DMA_MODE_0 + 1 = DMA_MODE_1 + */ + UART_FCR_DMA = 1 << 3, + + /* TX_TRIG + 0 = FIFO_COUNT_GREATER_16 + 1 = FIFO_COUNT_GREATER_8 + 2 = FIFO_COUNT_GREATER_4 + 3 = FIFO_COUNT_GREATER_1 + */ + UART_FCR_TX_TRIG = 3 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_16 = 0 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_8 = 1 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_4 = 2 << 4, + UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_1 = 3 << 4, + + /* RX_TRIG + 0 = FIFO_COUNT_GREATER_1 + 1 = FIFO_COUNT_GREATER_4 + 2 = FIFO_COUNT_GREATER_8 + 3 = FIFO_COUNT_GREATER_16 + */ + UART_FCR_RX_TRIG = 3 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_1 = 0 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_4 = 1 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_8 = 2 << 6, + UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_16 = 3 << 6, +} UartFifoControl; + +/* 36.3.3 UART_IIR_FCR_0 */ +typedef enum { + UART_IIR_IS_STA = 1 << 0, /* Interrupt Pending if ZERO */ + UART_IIR_IS_PRI0 = 1 << 1, /* Encoded Interrupt ID Refer to IIR[3:0] table [36.3.3] */ + UART_IIR_IS_PRI1 = 1 << 2, /* Encoded Interrupt ID Refer to IIR[3:0] table */ + UART_IIR_IS_PRI2 = 1 << 3, /* Encoded Interrupt ID Refer to IIR[3:0] table */ + + /* FIFO Mode Status + 0 = 16450 mode (no FIFO) + 1 = 16550 mode (FIFO) + */ + UART_IIR_EN_FIFO = 3 << 6, + UART_IIR_MODE_16450 = 0 << 6, + UART_IIR_MODE_16550 = 1 << 6, +} UartInterruptIdentification; + +typedef struct { + /* 0x00 */ u32 UART_THR_DLAB; + /* 0x04 */ u32 UART_IER_DLAB; + /* 0x08 */ u32 UART_IIR_FCR; + /* 0x0C */ u32 UART_LCR; + /* 0x10 */ u32 UART_MCR; + /* 0x14 */ u32 UART_LSR; + /* 0x18 */ u32 UART_MSR; + /* 0x1C */ u32 UART_SPR; + /* 0x20 */ u32 UART_IRDA_CSR; + /* 0x24 */ u32 UART_RX_FIFO_CFG; + /* 0x28 */ u32 UART_MIE; + /* 0x2C */ u32 UART_VENDOR_STATUS; + /* 0x30 */ uint8_t _pad_30[0x0C]; + /* 0x3C */ u32 UART_ASR; +} uart_t; + +void uart_select(UartDevice dev); +void uart_init(UartDevice dev, u32 baud); +void uart_wait_idle(UartDevice dev, UartVendorStatus status); +void uart_send(UartDevice dev, const void *buf, size_t len); +void uart_recv(UartDevice dev, void *buf, size_t len); + +static inline volatile uart_t *get_uart_device(UartDevice dev) { + static const size_t offsets[] = {0, 0x40, 0x200, 0x300, 0x400}; + return (volatile uart_t *)(UART_BASE + offsets[dev]); +} diff --git a/thermosphere/src/utils.c b/thermosphere/src/utils.c new file mode 100644 index 000000000..86b7c8726 --- /dev/null +++ b/thermosphere/src/utils.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "utils.h" + +__attribute__((noinline)) bool overlaps(u64 as, u64 ae, u64 bs, u64 be) +{ + if(as <= bs && bs < ae) + return true; + if(bs <= as && as < be) + return true; + return false; +} diff --git a/thermosphere/src/utils.h b/thermosphere/src/utils.h new file mode 100644 index 000000000..8db79c83c --- /dev/null +++ b/thermosphere/src/utils.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "types.h" + + +#define BIT(n) (1u << (n)) +#define BITL(n) (1ull << (n)) +#define MASK(n) (BIT(n) - 1) +#define MASKL(n) (BITL(n) - 1) +#define MASK2(a,b) (MASK(a) & ~MASK(b)) +#define MASK2L(a,b) (MASKL(a) & ~MASKL(b)) + +#define MAKE_REG32(a) (*(volatile u32 *)(a)) + +#define ALIGN(m) __attribute__((aligned(m))) +#define PACKED __attribute__((packed)) + +#define ALINLINE __attribute__((always_inline)) + +#define SET_SYSREG(reg, val) do { temp_reg = (val); __asm__ __volatile__ ("msr " #reg ", %0" :: "r"(temp_reg) : "memory"); } while(false) + +bool overlaps(u64 as, u64 ae, u64 bs, u64 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 & MASK2L(40, 12)) | ((uintptr_t)vaddr & MASKL(12)); +} + +static inline uintptr_t get_physical_address_el0(const uintptr_t el0_vaddr) { + uintptr_t PAR; + __asm__ __volatile__ ("at s1e0r, %0" :: "r"(el0_vaddr)); + __asm__ __volatile__ ("mrs %0, par_el1" : "=r"(PAR)); + return (PAR & 1) ? 0ull : (PAR & MASK2L(40, 12)) | ((uintptr_t)el0_vaddr & MASKL(12)); +} + +static inline u32 read32le(const volatile void *dword, size_t offset) { + return *(u32 *)((uintptr_t)dword + offset); +} + +static inline u32 read32be(const volatile void *dword, size_t offset) { + return __builtin_bswap32(read32le(dword, offset)); +} + +static inline u64 read64le(const volatile void *qword, size_t offset) { + return *(u64 *)((uintptr_t)qword + offset); +} + +static inline u64 read64be(const volatile void *qword, size_t offset) { + return __builtin_bswap64(read64le(qword, offset)); +} + +static inline void write32le(volatile void *dword, size_t offset, u32 value) { + *(u32 *)((uintptr_t)dword + offset) = value; +} + +static inline void write32be(volatile void *dword, size_t offset, u32 value) { + write32le(dword, offset, __builtin_bswap32(value)); +} + +static inline void write64le(volatile void *qword, size_t offset, u64 value) { + *(u64 *)((uintptr_t)qword + offset) = value; +} + +static inline void write64be(volatile void *qword, size_t offset, u64 value) { + write64le(qword, offset, __builtin_bswap64(value)); +} + +static inline unsigned int get_core_id(void) { + u64 core_id; + __asm__ __volatile__ ("mrs %0, mpidr_el1" : "=r"(core_id)); + return (unsigned int)core_id & 3; +} + +static inline u64 get_debug_authentication_status(void) { + u64 debug_auth; + __asm__ __volatile__ ("mrs %0, dbgauthstatus_el1" : "=r"(debug_auth)); + return debug_auth; +} + +static inline u32 get_spsr(void) { + u32 spsr; + __asm__ __volatile__ ("mrs %0, spsr_el2" : "=r"(spsr)); + return spsr; +} + +static inline bool check_32bit_additive_overflow(u32 a, u32 b) { + return __builtin_add_overflow_p(a, b, (u32)0); +}