LoongArch: Add support for kernel relocation
authorYouling Tang <tangyouling@loongson.cn>
Sat, 25 Feb 2023 07:52:56 +0000 (15:52 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Sat, 25 Feb 2023 14:12:16 +0000 (22:12 +0800)
This config allows to compile kernel as PIE and to relocate it at any
virtual address at runtime: this paves the way to KASLR.

Runtime relocation is possible since relocation metadata are embedded
into the kernel.

Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Xi Ruoyao <xry111@xry111.site> # Use arch_initcall
Signed-off-by: Jinyang He <hejinyang@loongson.cn> # Provide la_abs relocation code
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/Kconfig
arch/loongarch/Makefile
arch/loongarch/include/asm/asmmacro.h
arch/loongarch/include/asm/setup.h
arch/loongarch/kernel/Makefile
arch/loongarch/kernel/head.S
arch/loongarch/kernel/relocate.c [new file with mode: 0644]
arch/loongarch/kernel/vmlinux.lds.S

index 0c1c6063cc66175a4d448f38d4a1b1974b506c71..32ab90dd76e5e8741d85421316f737af65a8da92 100644 (file)
@@ -493,6 +493,14 @@ config PHYSICAL_START
          specified in the "crashkernel=YM@XM" command line boot parameter
          passed to the panic-ed kernel).
 
+config RELOCATABLE
+       bool "Relocatable kernel"
+       help
+         This builds the kernel as a Position Independent Executable (PIE),
+         which retains all relocation metadata required, so as to relocate
+         the kernel binary at runtime to a different virtual address from
+         its link address.
+
 config SECCOMP
        bool "Enable seccomp to safely compute untrusted bytecode"
        depends on PROC_FS
index 6e1c931a8507e0c67dd167736aa51e994212af2d..768592998b3918d3229da4307a9a4b59430809fa 100644 (file)
@@ -71,6 +71,11 @@ KBUILD_AFLAGS_MODULE         += -Wa,-mla-global-with-abs
 KBUILD_CFLAGS_MODULE           += -fplt -Wa,-mla-global-with-abs,-mla-local-with-abs
 endif
 
+ifeq ($(CONFIG_RELOCATABLE),y)
+KBUILD_CFLAGS_KERNEL           += -fPIE
+LDFLAGS_vmlinux                        += -static -pie --no-dynamic-linker -z notext
+endif
+
 cflags-y += -ffreestanding
 cflags-y += $(call cc-option, -mno-check-zero-division)
 
index cdc9935d5554cf6a0f56f61c48b6fd356b909cc0..c51a1b43acb4458da6bddb6a5952b3448d036c4b 100644 (file)
 .endm
 
 .macro la_abs reg, sym
+#ifndef CONFIG_RELOCATABLE
        la.abs  \reg, \sym
+#else
+       766:
+       lu12i.w \reg, 0
+       ori     \reg, \reg, 0
+       lu32i.d \reg, 0
+       lu52i.d \reg, \reg, 0
+       .pushsection ".la_abs", "aw", %progbits
+       768:
+       .dword  768b-766b
+       .dword  \sym
+       .popsection
+#endif
 .endm
 
 #endif /* _ASM_ASMMACRO_H */
index 72ead58039f3e159ae13c7e066c4ee9793aa2208..27d968655b4b8bdb96f219a89cd5ef887aade8dd 100644 (file)
@@ -21,4 +21,20 @@ extern void per_cpu_trap_init(int cpu);
 extern void set_handler(unsigned long offset, void *addr, unsigned long len);
 extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len);
 
+#ifdef CONFIG_RELOCATABLE
+
+struct rela_la_abs {
+       long offset;
+       long symvalue;
+};
+
+extern long __la_abs_begin;
+extern long __la_abs_end;
+extern long __rela_dyn_begin;
+extern long __rela_dyn_end;
+
+extern void __init relocate_kernel(void);
+
+#endif
+
 #endif /* __SETUP_H */
index df5dbabfe7a6c95274cf0c95c3d429c1f2d47b0d..94d33dbd4912e1a1d1daaa12b0670b81b2bdbc3a 100644 (file)
@@ -41,6 +41,8 @@ obj-$(CONFIG_NUMA)            += numa.o
 
 obj-$(CONFIG_MAGIC_SYSRQ)      += sysrq.o
 
+obj-$(CONFIG_RELOCATABLE)      += relocate.o
+
 obj-$(CONFIG_KEXEC)            += machine_kexec.o relocate_kernel.o
 obj-$(CONFIG_CRASH_DUMP)       += crash_dump.o
 
index d2ac26b5b22bdfdc28405428284818f41b33941d..c5c3ec2b819a92d53af065bf08e22145462c31e3 100644 (file)
@@ -86,6 +86,10 @@ SYM_CODE_START(kernel_entry)                 # kernel entry point
        PTR_ADD         sp, sp, tp
        set_saved_sp    sp, t0, t1
 
+#ifdef CONFIG_RELOCATABLE
+       bl              relocate_kernel
+#endif
+
        bl              start_kernel
        ASM_BUG()
 
diff --git a/arch/loongarch/kernel/relocate.c b/arch/loongarch/kernel/relocate.c
new file mode 100644 (file)
index 0000000..879c403
--- /dev/null
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for Kernel relocation at boot time
+ *
+ * Copyright (C) 2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/elf.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/panic_notifier.h>
+#include <asm/inst.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+
+#define RELOCATED(x) ((void *)((long)x + reloc_offset))
+
+static unsigned long reloc_offset;
+
+static inline void __init relocate_relative(void)
+{
+       Elf64_Rela *rela, *rela_end;
+       rela = (Elf64_Rela *)&__rela_dyn_begin;
+       rela_end = (Elf64_Rela *)&__rela_dyn_end;
+
+       for ( ; rela < rela_end; rela++) {
+               Elf64_Addr addr = rela->r_offset;
+               Elf64_Addr relocated_addr = rela->r_addend;
+
+               if (rela->r_info != R_LARCH_RELATIVE)
+                       continue;
+
+               if (relocated_addr >= VMLINUX_LOAD_ADDRESS)
+                       relocated_addr = (Elf64_Addr)RELOCATED(relocated_addr);
+
+               *(Elf64_Addr *)RELOCATED(addr) = relocated_addr;
+       }
+}
+
+static inline void __init relocate_absolute(void)
+{
+       void *begin, *end;
+       struct rela_la_abs *p;
+
+       begin = &__la_abs_begin;
+       end   = &__la_abs_end;
+
+       for (p = begin; (void *)p < end; p++) {
+               long v = p->symvalue;
+               uint32_t lu12iw, ori, lu32id, lu52id;
+               union loongarch_instruction *insn = (void *)p - p->offset;
+
+               lu12iw = (v >> 12) & 0xfffff;
+               ori    = v & 0xfff;
+               lu32id = (v >> 32) & 0xfffff;
+               lu52id = v >> 52;
+
+               insn[0].reg1i20_format.immediate = lu12iw;
+               insn[1].reg2i12_format.immediate = ori;
+               insn[2].reg1i20_format.immediate = lu32id;
+               insn[3].reg2i12_format.immediate = lu52id;
+       }
+}
+
+void __init relocate_kernel(void)
+{
+       reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
+
+       if (reloc_offset)
+               relocate_relative();
+
+       relocate_absolute();
+}
+
+/*
+ * Show relocation information on panic.
+ */
+static void show_kernel_relocation(const char *level)
+{
+       if (reloc_offset > 0) {
+               printk(level);
+               pr_cont("Kernel relocated by 0x%lx\n", reloc_offset);
+               pr_cont(" .text @ 0x%px\n", _text);
+               pr_cont(" .data @ 0x%px\n", _sdata);
+               pr_cont(" .bss  @ 0x%px\n", __bss_start);
+       }
+}
+
+static int kernel_location_notifier_fn(struct notifier_block *self,
+                                      unsigned long v, void *p)
+{
+       show_kernel_relocation(KERN_EMERG);
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block kernel_location_notifier = {
+       .notifier_call = kernel_location_notifier_fn
+};
+
+static int __init register_kernel_offset_dumper(void)
+{
+       atomic_notifier_chain_register(&panic_notifier_list,
+                                      &kernel_location_notifier);
+       return 0;
+}
+
+arch_initcall(register_kernel_offset_dumper);
index 733b16e8d55ddb55d3142cf7ccd02287c158c85f..ad2ce1a0908e4da6147a517012d9d0eb190ae71d 100644 (file)
@@ -66,10 +66,21 @@ SECTIONS
                __alt_instructions_end = .;
        }
 
+#ifdef CONFIG_RELOCATABLE
+       . = ALIGN(8);
+       .la_abs : AT(ADDR(.la_abs) - LOAD_OFFSET) {
+               __la_abs_begin = .;
+               *(.la_abs)
+               __la_abs_end = .;
+       }
+#endif
+
        .got : ALIGN(16) { *(.got) }
        .plt : ALIGN(16) { *(.plt) }
        .got.plt : ALIGN(16) { *(.got.plt) }
 
+       .data.rel : { *(.data.rel*) }
+
        . = ALIGN(PECOFF_SEGMENT_ALIGN);
        __init_begin = .;
        __inittext_begin = .;
@@ -93,8 +104,6 @@ SECTIONS
        PERCPU_SECTION(1 << CONFIG_L1_CACHE_SHIFT)
 #endif
 
-       .rela.dyn : ALIGN(8) { *(.rela.dyn) *(.rela*) }
-
        .init.bss : {
                *(.init.bss)
        }
@@ -107,6 +116,12 @@ SECTIONS
        RO_DATA(4096)
        RW_DATA(1 << CONFIG_L1_CACHE_SHIFT, PAGE_SIZE, THREAD_SIZE)
 
+       .rela.dyn : ALIGN(8) {
+               __rela_dyn_begin = .;
+                *(.rela.dyn) *(.rela*)
+               __rela_dyn_end = .;
+       }
+
        .sdata : {
                *(.sdata)
        }
@@ -133,6 +148,7 @@ SECTIONS
 
        DISCARDS
        /DISCARD/ : {
+               *(.dynamic .dynsym .dynstr .hash .gnu.hash)
                *(.gnu.attributes)
                *(.options)
                *(.eh_frame)