From c6b486f0edee49ed0c20e013f6e866cd2e556512 Mon Sep 17 00:00:00 2001 From: Ivaylo Ivanov Date: Thu, 10 Jul 2025 15:48:37 +0300 Subject: [PATCH] aarch64: introduce early relocation of the bootloader All newer exynos devices with s-boot have the "feature" of placing the android kernel at a random location in dram, specifically around 0x80000000 and 0x83000000. This made it hard to assume where we are at, hence why we made everything as PiE (position-independent) as possible. This, however, resulted in a lot of issues. I usually managed to get around them with ugly hacks, but in the last 5-6 commits, with the fixed support for armv7, everything fell apart for arm64. So, the solution for this is to implement code at the beginning of the bootloader that relocates it to a safe place in memory. This is currently enabled for dreamlte, and it must be enabled and configured for all other boards with that issue. Otherwise they will NOT boot anymore. Boards that don't need this can keep CONFIG_POSITION_INDEPENDENT disabled. --- Kconfig | 2 ++ Makefile | 3 ++- arch/Kconfig | 7 ++++++ arch/aarch64/Makefile | 1 + arch/aarch64/linker.lds.S | 50 +++++++++++++++++++++++++------------- arch/aarch64/reloc.S | 46 +++++++++++++++++++++++++++++++++++ arch/aarch64/start.S | 1 + configs/dreamlte_defconfig | 4 ++- main/main.c | 1 - 9 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 arch/Kconfig create mode 100644 arch/aarch64/reloc.S diff --git a/Kconfig b/Kconfig index 1b9080a..a9331c4 100644 --- a/Kconfig +++ b/Kconfig @@ -4,6 +4,8 @@ # mainmenu "uniLoader Configuration" +source "arch/Kconfig" + menu "Building options" config CROSS_COMPILE string "Cross-compiler tool prefix" diff --git a/Makefile b/Makefile index 9c8b844..18f533c 100644 --- a/Makefile +++ b/Makefile @@ -373,7 +373,8 @@ all: arch/$(ARCH)/linker.lds uniLoader # Generate linker script from template quiet_cmd_cpp_lds = LDS $@ - cmd_cpp_lds = $(CPP) $< -DTEXT_BASE=$(TEXT_BASE) \ + cmd_cpp_lds = $(CPP) -include include/generated/autoconf.h $< \ + -DTEXT_BASE=$(TEXT_BASE) \ -DKERNEL_PATH=$(KERNEL_PATH) \ -DDTB_PATH=$(DT_PATH) \ -DRAMDISK_PATH=$(RAMDISK_PATH) \ diff --git a/arch/Kconfig b/arch/Kconfig new file mode 100644 index 0000000..dc21446 --- /dev/null +++ b/arch/Kconfig @@ -0,0 +1,7 @@ +menu "Architecture" + config POSITION_INDEPENDENT + bool "Code relocation during early boot" + help + Say Y if your previous bootloader loads uniLoader at an unknown + or different address every time. +endmenu diff --git a/arch/aarch64/Makefile b/arch/aarch64/Makefile index de499da..6f1e0c9 100644 --- a/arch/aarch64/Makefile +++ b/arch/aarch64/Makefile @@ -2,4 +2,5 @@ # # Copyright (c) 2025 Ivaylo Ivanov +obj-$(CONFIG_POSITION_INDEPENDENT) += aarch64/reloc.o obj-y += aarch64/start.o aarch64/memcpy.o diff --git a/arch/aarch64/linker.lds.S b/arch/aarch64/linker.lds.S index daa98d8..666f204 100644 --- a/arch/aarch64/linker.lds.S +++ b/arch/aarch64/linker.lds.S @@ -2,8 +2,14 @@ /* * Copyright (c) 2022, Ivaylo Ivanov */ +ENTRY( +#ifdef CONFIG_POSITION_INDEPENDENT + _head +#else + _start +#endif + ); -ENTRY(_start); OUTPUT_FORMAT("elf64-littleaarch64") OUTPUT_ARCH(aarch64) @@ -15,12 +21,31 @@ INPUT(RAMDISK_PATH) SECTIONS { + . = TEXT_BASE; + .text : { - KEEP(*(.text.start)) +#ifdef CONFIG_POSITION_INDEPENDENT + _reloc_start = .; +#endif *(.text) *(.text.*) } + .kernel ALIGN(4096) : { + kernel = .; + KERNEL_PATH + } + + .dtb ALIGN(4096) : { + dtb = .; + DTB_PATH + } + + .ramdisk ALIGN(4096) : { + ramdisk = .; + RAMDISK_PATH + } + .rodata ALIGN(4) : { *(.rodata) *(.rodata.*) @@ -42,23 +67,14 @@ SECTIONS .stack ALIGN(4096) : { . = . + 4096; _stack_end = .; - } - - .dtb ALIGN(4096) : { - dtb = .; - DTB_PATH - } - - .kernel ALIGN(4096) : { - kernel = .; - KERNEL_PATH - } - - .ramdisk ALIGN(4096) : { - ramdisk = .; - RAMDISK_PATH +#ifdef CONFIG_POSITION_INDEPENDENT + _reloc_end = .; +#endif } kernel_size = SIZEOF(.kernel); ramdisk_size = SIZEOF(.ramdisk); +#ifdef CONFIG_POSITION_INDEPENDENT + _reloc_size = _reloc_end - _reloc_start; +#endif } diff --git a/arch/aarch64/reloc.S b/arch/aarch64/reloc.S new file mode 100644 index 0000000..be745f1 --- /dev/null +++ b/arch/aarch64/reloc.S @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2025, Ivaylo Ivanov + */ +#include "asmdefs.h" + +.extern _reloc_size +.extern _start + +ENTRY_ALIAS(_head) + adr x1, _relocated_uni + b _reloc_entry + +ENTRY_ALIAS(_rel_stack_base) + .quad _start + +ENTRY_ALIAS(_rel_stack_size) + .quad _reloc_size + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .ascii "ARM\x64" + .long 0 + +ENTRY_ALIAS( _reloc_entry) + adr x4, _start + ldr x5, _rel_stack_base + cmp x4, x5 + beq _entry + ldr x6, _rel_stack_size + +_copy: + ldp x2, x3, [x4], #16 + stp x2, x3, [x5], #16 + subs x6, x6, #16 + b.ne _copy + ldr x5, _rel_stack_base + +_entry: + br x5 + +_dead: /* we should never get here */ + b _dead + +ENTRY_ALIAS(_relocated_uni) diff --git a/arch/aarch64/start.S b/arch/aarch64/start.S index 16df791..5174008 100644 --- a/arch/aarch64/start.S +++ b/arch/aarch64/start.S @@ -27,3 +27,4 @@ END(_start) ENTRY(load_kernel_and_jump) br x4 END(load_kernel_and_jump) + diff --git a/configs/dreamlte_defconfig b/configs/dreamlte_defconfig index a5379a9..ca5510b 100644 --- a/configs/dreamlte_defconfig +++ b/configs/dreamlte_defconfig @@ -1,7 +1,9 @@ +CONFIG_POSITION_INDEPENDENT=y CONFIG_EXYNOS_8895=y CONFIG_SAMSUNG_DREAMLTE=y +CONFIG_TEXT_BASE=0x87000000 CONFIG_PAYLOAD_ENTRY=0x90000000 -CONFIG_RAMDISK_ENTRY=0x82000000 +CONFIG_RAMDISK_ENTRY=0x84000000 CONFIG_FRAMEBUFFER_BASE=0xcc000000 CONFIG_FRAMEBUFFER_WIDTH=1440 CONFIG_FRAMEBUFFER_HEIGHT=2960 diff --git a/main/main.c b/main/main.c index b87bb3e..4b92e5e 100644 --- a/main/main.c +++ b/main/main.c @@ -44,7 +44,6 @@ void main(void* dt, void* kernel, void* ramdisk) /* Copy kernel to memory and boot */ printk(-1, "booting linux...\n"); - #ifdef __aarch64__ memcpy((void*)CONFIG_PAYLOAD_ENTRY, kernel, (unsigned long) &kernel_size); __optimized_memcpy((void*)CONFIG_RAMDISK_ENTRY, ramdisk, (unsigned long) &ramdisk_size);