2023-02-10 08:47:46

by Youling Tang

[permalink] [raw]
Subject: [PATCH v4 0/5] LoongArch: Add kernel relocation and KASLR support

This patch series to support kernel relocation and KASLR (only 64bit).

Tested the kernel images built with new toolchain (Binutils-2.40 + patched
GCC-12.2) and old toolchain (kernel.org cross toolchain [1]) on a
3A5000-7A2000-EVB.

With CONFIG_RANDOMIZE_BASE=y, the results are:

1. first boot, new toolchain:

$ sudo cat /proc/iomem | grep Kernel
01080000-0189ffff : Kernel code
018a0000-01deb5ff : Kernel data
01deb600-01ef6e9f : Kernel bss

2. second boot, new toolchain:

$ sudo cat /proc/iomem | grep Kernel
012f0000-01b0ffff : Kernel code
01b10000-0205b5ff : Kernel data
0205b600-02166e9f : Kernel bss

3. first boot, old toolchain:
010e0000-018fffff : Kernel code
01900000-01e591ff : Kernel data
01e59200-01f56dcf : Kernel bss

4. second boot, old toolchain:
010b0000-018cffff : Kernel code
018d0000-01e291ff : Kernel data
01e29200-01f26dcf : Kernel bss

Changes from v3:

- JUMP_LINK_ADDR renamed to JUMP_VIRT_ADDR, and use the way of parameter
passing.

- Reimplement kernel relocation, when the link address and load address
are different, realize the effect of adaptive relocation (one of the
usage scenarios is kdump operation).

- Reimplement KASLR.

Changes from v2:

- Correctly fixup pcaddi12i/ori/lu32i.d/lu52i.d sequence generated by
GNU as <= 2.39 for la.pcrel.

Changes from v1 to v2:

- Relocate the handlers instead of using a trampoline, to avoid
performance issue on NUMA systems.
- Fix compiler warnings.

Xi Ruoyao (2):
LoongArch: Use la.pcrel instead of la.abs when it's trivially possible
LoongArch: Use la.pcrel instead of la.abs for exception handlers

Youling Tang (3):
LoongArch: Add JUMP_VIRT_ADDR macro implementation to avoid using
la.abs
LoongArch: Add support for kernel relocation
LoongArch: Add support for kernel address space layout randomization
(KASLR)

arch/loongarch/Kconfig | 38 +++++
arch/loongarch/Makefile | 5 +
arch/loongarch/include/asm/inst.h | 1 +
arch/loongarch/include/asm/setup.h | 6 +-
arch/loongarch/include/asm/stackframe.h | 14 +-
arch/loongarch/include/asm/uaccess.h | 1 -
arch/loongarch/kernel/Makefile | 2 +
arch/loongarch/kernel/entry.S | 2 +-
arch/loongarch/kernel/genex.S | 40 ++++-
arch/loongarch/kernel/head.S | 31 +++-
arch/loongarch/kernel/relocate.c | 215 ++++++++++++++++++++++++
arch/loongarch/kernel/traps.c | 158 ++++++++++++++---
arch/loongarch/kernel/vmlinux.lds.S | 11 +-
arch/loongarch/mm/tlb.c | 23 +--
arch/loongarch/mm/tlbex.S | 72 +++++++-
arch/loongarch/power/suspend_asm.S | 5 +-
16 files changed, 560 insertions(+), 64 deletions(-)
create mode 100644 arch/loongarch/kernel/relocate.c

--
2.37.3



2023-02-10 08:47:53

by Youling Tang

[permalink] [raw]
Subject: [PATCH v4 1/5] LoongArch: Use la.pcrel instead of la.abs when it's trivially possible

From: Xi Ruoyao <[email protected]>

Let's start to kill la.abs inpreparation for the subsequent support of the
PIE kernel.

Signed-off-by: Xi Ruoyao <[email protected]>
---
arch/loongarch/include/asm/stackframe.h | 2 +-
arch/loongarch/include/asm/uaccess.h | 1 -
arch/loongarch/kernel/entry.S | 2 +-
arch/loongarch/kernel/head.S | 2 +-
arch/loongarch/mm/tlbex.S | 3 +--
5 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h
index 4ca953062b5b..7deb043ce387 100644
--- a/arch/loongarch/include/asm/stackframe.h
+++ b/arch/loongarch/include/asm/stackframe.h
@@ -90,7 +90,7 @@
.endm

.macro set_saved_sp stackp temp temp2
- la.abs \temp, kernelsp
+ la.pcrel \temp, kernelsp
#ifdef CONFIG_SMP
LONG_ADD \temp, \temp, u0
#endif
diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h
index 255899d4a7c3..0d22991ae430 100644
--- a/arch/loongarch/include/asm/uaccess.h
+++ b/arch/loongarch/include/asm/uaccess.h
@@ -22,7 +22,6 @@
extern u64 __ua_limit;

#define __UA_ADDR ".dword"
-#define __UA_LA "la.abs"
#define __UA_LIMIT __ua_limit

/*
diff --git a/arch/loongarch/kernel/entry.S b/arch/loongarch/kernel/entry.S
index d53b631c9022..2566977f2f68 100644
--- a/arch/loongarch/kernel/entry.S
+++ b/arch/loongarch/kernel/entry.S
@@ -20,7 +20,7 @@
.align 5
SYM_FUNC_START(handle_syscall)
csrrd t0, PERCPU_BASE_KS
- la.abs t1, kernelsp
+ la.pcrel t1, kernelsp
add.d t1, t1, t0
move t2, sp
ld.d sp, t1, 0
diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
index 57bada6b4e93..aa6181714ec3 100644
--- a/arch/loongarch/kernel/head.S
+++ b/arch/loongarch/kernel/head.S
@@ -117,7 +117,7 @@ SYM_CODE_START(smpboot_entry)
li.w t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0
csrwr t0, LOONGARCH_CSR_EUEN

- la.abs t0, cpuboot_data
+ la.pcrel t0, cpuboot_data
ld.d sp, t0, CPU_BOOT_STACK
ld.d tp, t0, CPU_BOOT_TINFO

diff --git a/arch/loongarch/mm/tlbex.S b/arch/loongarch/mm/tlbex.S
index 58781c6e4191..3dd2a9615cd9 100644
--- a/arch/loongarch/mm/tlbex.S
+++ b/arch/loongarch/mm/tlbex.S
@@ -24,8 +24,7 @@
move a0, sp
REG_S a2, sp, PT_BVADDR
li.w a1, \write
- la.abs t0, do_page_fault
- jirl ra, t0, 0
+ bl do_page_fault
RESTORE_ALL_AND_RET
SYM_FUNC_END(tlb_do_page_fault_\write)
.endm
--
2.37.3


2023-02-10 08:48:04

by Youling Tang

[permalink] [raw]
Subject: [PATCH v4 3/5] LoongArch: Add JUMP_VIRT_ADDR macro implementation to avoid using la.abs

Add JUMP_VIRT_ADDR macro implementation to avoid using la.abs.

Signed-off-by: Youling Tang <[email protected]>
---
arch/loongarch/include/asm/stackframe.h | 9 +++++++++
arch/loongarch/kernel/head.S | 12 ++++--------
arch/loongarch/power/suspend_asm.S | 5 ++---
3 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h
index bbec1e56b61b..9a14bbb4cf36 100644
--- a/arch/loongarch/include/asm/stackframe.h
+++ b/arch/loongarch/include/asm/stackframe.h
@@ -10,6 +10,7 @@
#include <asm/asm.h>
#include <asm/asmmacro.h>
#include <asm/asm-offsets.h>
+#include <asm/addrspace.h>
#include <asm/loongarch.h>
#include <asm/thread_info.h>

@@ -217,4 +218,12 @@
RESTORE_SP_AND_RET \docfi
.endm

+/* Jump to the virtual address. */
+ .macro JUMP_VIRT_ADDR temp1 temp2
+ li.d \temp1, CACHE_BASE
+ pcaddi \temp2, 0
+ or \temp1, \temp1, \temp2
+ jirl zero, \temp1, 0xc
+ .endm
+
#endif /* _ASM_STACKFRAME_H */
diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
index aa6181714ec3..d2ac26b5b22b 100644
--- a/arch/loongarch/kernel/head.S
+++ b/arch/loongarch/kernel/head.S
@@ -50,11 +50,8 @@ SYM_CODE_START(kernel_entry) # kernel entry point
li.d t0, CSR_DMW1_INIT # CA, PLV0, 0x9000 xxxx xxxx xxxx
csrwr t0, LOONGARCH_CSR_DMWIN1

- /* We might not get launched at the address the kernel is linked to,
- so we jump there. */
- la.abs t0, 0f
- jr t0
-0:
+ JUMP_VIRT_ADDR t0, t1
+
/* Enable PG */
li.w t0, 0xb0 # PLV=0, IE=0, PG=1
csrwr t0, LOONGARCH_CSR_CRMD
@@ -106,9 +103,8 @@ SYM_CODE_START(smpboot_entry)
li.d t0, CSR_DMW1_INIT # CA, PLV0
csrwr t0, LOONGARCH_CSR_DMWIN1

- la.abs t0, 0f
- jr t0
-0:
+ JUMP_VIRT_ADDR t0, t1
+
/* Enable PG */
li.w t0, 0xb0 # PLV=0, IE=0, PG=1
csrwr t0, LOONGARCH_CSR_CRMD
diff --git a/arch/loongarch/power/suspend_asm.S b/arch/loongarch/power/suspend_asm.S
index eb2675642f9f..90da899c06a1 100644
--- a/arch/loongarch/power/suspend_asm.S
+++ b/arch/loongarch/power/suspend_asm.S
@@ -78,9 +78,8 @@ SYM_INNER_LABEL(loongarch_wakeup_start, SYM_L_GLOBAL)
li.d t0, CSR_DMW1_INIT # CA, PLV0
csrwr t0, LOONGARCH_CSR_DMWIN1

- la.abs t0, 0f
- jr t0
-0:
+ JUMP_VIRT_ADDR t0, t1
+
la.pcrel t0, acpi_saved_sp
ld.d sp, t0, 0
SETUP_WAKEUP
--
2.37.3


2023-02-10 08:48:07

by Youling Tang

[permalink] [raw]
Subject: [PATCH v4 2/5] LoongArch: Use la.pcrel instead of la.abs for exception handlers

From: Xi Ruoyao <[email protected]>

It's needed to build the kernel as a PIE, or the linker will complain.

For the consideration about performance, we copy the exception handlers
to a dedicated 64 KB area for each CPU. So, the PC-relative offset
calculated at link time will be incorrect and we need to relocate the
exception handlers after copying them.

For the simplicity, we don't use the ELF R_LARCH_* relocations, but code
an relocation entry as simply (offset_in_the_handler, symbol_addr). For
each exception handler, we also code the number of relocation entries.
Then we can use the relocation information to fix up the handlers after
copying them.

Signed-off-by: Xi Ruoyao <[email protected]>
---
arch/loongarch/include/asm/inst.h | 1 +
arch/loongarch/include/asm/setup.h | 6 +-
arch/loongarch/include/asm/stackframe.h | 3 +-
arch/loongarch/kernel/genex.S | 40 +++++-
arch/loongarch/kernel/traps.c | 158 ++++++++++++++++++++----
arch/loongarch/mm/tlb.c | 23 ++--
arch/loongarch/mm/tlbex.S | 69 +++++++++--
7 files changed, 255 insertions(+), 45 deletions(-)

diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
index 7eedd83fd0d7..426054518a3d 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -32,6 +32,7 @@ enum reg1i20_op {
lu12iw_op = 0x0a,
lu32id_op = 0x0b,
pcaddi_op = 0x0c,
+ pcalau12i_op = 0x0d,
pcaddu12i_op = 0x0e,
pcaddu18i_op = 0x0f,
};
diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h
index 72ead58039f3..f0a2b34365f1 100644
--- a/arch/loongarch/include/asm/setup.h
+++ b/arch/loongarch/include/asm/setup.h
@@ -11,6 +11,9 @@

#define VECSIZE 0x200

+struct handler_reloc;
+
+extern struct handler_reloc *eentry_reloc[];
extern unsigned long eentry;
extern unsigned long tlbrentry;
extern char init_command_line[COMMAND_LINE_SIZE];
@@ -18,7 +21,8 @@ extern void tlb_init(int cpu);
extern void cpu_cache_init(void);
extern void cache_error_setup(void);
extern void per_cpu_trap_init(int cpu);
-extern void set_handler(unsigned long offset, void *addr, unsigned long len);
+extern void set_handler(unsigned long exccode, void *addr);
extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len);
+extern void reloc_handler(unsigned long handler, struct handler_reloc *rel);

#endif /* __SETUP_H */
diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h
index 7deb043ce387..bbec1e56b61b 100644
--- a/arch/loongarch/include/asm/stackframe.h
+++ b/arch/loongarch/include/asm/stackframe.h
@@ -77,7 +77,8 @@
* new value in sp.
*/
.macro get_saved_sp docfi=0
- la.abs t1, kernelsp
+ /* The label is used for generating reloc tables for handlers */
+514: la.pcrel t1, t0, kernelsp
#ifdef CONFIG_SMP
csrrd t0, PERCPU_BASE_KS
LONG_ADD t1, t1, t0
diff --git a/arch/loongarch/kernel/genex.S b/arch/loongarch/kernel/genex.S
index 7e5c293ed89f..005a10fe5a50 100644
--- a/arch/loongarch/kernel/genex.S
+++ b/arch/loongarch/kernel/genex.S
@@ -34,7 +34,7 @@ SYM_FUNC_END(__arch_cpu_idle)
SYM_FUNC_START(handle_vint)
BACKUP_T0T1
SAVE_ALL
- la.abs t1, __arch_cpu_idle
+0: la.pcrel t1, t2, __arch_cpu_idle
LONG_L t0, sp, PT_ERA
/* 32 byte rollback region */
ori t0, t0, 0x1f
@@ -43,11 +43,25 @@ SYM_FUNC_START(handle_vint)
LONG_S t0, sp, PT_ERA
1: move a0, sp
move a1, sp
- la.abs t0, do_vint
+2: la.pcrel t0, t2, do_vint
jirl ra, t0, 0
RESTORE_ALL_AND_RET
SYM_FUNC_END(handle_vint)

+SYM_DATA_START(rel_handle_vint)
+LONG 3
+
+LONG 514b - handle_vint
+LONG kernelsp
+
+LONG 0b - handle_vint
+LONG __arch_cpu_idle
+
+LONG 2b - handle_vint
+LONG do_vint
+
+SYM_DATA_END(rel_handle_vint)
+
SYM_FUNC_START(except_vec_cex)
b cache_parity_error
SYM_FUNC_END(except_vec_cex)
@@ -72,12 +86,24 @@ SYM_FUNC_END(except_vec_cex)
SAVE_ALL
build_prep_\prep
move a0, sp
- la.abs t0, do_\handler
+ 667:
+ la.pcrel t0, t1, do_\handler
jirl ra, t0, 0
668:
RESTORE_ALL_AND_RET
SYM_FUNC_END(handle_\exception)
SYM_DATA(unwind_hint_\exception, .word 668b - 666b)
+
+ SYM_DATA_START(rel_handle_\exception)
+ LONG 2
+
+ LONG 514b - 666b
+ LONG kernelsp
+
+ LONG 667b - 666b
+ LONG do_\handler
+
+ SYM_DATA_END(rel_handle_\exception)
.endm

BUILD_HANDLER ade ade badv
@@ -93,6 +119,12 @@ SYM_FUNC_END(except_vec_cex)
BUILD_HANDLER reserved reserved none /* others */

SYM_FUNC_START(handle_sys)
- la.abs t0, handle_syscall
+ la.pcrel t0, t1, handle_syscall
jr t0
SYM_FUNC_END(handle_sys)
+
+SYM_DATA_START(rel_handle_sys)
+LONG 1
+LONG 0
+LONG handle_syscall
+SYM_DATA_END(rel_handle_sys)
diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c
index c38a146a973b..7e073854f493 100644
--- a/arch/loongarch/kernel/traps.c
+++ b/arch/loongarch/kernel/traps.c
@@ -62,6 +62,127 @@ extern asmlinkage void handle_reserved(void);
extern asmlinkage void handle_watch(void);
extern asmlinkage void handle_vint(void);

+struct handler_reloc_entry {
+ unsigned long offset;
+ unsigned long sym;
+};
+
+struct handler_reloc {
+ unsigned long cnt;
+ struct handler_reloc_entry entries[];
+};
+
+extern struct handler_reloc rel_handle_tlb_load;
+extern struct handler_reloc rel_handle_tlb_store;
+extern struct handler_reloc rel_handle_tlb_modify;
+extern struct handler_reloc rel_handle_tlb_protect;
+extern struct handler_reloc rel_handle_ade;
+extern struct handler_reloc rel_handle_ale;
+extern struct handler_reloc rel_handle_sys;
+extern struct handler_reloc rel_handle_bp;
+extern struct handler_reloc rel_handle_ri;
+extern struct handler_reloc rel_handle_fpu;
+extern struct handler_reloc rel_handle_lsx;
+extern struct handler_reloc rel_handle_lasx;
+extern struct handler_reloc rel_handle_fpe;
+extern struct handler_reloc rel_handle_lbt;
+extern struct handler_reloc rel_handle_watch;
+extern struct handler_reloc rel_handle_reserved;
+extern struct handler_reloc rel_handle_vint;
+
+struct handler_reloc *eentry_reloc[128] = {
+ [0] = NULL, /* merr handler */
+ [EXCCODE_TLBL] = &rel_handle_tlb_load,
+ [EXCCODE_TLBS] = &rel_handle_tlb_store,
+ [EXCCODE_TLBI] = &rel_handle_tlb_load,
+ [EXCCODE_TLBM] = &rel_handle_tlb_modify,
+ [EXCCODE_TLBNR] = &rel_handle_tlb_protect,
+ [EXCCODE_TLBNX] = &rel_handle_tlb_protect,
+ [EXCCODE_TLBPE] = &rel_handle_tlb_protect,
+ [EXCCODE_ADE] = &rel_handle_ade,
+ [EXCCODE_ALE] = &rel_handle_ale,
+ [EXCCODE_SYS] = &rel_handle_sys,
+ [EXCCODE_BP] = &rel_handle_bp,
+ [EXCCODE_INE] = &rel_handle_ri,
+ [EXCCODE_IPE] = &rel_handle_ri,
+ [EXCCODE_FPDIS] = &rel_handle_fpu,
+ [EXCCODE_LSXDIS] = &rel_handle_lsx,
+ [EXCCODE_LASXDIS] = &rel_handle_lasx,
+ [EXCCODE_FPE] = &rel_handle_fpe,
+ [EXCCODE_BTDIS] = &rel_handle_lbt,
+ [EXCCODE_WATCH] = &rel_handle_watch,
+ [(EXCCODE_WATCH + 1) ... (EXCCODE_INT_START - 1)] = &rel_handle_reserved,
+ [EXCCODE_INT_START ... (EXCCODE_INT_END - 1)] = &rel_handle_vint,
+};
+
+void reloc_handler(unsigned long handler, struct handler_reloc *rel)
+{
+ if (!rel)
+ return;
+
+ for (unsigned long i = 0; i < rel->cnt; i++) {
+ unsigned long pc = handler + rel->entries[i].offset;
+ union loongarch_instruction *insn =
+ (union loongarch_instruction *)pc;
+ u32 imm[4];
+ unsigned long v = rel->entries[i].sym;
+
+ /* GNU as >= 2.40 uses pcalau12i for la.pcrel, but GNU ld <= 2.39
+ * uses pcaddu12i.
+ */
+ if (insn->reg1i20_format.opcode == pcalau12i_op) {
+ /* Use s32 deliberately for sign extension. */
+ s32 offset_hi20 = ((v + 0x800) & ~0xfff) -
+ (pc & ~0xfff);
+ unsigned long anchor = (pc & ~0xfff) + offset_hi20;
+ unsigned long offset_rem = v - anchor;
+
+ imm[0] = (offset_hi20 >> 12) & 0xfffff;
+ imm[1] = v & 0xfff;
+ imm[2] = (offset_rem >> 32) & 0xfffff;
+ imm[3] = offset_rem >> 52;
+ } else if (insn->reg1i20_format.opcode == pcaddu12i_op) {
+ /* Use s32 deliberately for sign extension. */
+ s32 offset_lo = v - pc;
+ unsigned long offset_hi = v - pc - offset_lo;
+
+ imm[0] = (offset_lo >> 12) & 0xfffff;
+ imm[1] = offset_lo & 0xfff;
+ imm[2] = (offset_hi >> 32) & 0xfffff;
+ imm[3] = offset_hi >> 52;
+ } else
+ panic("Cannot fixup la.pcrel for exception handler at %lu: unexpected instruction %d!",
+ pc, insn->word);
+
+ insn[0].reg1i20_format.immediate = imm[0];
+ insn[1].reg2i12_format.immediate = imm[1];
+ insn[2].reg1i20_format.immediate = imm[2];
+ insn[3].reg2i12_format.immediate = imm[3];
+ }
+}
+
+/* Install CPU exception handler */
+static void do_set_handler(unsigned long exccode, void *addr,
+ struct handler_reloc *rel)
+{
+ unsigned long dest_addr = eentry + exccode * VECSIZE;
+
+ memcpy((void *)dest_addr, addr, VECSIZE);
+ reloc_handler(dest_addr, rel);
+ local_flush_icache_range(dest_addr, dest_addr + VECSIZE);
+}
+
+/* Install CPU exception handler, with the reloc table from eentry_reloc */
+void set_handler(unsigned long exccode, void *addr)
+{
+ do_set_handler(exccode, addr, eentry_reloc[exccode]);
+}
+
+static void set_handler_reserved(unsigned long exccode)
+{
+ do_set_handler(exccode, handle_reserved, &rel_handle_reserved);
+}
+
static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,
const char *loglvl, bool user)
{
@@ -704,19 +825,12 @@ void per_cpu_trap_init(int cpu)
/* Initialise exception handlers */
if (cpu == 0)
for (i = 0; i < 64; i++)
- set_handler(i * VECSIZE, handle_reserved, VECSIZE);
+ set_handler_reserved(i);

tlb_init(cpu);
cpu_cache_init();
}

-/* Install CPU exception handler */
-void set_handler(unsigned long offset, void *addr, unsigned long size)
-{
- memcpy((void *)(eentry + offset), addr, size);
- local_flush_icache_range(eentry + offset, eentry + offset + size);
-}
-
static const char panic_null_cerr[] =
"Trying to set NULL cache error exception handler\n";

@@ -741,20 +855,20 @@ void __init trap_init(void)

/* Set interrupt vector handler */
for (i = EXCCODE_INT_START; i < EXCCODE_INT_END; i++)
- set_handler(i * VECSIZE, handle_vint, VECSIZE);
-
- set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE);
- set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE);
- set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE);
- set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE);
- set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE);
- set_handler(EXCCODE_IPE * VECSIZE, handle_ri, VECSIZE);
- set_handler(EXCCODE_FPDIS * VECSIZE, handle_fpu, VECSIZE);
- set_handler(EXCCODE_LSXDIS * VECSIZE, handle_lsx, VECSIZE);
- set_handler(EXCCODE_LASXDIS * VECSIZE, handle_lasx, VECSIZE);
- set_handler(EXCCODE_FPE * VECSIZE, handle_fpe, VECSIZE);
- set_handler(EXCCODE_BTDIS * VECSIZE, handle_lbt, VECSIZE);
- set_handler(EXCCODE_WATCH * VECSIZE, handle_watch, VECSIZE);
+ set_handler(i, handle_vint);
+
+ set_handler(EXCCODE_ADE, handle_ade);
+ set_handler(EXCCODE_ALE, handle_ale);
+ set_handler(EXCCODE_SYS, handle_sys);
+ set_handler(EXCCODE_BP, handle_bp);
+ set_handler(EXCCODE_INE, handle_ri);
+ set_handler(EXCCODE_IPE, handle_ri);
+ set_handler(EXCCODE_FPDIS, handle_fpu);
+ set_handler(EXCCODE_LSXDIS, handle_lsx);
+ set_handler(EXCCODE_LASXDIS, handle_lasx);
+ set_handler(EXCCODE_FPE, handle_fpe);
+ set_handler(EXCCODE_BTDIS, handle_lbt);
+ set_handler(EXCCODE_WATCH, handle_watch);

cache_error_setup();

diff --git a/arch/loongarch/mm/tlb.c b/arch/loongarch/mm/tlb.c
index 8bad6b0cff59..6f70aab7202a 100644
--- a/arch/loongarch/mm/tlb.c
+++ b/arch/loongarch/mm/tlb.c
@@ -253,7 +253,6 @@ static void output_pgtable_bits_defines(void)
#ifdef CONFIG_NUMA
unsigned long pcpu_handlers[NR_CPUS];
#endif
-extern long exception_handlers[VECSIZE * 128 / sizeof(long)];

void setup_tlb_handler(int cpu)
{
@@ -264,19 +263,20 @@ void setup_tlb_handler(int cpu)
if (cpu == 0) {
memcpy((void *)tlbrentry, handle_tlb_refill, 0x80);
local_flush_icache_range(tlbrentry, tlbrentry + 0x80);
- set_handler(EXCCODE_TLBI * VECSIZE, handle_tlb_load, VECSIZE);
- set_handler(EXCCODE_TLBL * VECSIZE, handle_tlb_load, VECSIZE);
- set_handler(EXCCODE_TLBS * VECSIZE, handle_tlb_store, VECSIZE);
- set_handler(EXCCODE_TLBM * VECSIZE, handle_tlb_modify, VECSIZE);
- set_handler(EXCCODE_TLBNR * VECSIZE, handle_tlb_protect, VECSIZE);
- set_handler(EXCCODE_TLBNX * VECSIZE, handle_tlb_protect, VECSIZE);
- set_handler(EXCCODE_TLBPE * VECSIZE, handle_tlb_protect, VECSIZE);
+ set_handler(EXCCODE_TLBI, handle_tlb_load);
+ set_handler(EXCCODE_TLBL, handle_tlb_load);
+ set_handler(EXCCODE_TLBS, handle_tlb_store);
+ set_handler(EXCCODE_TLBM, handle_tlb_modify);
+ set_handler(EXCCODE_TLBNR, handle_tlb_protect);
+ set_handler(EXCCODE_TLBNX, handle_tlb_protect);
+ set_handler(EXCCODE_TLBPE, handle_tlb_protect);
}
#ifdef CONFIG_NUMA
else {
void *addr;
+ unsigned long addr_ul;
struct page *page;
- const int vec_sz = sizeof(exception_handlers);
+ const int vec_sz = VECSIZE * 128;

if (pcpu_handlers[cpu])
return;
@@ -286,8 +286,11 @@ void setup_tlb_handler(int cpu)
return;

addr = page_address(page);
+ addr_ul = (unsigned long)addr;
pcpu_handlers[cpu] = (unsigned long)addr;
- memcpy((void *)addr, (void *)eentry, vec_sz);
+ memcpy(addr, (void *)eentry, vec_sz);
+ for (unsigned long i = 0; i < 128; i++)
+ reloc_handler(addr_ul + i * VECSIZE, eentry_reloc[i]);
local_flush_icache_range((unsigned long)addr, (unsigned long)addr + vec_sz);
csr_write64(pcpu_handlers[cpu], LOONGARCH_CSR_EENTRY);
csr_write64(pcpu_handlers[cpu], LOONGARCH_CSR_MERRENTRY);
diff --git a/arch/loongarch/mm/tlbex.S b/arch/loongarch/mm/tlbex.S
index 3dd2a9615cd9..044c2190771a 100644
--- a/arch/loongarch/mm/tlbex.S
+++ b/arch/loongarch/mm/tlbex.S
@@ -39,11 +39,21 @@ SYM_FUNC_START(handle_tlb_protect)
move a1, zero
csrrd a2, LOONGARCH_CSR_BADV
REG_S a2, sp, PT_BVADDR
- la.abs t0, do_page_fault
+1: la.pcrel t0, t1, do_page_fault
jirl ra, t0, 0
RESTORE_ALL_AND_RET
SYM_FUNC_END(handle_tlb_protect)

+SYM_DATA_START(rel_handle_tlb_protect)
+ LONG 2
+
+ LONG 514b - handle_tlb_protect
+ LONG kernelsp
+
+ LONG 1b - handle_tlb_protect
+ LONG do_page_fault
+SYM_DATA_END(rel_handle_tlb_protect)
+
SYM_FUNC_START(handle_tlb_load)
csrwr t0, EXCEPTION_KS0
csrwr t1, EXCEPTION_KS1
@@ -115,7 +125,8 @@ smp_pgtable_change_load:

#ifdef CONFIG_64BIT
vmalloc_load:
- la.abs t1, swapper_pg_dir
+ /* The first insn of vmalloc_done_load overwrites ra */
+1: la.pcrel t1, ra, swapper_pg_dir
b vmalloc_done_load
#endif

@@ -186,10 +197,24 @@ tlb_huge_update_load:
nopage_tlb_load:
dbar 0
csrrd ra, EXCEPTION_KS2
- la.abs t0, tlb_do_page_fault_0
+2: la.pcrel t0, t1, tlb_do_page_fault_0
jr t0
SYM_FUNC_END(handle_tlb_load)

+SYM_DATA_START(rel_handle_tlb_load)
+#ifdef CONFIG_64BIT
+ LONG 2
+
+ LONG 1b - handle_tlb_load
+ LONG swapper_pg_dir
+#else
+ LONG 1
+#endif
+
+ LONG 2b - handle_tlb_load
+ LONG tlb_do_page_fault_0
+SYM_DATA_END(rel_handle_tlb_load)
+
SYM_FUNC_START(handle_tlb_store)
csrwr t0, EXCEPTION_KS0
csrwr t1, EXCEPTION_KS1
@@ -262,7 +287,8 @@ smp_pgtable_change_store:

#ifdef CONFIG_64BIT
vmalloc_store:
- la.abs t1, swapper_pg_dir
+ /* The first insn of vmalloc_done_store overwrites ra */
+1: la.pcrel t1, ra, swapper_pg_dir
b vmalloc_done_store
#endif

@@ -335,10 +361,24 @@ tlb_huge_update_store:
nopage_tlb_store:
dbar 0
csrrd ra, EXCEPTION_KS2
- la.abs t0, tlb_do_page_fault_1
+2: la.pcrel t0, t1, tlb_do_page_fault_1
jr t0
SYM_FUNC_END(handle_tlb_store)

+SYM_DATA_START(rel_handle_tlb_store)
+#ifdef CONFIG_64BIT
+ LONG 2
+
+ LONG 1b - handle_tlb_store
+ LONG swapper_pg_dir
+#else
+ LONG 1
+#endif
+
+ LONG 2b - handle_tlb_store
+ LONG tlb_do_page_fault_1
+SYM_DATA_END(rel_handle_tlb_store)
+
SYM_FUNC_START(handle_tlb_modify)
csrwr t0, EXCEPTION_KS0
csrwr t1, EXCEPTION_KS1
@@ -410,7 +450,8 @@ smp_pgtable_change_modify:

#ifdef CONFIG_64BIT
vmalloc_modify:
- la.abs t1, swapper_pg_dir
+ /* The first insn of vmalloc_done_modify overwrites ra */
+1: la.pcrel t1, ra, swapper_pg_dir
b vmalloc_done_modify
#endif

@@ -482,10 +523,24 @@ tlb_huge_update_modify:
nopage_tlb_modify:
dbar 0
csrrd ra, EXCEPTION_KS2
- la.abs t0, tlb_do_page_fault_1
+2: la.pcrel t0, t1, tlb_do_page_fault_1
jr t0
SYM_FUNC_END(handle_tlb_modify)

+SYM_DATA_START(rel_handle_tlb_modify)
+#ifdef CONFIG_64BIT
+ LONG 2
+
+ LONG 1b - handle_tlb_modify
+ LONG swapper_pg_dir
+#else
+ LONG 1
+#endif
+
+ LONG 2b - handle_tlb_modify
+ LONG tlb_do_page_fault_1
+SYM_DATA_END(rel_handle_tlb_modify)
+
SYM_FUNC_START(handle_tlb_refill)
csrwr t0, LOONGARCH_CSR_TLBRSAVE
csrrd t0, LOONGARCH_CSR_PGD
--
2.37.3


2023-02-10 08:48:24

by Youling Tang

[permalink] [raw]
Subject: [PATCH v4 4/5] LoongArch: Add support for kernel relocation

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 <[email protected]>
Signed-off-by: Xi Ruoyao <[email protected]> # Use arch_initcall
---
arch/loongarch/Kconfig | 15 ++++++
arch/loongarch/Makefile | 5 ++
arch/loongarch/kernel/Makefile | 2 +
arch/loongarch/kernel/head.S | 5 ++
arch/loongarch/kernel/relocate.c | 78 +++++++++++++++++++++++++++++
arch/loongarch/kernel/vmlinux.lds.S | 11 +++-
6 files changed, 114 insertions(+), 2 deletions(-)
create mode 100644 arch/loongarch/kernel/relocate.c

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 9cc8b84f7eb0..089a4695b1b3 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -48,6 +48,7 @@ config LOONGARCH
select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_SUPPORTS_HUGETLBFS
select ARCH_SUPPORTS_NUMA_BALANCING
+ select SYS_SUPPORTS_RELOCATABLE
select ARCH_USE_BUILTIN_BSWAP
select ARCH_USE_CMPXCHG_LOCKREF
select ARCH_USE_QUEUED_RWLOCKS
@@ -229,6 +230,11 @@ config SCHED_OMIT_FRAME_POINTER
config AS_HAS_EXPLICIT_RELOCS
def_bool $(as-instr,x:pcalau12i \$t0$(comma)%pc_hi20(x))

+config SYS_SUPPORTS_RELOCATABLE
+ bool
+ help
+ Selected if the platform supports relocating the kernel.
+
menu "Kernel type and options"

source "kernel/Kconfig.hz"
@@ -474,6 +480,15 @@ config PHYSICAL_START
specified in the "crashkernel=YM@XM" command line boot parameter
passed to the panic-ed kernel).

+config RELOCATABLE
+ bool "Relocatable kernel"
+ depends on SYS_SUPPORTS_RELOCATABLE
+ help
+ This builds the kernel as a Position Independent Executable (PIE),
+ which retains all relocation metadata required to relocate the
+ kernel binary at runtime to a different virtual address than the
+ address it was linked at.
+
config SECCOMP
bool "Enable seccomp to safely compute untrusted bytecode"
depends on PROC_FS
diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
index 4402387d2755..27b5a70ff31c 100644
--- a/arch/loongarch/Makefile
+++ b/arch/loongarch/Makefile
@@ -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)
+LDFLAGS_vmlinux += -static -pie --no-dynamic-linker -z notext
+KBUILD_CFLAGS_KERNEL += -fPIE
+endif
+
cflags-y += -ffreestanding
cflags-y += $(call cc-option, -mno-check-zero-division)

diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile
index c8cfbd562921..3341dd5f0926 100644
--- a/arch/loongarch/kernel/Makefile
+++ b/arch/loongarch/kernel/Makefile
@@ -31,6 +31,8 @@ endif
obj-$(CONFIG_MODULES) += module.o module-sections.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o

+obj-$(CONFIG_RELOCATABLE) += relocate.o
+
obj-$(CONFIG_PROC_FS) += proc.o

obj-$(CONFIG_SMP) += smp.o
diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
index d2ac26b5b22b..499edc80d8ab 100644
--- a/arch/loongarch/kernel/head.S
+++ b/arch/loongarch/kernel/head.S
@@ -86,6 +86,11 @@ SYM_CODE_START(kernel_entry) # kernel entry point
PTR_ADD sp, sp, tp
set_saved_sp sp, t0, t1

+#ifdef CONFIG_RELOCATABLE
+ /* Apply the relocations */
+ 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
index 000000000000..91ce92433bab
--- /dev/null
+++ b/arch/loongarch/kernel/relocate.c
@@ -0,0 +1,78 @@
+// 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/sections.h>
+
+#define RELOCATED(x) ((void *)((long)x + reloc_offset))
+
+extern long __rela_dyn_start;
+extern long __rela_dyn_end;
+
+static unsigned long reloc_offset;
+
+void __init relocate_kernel(void)
+{
+ reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
+
+ if (reloc_offset) {
+ Elf64_Rela *rela, *rela_end;
+ rela = (Elf64_Rela *)&__rela_dyn_start;
+ 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;
+ }
+ }
+}
+
+/*
+ * Show relocation information on panic.
+ */
+static void show_kernel_relocation(const char *level)
+{
+ if (reloc_offset > 0) {
+ printk(level);
+ pr_cont("Kernel relocated offset @ 0x%lx\n", reloc_offset);
+ pr_cont(" .text @ 0x%lx\n", (unsigned long)&_text);
+ pr_cont(" .data @ 0x%lx\n", (unsigned long)&_sdata);
+ pr_cont(" .bss @ 0x%lx\n", (unsigned long)&__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);
diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S
index 733b16e8d55d..aec0b6567d24 100644
--- a/arch/loongarch/kernel/vmlinux.lds.S
+++ b/arch/loongarch/kernel/vmlinux.lds.S
@@ -70,6 +70,8 @@ SECTIONS
.plt : ALIGN(16) { *(.plt) }
.got.plt : ALIGN(16) { *(.got.plt) }

+ .data.rel : { *(.data.rel*) }
+
. = ALIGN(PECOFF_SEGMENT_ALIGN);
__init_begin = .;
__inittext_begin = .;
@@ -93,8 +95,6 @@ SECTIONS
PERCPU_SECTION(1 << CONFIG_L1_CACHE_SHIFT)
#endif

- .rela.dyn : ALIGN(8) { *(.rela.dyn) *(.rela*) }
-
.init.bss : {
*(.init.bss)
}
@@ -107,6 +107,12 @@ SECTIONS
RO_DATA(4096)
RW_DATA(1 << CONFIG_L1_CACHE_SHIFT, PAGE_SIZE, THREAD_SIZE)

+ .rela.dyn : ALIGN(8) {
+ __rela_dyn_start = .;
+ *(.rela.dyn) *(.rela*)
+ __rela_dyn_end = .;
+ }
+
.sdata : {
*(.sdata)
}
@@ -133,6 +139,7 @@ SECTIONS

DISCARDS
/DISCARD/ : {
+ *(.dynamic .dynsym .dynstr .hash .gnu.hash)
*(.gnu.attributes)
*(.options)
*(.eh_frame)
--
2.37.3


2023-02-10 08:48:28

by Youling Tang

[permalink] [raw]
Subject: [PATCH v4 5/5] LoongArch: Add support for kernel address space layout randomization (KASLR)

This patch adds support for relocating the kernel to a random address.

Entropy is derived from the banner, which will change every build and
random_get_entropy() which should provide additional runtime entropy.

The kernel is relocated by up to RANDOMIZE_BASE_MAX_OFFSET bytes from
its link address. Because relocation happens so early in the kernel boot,
the amount of physical memory has not yet been determined. This means
the only way to limit relocation within the available memory is via
Kconfig. Limit the maximum value of RANDOMIZE_BASE_MAX_OFFSET to
256M(0x10000000) because our memory layout has many holes.

Signed-off-by: Youling Tang <[email protected]>
Signed-off-by: Xi Ruoyao <[email protected]> # Fix compiler warnings
---
arch/loongarch/Kconfig | 23 +++++
arch/loongarch/kernel/head.S | 14 ++-
arch/loongarch/kernel/relocate.c | 143 ++++++++++++++++++++++++++++++-
3 files changed, 176 insertions(+), 4 deletions(-)

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 089a4695b1b3..f0a070bd7254 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -489,6 +489,29 @@ config RELOCATABLE
kernel binary at runtime to a different virtual address than the
address it was linked at.

+config RANDOMIZE_BASE
+ bool "Randomize the address of the kernel image (KASLR)"
+ depends on RELOCATABLE
+ help
+ Randomizes the physical and virtual address at which the
+ kernel image is loaded, as a security feature that
+ deters exploit attempts relying on knowledge of the location
+ of kernel internals.
+
+ The kernel will be offset by up to RANDOMIZE_BASE_MAX_OFFSET.
+
+ If unsure, say N.
+
+config RANDOMIZE_BASE_MAX_OFFSET
+ hex "Maximum KASLR offset" if EXPERT
+ depends on RANDOMIZE_BASE
+ range 0x0 0x10000000 if 64BIT
+ default "0x01000000"
+ help
+ When KASLR is active, this provides the maximum offset that will
+ be applied to the kernel image.
+
+
config SECCOMP
bool "Enable seccomp to safely compute untrusted bytecode"
depends on PROC_FS
diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
index 499edc80d8ab..b12f459ad73a 100644
--- a/arch/loongarch/kernel/head.S
+++ b/arch/loongarch/kernel/head.S
@@ -87,10 +87,22 @@ SYM_CODE_START(kernel_entry) # kernel entry point
set_saved_sp sp, t0, t1

#ifdef CONFIG_RELOCATABLE
+#ifdef CONFIG_RANDOMIZE_BASE
+ bl do_kaslr
+
+ /* Repoint the sp into the new kernel image */
+ PTR_LI sp, (_THREAD_SIZE - PT_SIZE)
+ PTR_ADD sp, sp, tp
+ set_saved_sp sp, t0, t1
+
+ /* do_kaslr returns the new kernel image entry point */
+ jr a0
+ ASM_BUG()
+#else
/* Apply the relocations */
bl relocate_kernel
#endif
-
+#endif
bl start_kernel
ASM_BUG()

diff --git a/arch/loongarch/kernel/relocate.c b/arch/loongarch/kernel/relocate.c
index 91ce92433bab..5266f23a3006 100644
--- a/arch/loongarch/kernel/relocate.c
+++ b/arch/loongarch/kernel/relocate.c
@@ -9,19 +9,21 @@
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/panic_notifier.h>
+#include <linux/start_kernel.h>
+#include <asm/bootinfo.h>
+#include <asm/early_ioremap.h>
#include <asm/sections.h>

#define RELOCATED(x) ((void *)((long)x + reloc_offset))
+#define RELOCATED_KASLR(x) ((void *)((long)x + offset))

extern long __rela_dyn_start;
extern long __rela_dyn_end;

static unsigned long reloc_offset;

-void __init relocate_kernel(void)
+static inline __init void relocate_relative(void)
{
- reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
-
if (reloc_offset) {
Elf64_Rela *rela, *rela_end;
rela = (Elf64_Rela *)&__rela_dyn_start;
@@ -43,6 +45,141 @@ void __init relocate_kernel(void)
}
}

+#ifdef CONFIG_RANDOMIZE_BASE
+static inline __init unsigned long rotate_xor(unsigned long hash,
+ const void *area, size_t size)
+{
+ size_t i;
+ unsigned long *ptr = (unsigned long *)area;
+
+ for (i = 0; i < size / sizeof(hash); i++) {
+ /* Rotate by odd number of bits and XOR. */
+ hash = (hash << ((sizeof(hash) * 8) - 7)) | (hash >> 7);
+ hash ^= ptr[i];
+ }
+
+ return hash;
+}
+
+static inline __init unsigned long get_random_boot(void)
+{
+ unsigned long entropy = random_get_entropy();
+ unsigned long hash = 0;
+
+ /* Attempt to create a simple but unpredictable starting entropy. */
+ hash = rotate_xor(hash, linux_banner, strlen(linux_banner));
+
+ /* Add in any runtime entropy we can get */
+ hash = rotate_xor(hash, &entropy, sizeof(entropy));
+
+ return hash;
+}
+
+static inline __init bool kaslr_disabled(void)
+{
+ char *str;
+
+ str = strstr(boot_command_line, "nokaslr");
+ if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' '))
+ return true;
+
+ return false;
+}
+
+/* Choose a new address for the kernel */
+static inline void __init *determine_relocation_address(void)
+{
+ unsigned long kernel_length;
+ void *dest = _text;
+ unsigned long offset;
+
+ if (kaslr_disabled())
+ return dest;
+
+ kernel_length = (long)_end - (long)_text;
+
+ offset = get_random_boot() << 16;
+ offset &= (CONFIG_RANDOMIZE_BASE_MAX_OFFSET - 1);
+ if (offset < kernel_length)
+ offset += ALIGN(kernel_length, 0xffff);
+
+ return RELOCATED_KASLR(dest);
+}
+
+static inline int __init relocation_addr_valid(void *loc_new)
+{
+ if ((unsigned long)loc_new & 0x00000ffff) {
+ /* Inappropriately aligned new location */
+ return 0;
+ }
+ if ((unsigned long)loc_new < (unsigned long)_end) {
+ /* New location overlaps original kernel */
+ return 0;
+ }
+ return 1;
+}
+
+static inline void __init update_reloc_offset(unsigned long *addr, long offset)
+{
+ unsigned long *new_addr = (unsigned long *)RELOCATED_KASLR(addr);
+
+ *new_addr = (unsigned long)offset;
+}
+
+void *__init do_kaslr(void)
+{
+ void *loc_new;
+ unsigned long kernel_length;
+ long offset = 0;
+ /* Default to original kernel entry point */
+ void *kernel_entry = start_kernel;
+ char *cmdline = early_ioremap(fw_arg1, COMMAND_LINE_SIZE);
+
+ /* Boot command line was passed in fw_arg1 */
+ strscpy(boot_command_line, cmdline, COMMAND_LINE_SIZE);
+
+ kernel_length = (long)(_end) - (long)(_text);
+
+ loc_new = determine_relocation_address();
+
+ /* Sanity check relocation address */
+ if (relocation_addr_valid(loc_new))
+ offset = (unsigned long)loc_new - (unsigned long)(_text);
+
+ if (offset) {
+ /* Copy the kernel to it's new location */
+ memcpy(loc_new, _text, kernel_length);
+
+ /* Sync the caches ready for execution of new kernel */
+ __asm__ __volatile__ (
+ "ibar 0 \t\n"
+ "dbar 0 \t\n");
+
+ reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
+ reloc_offset += offset;
+
+ relocate_relative();
+
+ /* The current thread is now within the relocated image */
+ __current_thread_info = RELOCATED_KASLR(__current_thread_info);
+
+ /* Return the new kernel's entry point */
+ kernel_entry = RELOCATED_KASLR(start_kernel);
+
+ update_reloc_offset(&reloc_offset, offset);
+ }
+
+ return kernel_entry;
+}
+#endif
+
+void __init relocate_kernel(void)
+{
+ reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
+
+ relocate_relative();
+}
+
/*
* Show relocation information on panic.
*/
--
2.37.3


2023-02-10 09:06:03

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH v4 1/5] LoongArch: Use la.pcrel instead of la.abs when it's trivially possible

Hi, Youling,

On Fri, Feb 10, 2023 at 4:47 PM Youling Tang <[email protected]> wrote:
>
> From: Xi Ruoyao <[email protected]>
>
> Let's start to kill la.abs inpreparation for the subsequent support of the
> PIE kernel.
>
> Signed-off-by: Xi Ruoyao <[email protected]>
> ---
> arch/loongarch/include/asm/stackframe.h | 2 +-
> arch/loongarch/include/asm/uaccess.h | 1 -
> arch/loongarch/kernel/entry.S | 2 +-
> arch/loongarch/kernel/head.S | 2 +-
> arch/loongarch/mm/tlbex.S | 3 +--
> 5 files changed, 4 insertions(+), 6 deletions(-)
>
> diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h
> index 4ca953062b5b..7deb043ce387 100644
> --- a/arch/loongarch/include/asm/stackframe.h
> +++ b/arch/loongarch/include/asm/stackframe.h
> @@ -90,7 +90,7 @@
> .endm
>
> .macro set_saved_sp stackp temp temp2
> - la.abs \temp, kernelsp
> + la.pcrel \temp, kernelsp
> #ifdef CONFIG_SMP
> LONG_ADD \temp, \temp, u0
> #endif
> diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h
> index 255899d4a7c3..0d22991ae430 100644
> --- a/arch/loongarch/include/asm/uaccess.h
> +++ b/arch/loongarch/include/asm/uaccess.h
> @@ -22,7 +22,6 @@
> extern u64 __ua_limit;
>
> #define __UA_ADDR ".dword"
> -#define __UA_LA "la.abs"
> #define __UA_LIMIT __ua_limit
>
> /*
> diff --git a/arch/loongarch/kernel/entry.S b/arch/loongarch/kernel/entry.S
> index d53b631c9022..2566977f2f68 100644
> --- a/arch/loongarch/kernel/entry.S
> +++ b/arch/loongarch/kernel/entry.S
> @@ -20,7 +20,7 @@
> .align 5
> SYM_FUNC_START(handle_syscall)
> csrrd t0, PERCPU_BASE_KS
> - la.abs t1, kernelsp
> + la.pcrel t1, kernelsp
Retab the whole function to align the first parameter, please.

Huacai
> add.d t1, t1, t0
> move t2, sp
> ld.d sp, t1, 0
> diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
> index 57bada6b4e93..aa6181714ec3 100644
> --- a/arch/loongarch/kernel/head.S
> +++ b/arch/loongarch/kernel/head.S
> @@ -117,7 +117,7 @@ SYM_CODE_START(smpboot_entry)
> li.w t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0
> csrwr t0, LOONGARCH_CSR_EUEN
>
> - la.abs t0, cpuboot_data
> + la.pcrel t0, cpuboot_data
> ld.d sp, t0, CPU_BOOT_STACK
> ld.d tp, t0, CPU_BOOT_TINFO
>
> diff --git a/arch/loongarch/mm/tlbex.S b/arch/loongarch/mm/tlbex.S
> index 58781c6e4191..3dd2a9615cd9 100644
> --- a/arch/loongarch/mm/tlbex.S
> +++ b/arch/loongarch/mm/tlbex.S
> @@ -24,8 +24,7 @@
> move a0, sp
> REG_S a2, sp, PT_BVADDR
> li.w a1, \write
> - la.abs t0, do_page_fault
> - jirl ra, t0, 0
> + bl do_page_fault
> RESTORE_ALL_AND_RET
> SYM_FUNC_END(tlb_do_page_fault_\write)
> .endm
> --
> 2.37.3
>
>

2023-02-10 09:07:11

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH v4 4/5] LoongArch: Add support for kernel relocation

Hi, Youling,

On Fri, Feb 10, 2023 at 4:48 PM Youling Tang <[email protected]> wrote:
>
> 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 <[email protected]>
> Signed-off-by: Xi Ruoyao <[email protected]> # Use arch_initcall
> ---
> arch/loongarch/Kconfig | 15 ++++++
> arch/loongarch/Makefile | 5 ++
> arch/loongarch/kernel/Makefile | 2 +
> arch/loongarch/kernel/head.S | 5 ++
> arch/loongarch/kernel/relocate.c | 78 +++++++++++++++++++++++++++++
> arch/loongarch/kernel/vmlinux.lds.S | 11 +++-
> 6 files changed, 114 insertions(+), 2 deletions(-)
> create mode 100644 arch/loongarch/kernel/relocate.c
>
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 9cc8b84f7eb0..089a4695b1b3 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -48,6 +48,7 @@ config LOONGARCH
> select ARCH_SUPPORTS_ATOMIC_RMW
> select ARCH_SUPPORTS_HUGETLBFS
> select ARCH_SUPPORTS_NUMA_BALANCING
> + select SYS_SUPPORTS_RELOCATABLE
We don't need such a Kconfig option since it is always true.

Huacai
> select ARCH_USE_BUILTIN_BSWAP
> select ARCH_USE_CMPXCHG_LOCKREF
> select ARCH_USE_QUEUED_RWLOCKS
> @@ -229,6 +230,11 @@ config SCHED_OMIT_FRAME_POINTER
> config AS_HAS_EXPLICIT_RELOCS
> def_bool $(as-instr,x:pcalau12i \$t0$(comma)%pc_hi20(x))
>
> +config SYS_SUPPORTS_RELOCATABLE
> + bool
> + help
> + Selected if the platform supports relocating the kernel.
> +
> menu "Kernel type and options"
>
> source "kernel/Kconfig.hz"
> @@ -474,6 +480,15 @@ config PHYSICAL_START
> specified in the "crashkernel=YM@XM" command line boot parameter
> passed to the panic-ed kernel).
>
> +config RELOCATABLE
> + bool "Relocatable kernel"
> + depends on SYS_SUPPORTS_RELOCATABLE
> + help
> + This builds the kernel as a Position Independent Executable (PIE),
> + which retains all relocation metadata required to relocate the
> + kernel binary at runtime to a different virtual address than the
> + address it was linked at.
> +
> config SECCOMP
> bool "Enable seccomp to safely compute untrusted bytecode"
> depends on PROC_FS
> diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
> index 4402387d2755..27b5a70ff31c 100644
> --- a/arch/loongarch/Makefile
> +++ b/arch/loongarch/Makefile
> @@ -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)
> +LDFLAGS_vmlinux += -static -pie --no-dynamic-linker -z notext
> +KBUILD_CFLAGS_KERNEL += -fPIE
> +endif
> +
> cflags-y += -ffreestanding
> cflags-y += $(call cc-option, -mno-check-zero-division)
>
> diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile
> index c8cfbd562921..3341dd5f0926 100644
> --- a/arch/loongarch/kernel/Makefile
> +++ b/arch/loongarch/kernel/Makefile
> @@ -31,6 +31,8 @@ endif
> obj-$(CONFIG_MODULES) += module.o module-sections.o
> obj-$(CONFIG_STACKTRACE) += stacktrace.o
>
> +obj-$(CONFIG_RELOCATABLE) += relocate.o
> +
> obj-$(CONFIG_PROC_FS) += proc.o
>
> obj-$(CONFIG_SMP) += smp.o
> diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
> index d2ac26b5b22b..499edc80d8ab 100644
> --- a/arch/loongarch/kernel/head.S
> +++ b/arch/loongarch/kernel/head.S
> @@ -86,6 +86,11 @@ SYM_CODE_START(kernel_entry) # kernel entry point
> PTR_ADD sp, sp, tp
> set_saved_sp sp, t0, t1
>
> +#ifdef CONFIG_RELOCATABLE
> + /* Apply the relocations */
> + 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
> index 000000000000..91ce92433bab
> --- /dev/null
> +++ b/arch/loongarch/kernel/relocate.c
> @@ -0,0 +1,78 @@
> +// 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/sections.h>
> +
> +#define RELOCATED(x) ((void *)((long)x + reloc_offset))
> +
> +extern long __rela_dyn_start;
> +extern long __rela_dyn_end;
> +
> +static unsigned long reloc_offset;
> +
> +void __init relocate_kernel(void)
> +{
> + reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
> +
> + if (reloc_offset) {
> + Elf64_Rela *rela, *rela_end;
> + rela = (Elf64_Rela *)&__rela_dyn_start;
> + 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;
> + }
> + }
> +}
> +
> +/*
> + * Show relocation information on panic.
> + */
> +static void show_kernel_relocation(const char *level)
> +{
> + if (reloc_offset > 0) {
> + printk(level);
> + pr_cont("Kernel relocated offset @ 0x%lx\n", reloc_offset);
> + pr_cont(" .text @ 0x%lx\n", (unsigned long)&_text);
> + pr_cont(" .data @ 0x%lx\n", (unsigned long)&_sdata);
> + pr_cont(" .bss @ 0x%lx\n", (unsigned long)&__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);
> diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S
> index 733b16e8d55d..aec0b6567d24 100644
> --- a/arch/loongarch/kernel/vmlinux.lds.S
> +++ b/arch/loongarch/kernel/vmlinux.lds.S
> @@ -70,6 +70,8 @@ SECTIONS
> .plt : ALIGN(16) { *(.plt) }
> .got.plt : ALIGN(16) { *(.got.plt) }
>
> + .data.rel : { *(.data.rel*) }
> +
> . = ALIGN(PECOFF_SEGMENT_ALIGN);
> __init_begin = .;
> __inittext_begin = .;
> @@ -93,8 +95,6 @@ SECTIONS
> PERCPU_SECTION(1 << CONFIG_L1_CACHE_SHIFT)
> #endif
>
> - .rela.dyn : ALIGN(8) { *(.rela.dyn) *(.rela*) }
> -
> .init.bss : {
> *(.init.bss)
> }
> @@ -107,6 +107,12 @@ SECTIONS
> RO_DATA(4096)
> RW_DATA(1 << CONFIG_L1_CACHE_SHIFT, PAGE_SIZE, THREAD_SIZE)
>
> + .rela.dyn : ALIGN(8) {
> + __rela_dyn_start = .;
> + *(.rela.dyn) *(.rela*)
> + __rela_dyn_end = .;
> + }
> +
> .sdata : {
> *(.sdata)
> }
> @@ -133,6 +139,7 @@ SECTIONS
>
> DISCARDS
> /DISCARD/ : {
> + *(.dynamic .dynsym .dynstr .hash .gnu.hash)
> *(.gnu.attributes)
> *(.options)
> *(.eh_frame)
> --
> 2.37.3
>

2023-02-10 09:07:14

by Youling Tang

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] LoongArch: Add support for kernel address space layout randomization (KASLR)



On 02/10/2023 04:47 PM, Youling Tang wrote:
> This patch adds support for relocating the kernel to a random address.
>
> Entropy is derived from the banner, which will change every build and
> random_get_entropy() which should provide additional runtime entropy.
>
> The kernel is relocated by up to RANDOMIZE_BASE_MAX_OFFSET bytes from
> its link address. Because relocation happens so early in the kernel boot,
> the amount of physical memory has not yet been determined. This means
> the only way to limit relocation within the available memory is via
> Kconfig. Limit the maximum value of RANDOMIZE_BASE_MAX_OFFSET to
> 256M(0x10000000) because our memory layout has many holes.
>
> Signed-off-by: Youling Tang <[email protected]>
> Signed-off-by: Xi Ruoyao <[email protected]> # Fix compiler warnings
> ---
> arch/loongarch/Kconfig | 23 +++++
> arch/loongarch/kernel/head.S | 14 ++-
> arch/loongarch/kernel/relocate.c | 143 ++++++++++++++++++++++++++++++-
> 3 files changed, 176 insertions(+), 4 deletions(-)
>
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 089a4695b1b3..f0a070bd7254 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -489,6 +489,29 @@ config RELOCATABLE
> kernel binary at runtime to a different virtual address than the
> address it was linked at.
>
> +config RANDOMIZE_BASE
> + bool "Randomize the address of the kernel image (KASLR)"
> + depends on RELOCATABLE
> + help
> + Randomizes the physical and virtual address at which the
> + kernel image is loaded, as a security feature that
> + deters exploit attempts relying on knowledge of the location
> + of kernel internals.
> +
> + The kernel will be offset by up to RANDOMIZE_BASE_MAX_OFFSET.
> +
> + If unsure, say N.
> +
> +config RANDOMIZE_BASE_MAX_OFFSET
> + hex "Maximum KASLR offset" if EXPERT
> + depends on RANDOMIZE_BASE
> + range 0x0 0x10000000 if 64BIT
> + default "0x01000000"
> + help
> + When KASLR is active, this provides the maximum offset that will
> + be applied to the kernel image.
> +
> +
> config SECCOMP
> bool "Enable seccomp to safely compute untrusted bytecode"
> depends on PROC_FS
> diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
> index 499edc80d8ab..b12f459ad73a 100644
> --- a/arch/loongarch/kernel/head.S
> +++ b/arch/loongarch/kernel/head.S
> @@ -87,10 +87,22 @@ SYM_CODE_START(kernel_entry) # kernel entry point
> set_saved_sp sp, t0, t1
>
> #ifdef CONFIG_RELOCATABLE
> +#ifdef CONFIG_RANDOMIZE_BASE
> + bl do_kaslr
> +
> + /* Repoint the sp into the new kernel image */
> + PTR_LI sp, (_THREAD_SIZE - PT_SIZE)
> + PTR_ADD sp, sp, tp
> + set_saved_sp sp, t0, t1
> +
> + /* do_kaslr returns the new kernel image entry point */
> + jr a0
> + ASM_BUG()
> +#else
> /* Apply the relocations */
> bl relocate_kernel
> #endif
> -
> +#endif
> bl start_kernel
> ASM_BUG()
>
> diff --git a/arch/loongarch/kernel/relocate.c b/arch/loongarch/kernel/relocate.c
> index 91ce92433bab..5266f23a3006 100644
> --- a/arch/loongarch/kernel/relocate.c
> +++ b/arch/loongarch/kernel/relocate.c
> @@ -9,19 +9,21 @@
> #include <linux/kernel.h>
> #include <linux/printk.h>
> #include <linux/panic_notifier.h>
> +#include <linux/start_kernel.h>
> +#include <asm/bootinfo.h>
> +#include <asm/early_ioremap.h>
> #include <asm/sections.h>
>
> #define RELOCATED(x) ((void *)((long)x + reloc_offset))
> +#define RELOCATED_KASLR(x) ((void *)((long)x + offset))
>
> extern long __rela_dyn_start;
> extern long __rela_dyn_end;
>
> static unsigned long reloc_offset;
>
> -void __init relocate_kernel(void)
> +static inline __init void relocate_relative(void)
> {
> - reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
> -
> if (reloc_offset) {
> Elf64_Rela *rela, *rela_end;
> rela = (Elf64_Rela *)&__rela_dyn_start;
> @@ -43,6 +45,141 @@ void __init relocate_kernel(void)
> }
> }
>
> +#ifdef CONFIG_RANDOMIZE_BASE
> +static inline __init unsigned long rotate_xor(unsigned long hash,
> + const void *area, size_t size)
> +{
> + size_t i;
> + unsigned long *ptr = (unsigned long *)area;
> +
> + for (i = 0; i < size / sizeof(hash); i++) {
> + /* Rotate by odd number of bits and XOR. */
> + hash = (hash << ((sizeof(hash) * 8) - 7)) | (hash >> 7);
> + hash ^= ptr[i];
> + }
> +
> + return hash;
> +}
> +
> +static inline __init unsigned long get_random_boot(void)
> +{
> + unsigned long entropy = random_get_entropy();
> + unsigned long hash = 0;
> +
> + /* Attempt to create a simple but unpredictable starting entropy. */
> + hash = rotate_xor(hash, linux_banner, strlen(linux_banner));
> +
> + /* Add in any runtime entropy we can get */
> + hash = rotate_xor(hash, &entropy, sizeof(entropy));
> +
> + return hash;
> +}
> +
> +static inline __init bool kaslr_disabled(void)
> +{
> + char *str;
> +
> + str = strstr(boot_command_line, "nokaslr");
> + if (str == boot_command_line || (str > boot_command_line && *(str - 1) == ' '))
> + return true;
> +
> + return false;
> +}
> +
> +/* Choose a new address for the kernel */
> +static inline void __init *determine_relocation_address(void)
> +{
> + unsigned long kernel_length;
> + void *dest = _text;
> + unsigned long offset;
> +
> + if (kaslr_disabled())
> + return dest;
> +
> + kernel_length = (long)_end - (long)_text;
> +
> + offset = get_random_boot() << 16;
> + offset &= (CONFIG_RANDOMIZE_BASE_MAX_OFFSET - 1);
> + if (offset < kernel_length)
> + offset += ALIGN(kernel_length, 0xffff);
> +
> + return RELOCATED_KASLR(dest);
> +}
> +
> +static inline int __init relocation_addr_valid(void *loc_new)
> +{
> + if ((unsigned long)loc_new & 0x00000ffff) {
> + /* Inappropriately aligned new location */
> + return 0;
> + }
> + if ((unsigned long)loc_new < (unsigned long)_end) {
> + /* New location overlaps original kernel */
> + return 0;
> + }
> + return 1;
> +}
> +
> +static inline void __init update_reloc_offset(unsigned long *addr, long offset)
> +{
> + unsigned long *new_addr = (unsigned long *)RELOCATED_KASLR(addr);
> +
> + *new_addr = (unsigned long)offset;
> +}
> +
> +void *__init do_kaslr(void)
> +{
> + void *loc_new;
> + unsigned long kernel_length;
> + long offset = 0;
> + /* Default to original kernel entry point */
> + void *kernel_entry = start_kernel;
> + char *cmdline = early_ioremap(fw_arg1, COMMAND_LINE_SIZE);
> +
> + /* Boot command line was passed in fw_arg1 */
> + strscpy(boot_command_line, cmdline, COMMAND_LINE_SIZE);
> +
> + kernel_length = (long)(_end) - (long)(_text);
> +
> + loc_new = determine_relocation_address();
> +
> + /* Sanity check relocation address */
> + if (relocation_addr_valid(loc_new))
> + offset = (unsigned long)loc_new - (unsigned long)(_text);
> +
> + if (offset) {
> + /* Copy the kernel to it's new location */
> + memcpy(loc_new, _text, kernel_length);
> +
> + /* Sync the caches ready for execution of new kernel */
> + __asm__ __volatile__ (
> + "ibar 0 \t\n"
> + "dbar 0 \t\n");
> +
> + reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
> + reloc_offset += offset;
> +
> + relocate_relative();
> +
> + /* The current thread is now within the relocated image */
> + __current_thread_info = RELOCATED_KASLR(__current_thread_info);
> +
> + /* Return the new kernel's entry point */
> + kernel_entry = RELOCATED_KASLR(start_kernel);
> +
> + update_reloc_offset(&reloc_offset, offset);
> + }

Self review:

There is a problem with do_kaslr implementation, which will be fixed in
the next version.

> +
> + return kernel_entry;
> +}
> +#endif
> +
> +void __init relocate_kernel(void)
> +{
> + reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
> +
> + relocate_relative();
> +}
> +
> /*
> * Show relocation information on panic.
> */
>


2023-02-10 09:10:13

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH v4 2/5] LoongArch: Use la.pcrel instead of la.abs for exception handlers

Hi, Youling and Ruoyao,

Thank you very much for implementing the per-node exceptions. But I
want to know if the per-node solution is really worthy for a PIE
kernel. So, could you please test the performance? Maybe we can reduce
the complexity if we give up the per-node solution.

Huacai

On Fri, Feb 10, 2023 at 4:47 PM Youling Tang <[email protected]> wrote:
>
> From: Xi Ruoyao <[email protected]>
>
> It's needed to build the kernel as a PIE, or the linker will complain.
>
> For the consideration about performance, we copy the exception handlers
> to a dedicated 64 KB area for each CPU. So, the PC-relative offset
> calculated at link time will be incorrect and we need to relocate the
> exception handlers after copying them.
>
> For the simplicity, we don't use the ELF R_LARCH_* relocations, but code
> an relocation entry as simply (offset_in_the_handler, symbol_addr). For
> each exception handler, we also code the number of relocation entries.
> Then we can use the relocation information to fix up the handlers after
> copying them.
>
> Signed-off-by: Xi Ruoyao <[email protected]>
> ---
> arch/loongarch/include/asm/inst.h | 1 +
> arch/loongarch/include/asm/setup.h | 6 +-
> arch/loongarch/include/asm/stackframe.h | 3 +-
> arch/loongarch/kernel/genex.S | 40 +++++-
> arch/loongarch/kernel/traps.c | 158 ++++++++++++++++++++----
> arch/loongarch/mm/tlb.c | 23 ++--
> arch/loongarch/mm/tlbex.S | 69 +++++++++--
> 7 files changed, 255 insertions(+), 45 deletions(-)
>
> diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
> index 7eedd83fd0d7..426054518a3d 100644
> --- a/arch/loongarch/include/asm/inst.h
> +++ b/arch/loongarch/include/asm/inst.h
> @@ -32,6 +32,7 @@ enum reg1i20_op {
> lu12iw_op = 0x0a,
> lu32id_op = 0x0b,
> pcaddi_op = 0x0c,
> + pcalau12i_op = 0x0d,
> pcaddu12i_op = 0x0e,
> pcaddu18i_op = 0x0f,
> };
> diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h
> index 72ead58039f3..f0a2b34365f1 100644
> --- a/arch/loongarch/include/asm/setup.h
> +++ b/arch/loongarch/include/asm/setup.h
> @@ -11,6 +11,9 @@
>
> #define VECSIZE 0x200
>
> +struct handler_reloc;
> +
> +extern struct handler_reloc *eentry_reloc[];
> extern unsigned long eentry;
> extern unsigned long tlbrentry;
> extern char init_command_line[COMMAND_LINE_SIZE];
> @@ -18,7 +21,8 @@ extern void tlb_init(int cpu);
> extern void cpu_cache_init(void);
> extern void cache_error_setup(void);
> extern void per_cpu_trap_init(int cpu);
> -extern void set_handler(unsigned long offset, void *addr, unsigned long len);
> +extern void set_handler(unsigned long exccode, void *addr);
> extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len);
> +extern void reloc_handler(unsigned long handler, struct handler_reloc *rel);
>
> #endif /* __SETUP_H */
> diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h
> index 7deb043ce387..bbec1e56b61b 100644
> --- a/arch/loongarch/include/asm/stackframe.h
> +++ b/arch/loongarch/include/asm/stackframe.h
> @@ -77,7 +77,8 @@
> * new value in sp.
> */
> .macro get_saved_sp docfi=0
> - la.abs t1, kernelsp
> + /* The label is used for generating reloc tables for handlers */
> +514: la.pcrel t1, t0, kernelsp
> #ifdef CONFIG_SMP
> csrrd t0, PERCPU_BASE_KS
> LONG_ADD t1, t1, t0
> diff --git a/arch/loongarch/kernel/genex.S b/arch/loongarch/kernel/genex.S
> index 7e5c293ed89f..005a10fe5a50 100644
> --- a/arch/loongarch/kernel/genex.S
> +++ b/arch/loongarch/kernel/genex.S
> @@ -34,7 +34,7 @@ SYM_FUNC_END(__arch_cpu_idle)
> SYM_FUNC_START(handle_vint)
> BACKUP_T0T1
> SAVE_ALL
> - la.abs t1, __arch_cpu_idle
> +0: la.pcrel t1, t2, __arch_cpu_idle
> LONG_L t0, sp, PT_ERA
> /* 32 byte rollback region */
> ori t0, t0, 0x1f
> @@ -43,11 +43,25 @@ SYM_FUNC_START(handle_vint)
> LONG_S t0, sp, PT_ERA
> 1: move a0, sp
> move a1, sp
> - la.abs t0, do_vint
> +2: la.pcrel t0, t2, do_vint
> jirl ra, t0, 0
> RESTORE_ALL_AND_RET
> SYM_FUNC_END(handle_vint)
>
> +SYM_DATA_START(rel_handle_vint)
> +LONG 3
> +
> +LONG 514b - handle_vint
> +LONG kernelsp
> +
> +LONG 0b - handle_vint
> +LONG __arch_cpu_idle
> +
> +LONG 2b - handle_vint
> +LONG do_vint
> +
> +SYM_DATA_END(rel_handle_vint)
> +
> SYM_FUNC_START(except_vec_cex)
> b cache_parity_error
> SYM_FUNC_END(except_vec_cex)
> @@ -72,12 +86,24 @@ SYM_FUNC_END(except_vec_cex)
> SAVE_ALL
> build_prep_\prep
> move a0, sp
> - la.abs t0, do_\handler
> + 667:
> + la.pcrel t0, t1, do_\handler
> jirl ra, t0, 0
> 668:
> RESTORE_ALL_AND_RET
> SYM_FUNC_END(handle_\exception)
> SYM_DATA(unwind_hint_\exception, .word 668b - 666b)
> +
> + SYM_DATA_START(rel_handle_\exception)
> + LONG 2
> +
> + LONG 514b - 666b
> + LONG kernelsp
> +
> + LONG 667b - 666b
> + LONG do_\handler
> +
> + SYM_DATA_END(rel_handle_\exception)
> .endm
>
> BUILD_HANDLER ade ade badv
> @@ -93,6 +119,12 @@ SYM_FUNC_END(except_vec_cex)
> BUILD_HANDLER reserved reserved none /* others */
>
> SYM_FUNC_START(handle_sys)
> - la.abs t0, handle_syscall
> + la.pcrel t0, t1, handle_syscall
> jr t0
> SYM_FUNC_END(handle_sys)
> +
> +SYM_DATA_START(rel_handle_sys)
> +LONG 1
> +LONG 0
> +LONG handle_syscall
> +SYM_DATA_END(rel_handle_sys)
> diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c
> index c38a146a973b..7e073854f493 100644
> --- a/arch/loongarch/kernel/traps.c
> +++ b/arch/loongarch/kernel/traps.c
> @@ -62,6 +62,127 @@ extern asmlinkage void handle_reserved(void);
> extern asmlinkage void handle_watch(void);
> extern asmlinkage void handle_vint(void);
>
> +struct handler_reloc_entry {
> + unsigned long offset;
> + unsigned long sym;
> +};
> +
> +struct handler_reloc {
> + unsigned long cnt;
> + struct handler_reloc_entry entries[];
> +};
> +
> +extern struct handler_reloc rel_handle_tlb_load;
> +extern struct handler_reloc rel_handle_tlb_store;
> +extern struct handler_reloc rel_handle_tlb_modify;
> +extern struct handler_reloc rel_handle_tlb_protect;
> +extern struct handler_reloc rel_handle_ade;
> +extern struct handler_reloc rel_handle_ale;
> +extern struct handler_reloc rel_handle_sys;
> +extern struct handler_reloc rel_handle_bp;
> +extern struct handler_reloc rel_handle_ri;
> +extern struct handler_reloc rel_handle_fpu;
> +extern struct handler_reloc rel_handle_lsx;
> +extern struct handler_reloc rel_handle_lasx;
> +extern struct handler_reloc rel_handle_fpe;
> +extern struct handler_reloc rel_handle_lbt;
> +extern struct handler_reloc rel_handle_watch;
> +extern struct handler_reloc rel_handle_reserved;
> +extern struct handler_reloc rel_handle_vint;
> +
> +struct handler_reloc *eentry_reloc[128] = {
> + [0] = NULL, /* merr handler */
> + [EXCCODE_TLBL] = &rel_handle_tlb_load,
> + [EXCCODE_TLBS] = &rel_handle_tlb_store,
> + [EXCCODE_TLBI] = &rel_handle_tlb_load,
> + [EXCCODE_TLBM] = &rel_handle_tlb_modify,
> + [EXCCODE_TLBNR] = &rel_handle_tlb_protect,
> + [EXCCODE_TLBNX] = &rel_handle_tlb_protect,
> + [EXCCODE_TLBPE] = &rel_handle_tlb_protect,
> + [EXCCODE_ADE] = &rel_handle_ade,
> + [EXCCODE_ALE] = &rel_handle_ale,
> + [EXCCODE_SYS] = &rel_handle_sys,
> + [EXCCODE_BP] = &rel_handle_bp,
> + [EXCCODE_INE] = &rel_handle_ri,
> + [EXCCODE_IPE] = &rel_handle_ri,
> + [EXCCODE_FPDIS] = &rel_handle_fpu,
> + [EXCCODE_LSXDIS] = &rel_handle_lsx,
> + [EXCCODE_LASXDIS] = &rel_handle_lasx,
> + [EXCCODE_FPE] = &rel_handle_fpe,
> + [EXCCODE_BTDIS] = &rel_handle_lbt,
> + [EXCCODE_WATCH] = &rel_handle_watch,
> + [(EXCCODE_WATCH + 1) ... (EXCCODE_INT_START - 1)] = &rel_handle_reserved,
> + [EXCCODE_INT_START ... (EXCCODE_INT_END - 1)] = &rel_handle_vint,
> +};
> +
> +void reloc_handler(unsigned long handler, struct handler_reloc *rel)
> +{
> + if (!rel)
> + return;
> +
> + for (unsigned long i = 0; i < rel->cnt; i++) {
> + unsigned long pc = handler + rel->entries[i].offset;
> + union loongarch_instruction *insn =
> + (union loongarch_instruction *)pc;
> + u32 imm[4];
> + unsigned long v = rel->entries[i].sym;
> +
> + /* GNU as >= 2.40 uses pcalau12i for la.pcrel, but GNU ld <= 2.39
> + * uses pcaddu12i.
> + */
> + if (insn->reg1i20_format.opcode == pcalau12i_op) {
> + /* Use s32 deliberately for sign extension. */
> + s32 offset_hi20 = ((v + 0x800) & ~0xfff) -
> + (pc & ~0xfff);
> + unsigned long anchor = (pc & ~0xfff) + offset_hi20;
> + unsigned long offset_rem = v - anchor;
> +
> + imm[0] = (offset_hi20 >> 12) & 0xfffff;
> + imm[1] = v & 0xfff;
> + imm[2] = (offset_rem >> 32) & 0xfffff;
> + imm[3] = offset_rem >> 52;
> + } else if (insn->reg1i20_format.opcode == pcaddu12i_op) {
> + /* Use s32 deliberately for sign extension. */
> + s32 offset_lo = v - pc;
> + unsigned long offset_hi = v - pc - offset_lo;
> +
> + imm[0] = (offset_lo >> 12) & 0xfffff;
> + imm[1] = offset_lo & 0xfff;
> + imm[2] = (offset_hi >> 32) & 0xfffff;
> + imm[3] = offset_hi >> 52;
> + } else
> + panic("Cannot fixup la.pcrel for exception handler at %lu: unexpected instruction %d!",
> + pc, insn->word);
> +
> + insn[0].reg1i20_format.immediate = imm[0];
> + insn[1].reg2i12_format.immediate = imm[1];
> + insn[2].reg1i20_format.immediate = imm[2];
> + insn[3].reg2i12_format.immediate = imm[3];
> + }
> +}
> +
> +/* Install CPU exception handler */
> +static void do_set_handler(unsigned long exccode, void *addr,
> + struct handler_reloc *rel)
> +{
> + unsigned long dest_addr = eentry + exccode * VECSIZE;
> +
> + memcpy((void *)dest_addr, addr, VECSIZE);
> + reloc_handler(dest_addr, rel);
> + local_flush_icache_range(dest_addr, dest_addr + VECSIZE);
> +}
> +
> +/* Install CPU exception handler, with the reloc table from eentry_reloc */
> +void set_handler(unsigned long exccode, void *addr)
> +{
> + do_set_handler(exccode, addr, eentry_reloc[exccode]);
> +}
> +
> +static void set_handler_reserved(unsigned long exccode)
> +{
> + do_set_handler(exccode, handle_reserved, &rel_handle_reserved);
> +}
> +
> static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,
> const char *loglvl, bool user)
> {
> @@ -704,19 +825,12 @@ void per_cpu_trap_init(int cpu)
> /* Initialise exception handlers */
> if (cpu == 0)
> for (i = 0; i < 64; i++)
> - set_handler(i * VECSIZE, handle_reserved, VECSIZE);
> + set_handler_reserved(i);
>
> tlb_init(cpu);
> cpu_cache_init();
> }
>
> -/* Install CPU exception handler */
> -void set_handler(unsigned long offset, void *addr, unsigned long size)
> -{
> - memcpy((void *)(eentry + offset), addr, size);
> - local_flush_icache_range(eentry + offset, eentry + offset + size);
> -}
> -
> static const char panic_null_cerr[] =
> "Trying to set NULL cache error exception handler\n";
>
> @@ -741,20 +855,20 @@ void __init trap_init(void)
>
> /* Set interrupt vector handler */
> for (i = EXCCODE_INT_START; i < EXCCODE_INT_END; i++)
> - set_handler(i * VECSIZE, handle_vint, VECSIZE);
> -
> - set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE);
> - set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE);
> - set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE);
> - set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE);
> - set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE);
> - set_handler(EXCCODE_IPE * VECSIZE, handle_ri, VECSIZE);
> - set_handler(EXCCODE_FPDIS * VECSIZE, handle_fpu, VECSIZE);
> - set_handler(EXCCODE_LSXDIS * VECSIZE, handle_lsx, VECSIZE);
> - set_handler(EXCCODE_LASXDIS * VECSIZE, handle_lasx, VECSIZE);
> - set_handler(EXCCODE_FPE * VECSIZE, handle_fpe, VECSIZE);
> - set_handler(EXCCODE_BTDIS * VECSIZE, handle_lbt, VECSIZE);
> - set_handler(EXCCODE_WATCH * VECSIZE, handle_watch, VECSIZE);
> + set_handler(i, handle_vint);
> +
> + set_handler(EXCCODE_ADE, handle_ade);
> + set_handler(EXCCODE_ALE, handle_ale);
> + set_handler(EXCCODE_SYS, handle_sys);
> + set_handler(EXCCODE_BP, handle_bp);
> + set_handler(EXCCODE_INE, handle_ri);
> + set_handler(EXCCODE_IPE, handle_ri);
> + set_handler(EXCCODE_FPDIS, handle_fpu);
> + set_handler(EXCCODE_LSXDIS, handle_lsx);
> + set_handler(EXCCODE_LASXDIS, handle_lasx);
> + set_handler(EXCCODE_FPE, handle_fpe);
> + set_handler(EXCCODE_BTDIS, handle_lbt);
> + set_handler(EXCCODE_WATCH, handle_watch);
>
> cache_error_setup();
>
> diff --git a/arch/loongarch/mm/tlb.c b/arch/loongarch/mm/tlb.c
> index 8bad6b0cff59..6f70aab7202a 100644
> --- a/arch/loongarch/mm/tlb.c
> +++ b/arch/loongarch/mm/tlb.c
> @@ -253,7 +253,6 @@ static void output_pgtable_bits_defines(void)
> #ifdef CONFIG_NUMA
> unsigned long pcpu_handlers[NR_CPUS];
> #endif
> -extern long exception_handlers[VECSIZE * 128 / sizeof(long)];
>
> void setup_tlb_handler(int cpu)
> {
> @@ -264,19 +263,20 @@ void setup_tlb_handler(int cpu)
> if (cpu == 0) {
> memcpy((void *)tlbrentry, handle_tlb_refill, 0x80);
> local_flush_icache_range(tlbrentry, tlbrentry + 0x80);
> - set_handler(EXCCODE_TLBI * VECSIZE, handle_tlb_load, VECSIZE);
> - set_handler(EXCCODE_TLBL * VECSIZE, handle_tlb_load, VECSIZE);
> - set_handler(EXCCODE_TLBS * VECSIZE, handle_tlb_store, VECSIZE);
> - set_handler(EXCCODE_TLBM * VECSIZE, handle_tlb_modify, VECSIZE);
> - set_handler(EXCCODE_TLBNR * VECSIZE, handle_tlb_protect, VECSIZE);
> - set_handler(EXCCODE_TLBNX * VECSIZE, handle_tlb_protect, VECSIZE);
> - set_handler(EXCCODE_TLBPE * VECSIZE, handle_tlb_protect, VECSIZE);
> + set_handler(EXCCODE_TLBI, handle_tlb_load);
> + set_handler(EXCCODE_TLBL, handle_tlb_load);
> + set_handler(EXCCODE_TLBS, handle_tlb_store);
> + set_handler(EXCCODE_TLBM, handle_tlb_modify);
> + set_handler(EXCCODE_TLBNR, handle_tlb_protect);
> + set_handler(EXCCODE_TLBNX, handle_tlb_protect);
> + set_handler(EXCCODE_TLBPE, handle_tlb_protect);
> }
> #ifdef CONFIG_NUMA
> else {
> void *addr;
> + unsigned long addr_ul;
> struct page *page;
> - const int vec_sz = sizeof(exception_handlers);
> + const int vec_sz = VECSIZE * 128;
>
> if (pcpu_handlers[cpu])
> return;
> @@ -286,8 +286,11 @@ void setup_tlb_handler(int cpu)
> return;
>
> addr = page_address(page);
> + addr_ul = (unsigned long)addr;
> pcpu_handlers[cpu] = (unsigned long)addr;
> - memcpy((void *)addr, (void *)eentry, vec_sz);
> + memcpy(addr, (void *)eentry, vec_sz);
> + for (unsigned long i = 0; i < 128; i++)
> + reloc_handler(addr_ul + i * VECSIZE, eentry_reloc[i]);
> local_flush_icache_range((unsigned long)addr, (unsigned long)addr + vec_sz);
> csr_write64(pcpu_handlers[cpu], LOONGARCH_CSR_EENTRY);
> csr_write64(pcpu_handlers[cpu], LOONGARCH_CSR_MERRENTRY);
> diff --git a/arch/loongarch/mm/tlbex.S b/arch/loongarch/mm/tlbex.S
> index 3dd2a9615cd9..044c2190771a 100644
> --- a/arch/loongarch/mm/tlbex.S
> +++ b/arch/loongarch/mm/tlbex.S
> @@ -39,11 +39,21 @@ SYM_FUNC_START(handle_tlb_protect)
> move a1, zero
> csrrd a2, LOONGARCH_CSR_BADV
> REG_S a2, sp, PT_BVADDR
> - la.abs t0, do_page_fault
> +1: la.pcrel t0, t1, do_page_fault
> jirl ra, t0, 0
> RESTORE_ALL_AND_RET
> SYM_FUNC_END(handle_tlb_protect)
>
> +SYM_DATA_START(rel_handle_tlb_protect)
> + LONG 2
> +
> + LONG 514b - handle_tlb_protect
> + LONG kernelsp
> +
> + LONG 1b - handle_tlb_protect
> + LONG do_page_fault
> +SYM_DATA_END(rel_handle_tlb_protect)
> +
> SYM_FUNC_START(handle_tlb_load)
> csrwr t0, EXCEPTION_KS0
> csrwr t1, EXCEPTION_KS1
> @@ -115,7 +125,8 @@ smp_pgtable_change_load:
>
> #ifdef CONFIG_64BIT
> vmalloc_load:
> - la.abs t1, swapper_pg_dir
> + /* The first insn of vmalloc_done_load overwrites ra */
> +1: la.pcrel t1, ra, swapper_pg_dir
> b vmalloc_done_load
> #endif
>
> @@ -186,10 +197,24 @@ tlb_huge_update_load:
> nopage_tlb_load:
> dbar 0
> csrrd ra, EXCEPTION_KS2
> - la.abs t0, tlb_do_page_fault_0
> +2: la.pcrel t0, t1, tlb_do_page_fault_0
> jr t0
> SYM_FUNC_END(handle_tlb_load)
>
> +SYM_DATA_START(rel_handle_tlb_load)
> +#ifdef CONFIG_64BIT
> + LONG 2
> +
> + LONG 1b - handle_tlb_load
> + LONG swapper_pg_dir
> +#else
> + LONG 1
> +#endif
> +
> + LONG 2b - handle_tlb_load
> + LONG tlb_do_page_fault_0
> +SYM_DATA_END(rel_handle_tlb_load)
> +
> SYM_FUNC_START(handle_tlb_store)
> csrwr t0, EXCEPTION_KS0
> csrwr t1, EXCEPTION_KS1
> @@ -262,7 +287,8 @@ smp_pgtable_change_store:
>
> #ifdef CONFIG_64BIT
> vmalloc_store:
> - la.abs t1, swapper_pg_dir
> + /* The first insn of vmalloc_done_store overwrites ra */
> +1: la.pcrel t1, ra, swapper_pg_dir
> b vmalloc_done_store
> #endif
>
> @@ -335,10 +361,24 @@ tlb_huge_update_store:
> nopage_tlb_store:
> dbar 0
> csrrd ra, EXCEPTION_KS2
> - la.abs t0, tlb_do_page_fault_1
> +2: la.pcrel t0, t1, tlb_do_page_fault_1
> jr t0
> SYM_FUNC_END(handle_tlb_store)
>
> +SYM_DATA_START(rel_handle_tlb_store)
> +#ifdef CONFIG_64BIT
> + LONG 2
> +
> + LONG 1b - handle_tlb_store
> + LONG swapper_pg_dir
> +#else
> + LONG 1
> +#endif
> +
> + LONG 2b - handle_tlb_store
> + LONG tlb_do_page_fault_1
> +SYM_DATA_END(rel_handle_tlb_store)
> +
> SYM_FUNC_START(handle_tlb_modify)
> csrwr t0, EXCEPTION_KS0
> csrwr t1, EXCEPTION_KS1
> @@ -410,7 +450,8 @@ smp_pgtable_change_modify:
>
> #ifdef CONFIG_64BIT
> vmalloc_modify:
> - la.abs t1, swapper_pg_dir
> + /* The first insn of vmalloc_done_modify overwrites ra */
> +1: la.pcrel t1, ra, swapper_pg_dir
> b vmalloc_done_modify
> #endif
>
> @@ -482,10 +523,24 @@ tlb_huge_update_modify:
> nopage_tlb_modify:
> dbar 0
> csrrd ra, EXCEPTION_KS2
> - la.abs t0, tlb_do_page_fault_1
> +2: la.pcrel t0, t1, tlb_do_page_fault_1
> jr t0
> SYM_FUNC_END(handle_tlb_modify)
>
> +SYM_DATA_START(rel_handle_tlb_modify)
> +#ifdef CONFIG_64BIT
> + LONG 2
> +
> + LONG 1b - handle_tlb_modify
> + LONG swapper_pg_dir
> +#else
> + LONG 1
> +#endif
> +
> + LONG 2b - handle_tlb_modify
> + LONG tlb_do_page_fault_1
> +SYM_DATA_END(rel_handle_tlb_modify)
> +
> SYM_FUNC_START(handle_tlb_refill)
> csrwr t0, LOONGARCH_CSR_TLBRSAVE
> csrrd t0, LOONGARCH_CSR_PGD
> --
> 2.37.3
>

2023-02-10 09:18:15

by Youling Tang

[permalink] [raw]
Subject: Re: [PATCH v4 2/5] LoongArch: Use la.pcrel instead of la.abs for exception handlers



On 02/10/2023 05:09 PM, Huacai Chen wrote:
> Hi, Youling and Ruoyao,
>
> Thank you very much for implementing the per-node exceptions. But I
> want to know if the per-node solution is really worthy for a PIE
> kernel. So, could you please test the performance? Maybe we can reduce
> the complexity if we give up the per-node solution.

I will test performance on NUMA machines based on v2 and v3 patch sets.

Youling.
>
> Huacai
>
> On Fri, Feb 10, 2023 at 4:47 PM Youling Tang <[email protected]> wrote:
>>
>> From: Xi Ruoyao <[email protected]>
>>
>> It's needed to build the kernel as a PIE, or the linker will complain.
>>
>> For the consideration about performance, we copy the exception handlers
>> to a dedicated 64 KB area for each CPU. So, the PC-relative offset
>> calculated at link time will be incorrect and we need to relocate the
>> exception handlers after copying them.
>>
>> For the simplicity, we don't use the ELF R_LARCH_* relocations, but code
>> an relocation entry as simply (offset_in_the_handler, symbol_addr). For
>> each exception handler, we also code the number of relocation entries.
>> Then we can use the relocation information to fix up the handlers after
>> copying them.
>>
>> Signed-off-by: Xi Ruoyao <[email protected]>
>> ---
>> arch/loongarch/include/asm/inst.h | 1 +
>> arch/loongarch/include/asm/setup.h | 6 +-
>> arch/loongarch/include/asm/stackframe.h | 3 +-
>> arch/loongarch/kernel/genex.S | 40 +++++-
>> arch/loongarch/kernel/traps.c | 158 ++++++++++++++++++++----
>> arch/loongarch/mm/tlb.c | 23 ++--
>> arch/loongarch/mm/tlbex.S | 69 +++++++++--
>> 7 files changed, 255 insertions(+), 45 deletions(-)
>>
>> diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
>> index 7eedd83fd0d7..426054518a3d 100644
>> --- a/arch/loongarch/include/asm/inst.h
>> +++ b/arch/loongarch/include/asm/inst.h
>> @@ -32,6 +32,7 @@ enum reg1i20_op {
>> lu12iw_op = 0x0a,
>> lu32id_op = 0x0b,
>> pcaddi_op = 0x0c,
>> + pcalau12i_op = 0x0d,
>> pcaddu12i_op = 0x0e,
>> pcaddu18i_op = 0x0f,
>> };
>> diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h
>> index 72ead58039f3..f0a2b34365f1 100644
>> --- a/arch/loongarch/include/asm/setup.h
>> +++ b/arch/loongarch/include/asm/setup.h
>> @@ -11,6 +11,9 @@
>>
>> #define VECSIZE 0x200
>>
>> +struct handler_reloc;
>> +
>> +extern struct handler_reloc *eentry_reloc[];
>> extern unsigned long eentry;
>> extern unsigned long tlbrentry;
>> extern char init_command_line[COMMAND_LINE_SIZE];
>> @@ -18,7 +21,8 @@ extern void tlb_init(int cpu);
>> extern void cpu_cache_init(void);
>> extern void cache_error_setup(void);
>> extern void per_cpu_trap_init(int cpu);
>> -extern void set_handler(unsigned long offset, void *addr, unsigned long len);
>> +extern void set_handler(unsigned long exccode, void *addr);
>> extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len);
>> +extern void reloc_handler(unsigned long handler, struct handler_reloc *rel);
>>
>> #endif /* __SETUP_H */
>> diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h
>> index 7deb043ce387..bbec1e56b61b 100644
>> --- a/arch/loongarch/include/asm/stackframe.h
>> +++ b/arch/loongarch/include/asm/stackframe.h
>> @@ -77,7 +77,8 @@
>> * new value in sp.
>> */
>> .macro get_saved_sp docfi=0
>> - la.abs t1, kernelsp
>> + /* The label is used for generating reloc tables for handlers */
>> +514: la.pcrel t1, t0, kernelsp
>> #ifdef CONFIG_SMP
>> csrrd t0, PERCPU_BASE_KS
>> LONG_ADD t1, t1, t0
>> diff --git a/arch/loongarch/kernel/genex.S b/arch/loongarch/kernel/genex.S
>> index 7e5c293ed89f..005a10fe5a50 100644
>> --- a/arch/loongarch/kernel/genex.S
>> +++ b/arch/loongarch/kernel/genex.S
>> @@ -34,7 +34,7 @@ SYM_FUNC_END(__arch_cpu_idle)
>> SYM_FUNC_START(handle_vint)
>> BACKUP_T0T1
>> SAVE_ALL
>> - la.abs t1, __arch_cpu_idle
>> +0: la.pcrel t1, t2, __arch_cpu_idle
>> LONG_L t0, sp, PT_ERA
>> /* 32 byte rollback region */
>> ori t0, t0, 0x1f
>> @@ -43,11 +43,25 @@ SYM_FUNC_START(handle_vint)
>> LONG_S t0, sp, PT_ERA
>> 1: move a0, sp
>> move a1, sp
>> - la.abs t0, do_vint
>> +2: la.pcrel t0, t2, do_vint
>> jirl ra, t0, 0
>> RESTORE_ALL_AND_RET
>> SYM_FUNC_END(handle_vint)
>>
>> +SYM_DATA_START(rel_handle_vint)
>> +LONG 3
>> +
>> +LONG 514b - handle_vint
>> +LONG kernelsp
>> +
>> +LONG 0b - handle_vint
>> +LONG __arch_cpu_idle
>> +
>> +LONG 2b - handle_vint
>> +LONG do_vint
>> +
>> +SYM_DATA_END(rel_handle_vint)
>> +
>> SYM_FUNC_START(except_vec_cex)
>> b cache_parity_error
>> SYM_FUNC_END(except_vec_cex)
>> @@ -72,12 +86,24 @@ SYM_FUNC_END(except_vec_cex)
>> SAVE_ALL
>> build_prep_\prep
>> move a0, sp
>> - la.abs t0, do_\handler
>> + 667:
>> + la.pcrel t0, t1, do_\handler
>> jirl ra, t0, 0
>> 668:
>> RESTORE_ALL_AND_RET
>> SYM_FUNC_END(handle_\exception)
>> SYM_DATA(unwind_hint_\exception, .word 668b - 666b)
>> +
>> + SYM_DATA_START(rel_handle_\exception)
>> + LONG 2
>> +
>> + LONG 514b - 666b
>> + LONG kernelsp
>> +
>> + LONG 667b - 666b
>> + LONG do_\handler
>> +
>> + SYM_DATA_END(rel_handle_\exception)
>> .endm
>>
>> BUILD_HANDLER ade ade badv
>> @@ -93,6 +119,12 @@ SYM_FUNC_END(except_vec_cex)
>> BUILD_HANDLER reserved reserved none /* others */
>>
>> SYM_FUNC_START(handle_sys)
>> - la.abs t0, handle_syscall
>> + la.pcrel t0, t1, handle_syscall
>> jr t0
>> SYM_FUNC_END(handle_sys)
>> +
>> +SYM_DATA_START(rel_handle_sys)
>> +LONG 1
>> +LONG 0
>> +LONG handle_syscall
>> +SYM_DATA_END(rel_handle_sys)
>> diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c
>> index c38a146a973b..7e073854f493 100644
>> --- a/arch/loongarch/kernel/traps.c
>> +++ b/arch/loongarch/kernel/traps.c
>> @@ -62,6 +62,127 @@ extern asmlinkage void handle_reserved(void);
>> extern asmlinkage void handle_watch(void);
>> extern asmlinkage void handle_vint(void);
>>
>> +struct handler_reloc_entry {
>> + unsigned long offset;
>> + unsigned long sym;
>> +};
>> +
>> +struct handler_reloc {
>> + unsigned long cnt;
>> + struct handler_reloc_entry entries[];
>> +};
>> +
>> +extern struct handler_reloc rel_handle_tlb_load;
>> +extern struct handler_reloc rel_handle_tlb_store;
>> +extern struct handler_reloc rel_handle_tlb_modify;
>> +extern struct handler_reloc rel_handle_tlb_protect;
>> +extern struct handler_reloc rel_handle_ade;
>> +extern struct handler_reloc rel_handle_ale;
>> +extern struct handler_reloc rel_handle_sys;
>> +extern struct handler_reloc rel_handle_bp;
>> +extern struct handler_reloc rel_handle_ri;
>> +extern struct handler_reloc rel_handle_fpu;
>> +extern struct handler_reloc rel_handle_lsx;
>> +extern struct handler_reloc rel_handle_lasx;
>> +extern struct handler_reloc rel_handle_fpe;
>> +extern struct handler_reloc rel_handle_lbt;
>> +extern struct handler_reloc rel_handle_watch;
>> +extern struct handler_reloc rel_handle_reserved;
>> +extern struct handler_reloc rel_handle_vint;
>> +
>> +struct handler_reloc *eentry_reloc[128] = {
>> + [0] = NULL, /* merr handler */
>> + [EXCCODE_TLBL] = &rel_handle_tlb_load,
>> + [EXCCODE_TLBS] = &rel_handle_tlb_store,
>> + [EXCCODE_TLBI] = &rel_handle_tlb_load,
>> + [EXCCODE_TLBM] = &rel_handle_tlb_modify,
>> + [EXCCODE_TLBNR] = &rel_handle_tlb_protect,
>> + [EXCCODE_TLBNX] = &rel_handle_tlb_protect,
>> + [EXCCODE_TLBPE] = &rel_handle_tlb_protect,
>> + [EXCCODE_ADE] = &rel_handle_ade,
>> + [EXCCODE_ALE] = &rel_handle_ale,
>> + [EXCCODE_SYS] = &rel_handle_sys,
>> + [EXCCODE_BP] = &rel_handle_bp,
>> + [EXCCODE_INE] = &rel_handle_ri,
>> + [EXCCODE_IPE] = &rel_handle_ri,
>> + [EXCCODE_FPDIS] = &rel_handle_fpu,
>> + [EXCCODE_LSXDIS] = &rel_handle_lsx,
>> + [EXCCODE_LASXDIS] = &rel_handle_lasx,
>> + [EXCCODE_FPE] = &rel_handle_fpe,
>> + [EXCCODE_BTDIS] = &rel_handle_lbt,
>> + [EXCCODE_WATCH] = &rel_handle_watch,
>> + [(EXCCODE_WATCH + 1) ... (EXCCODE_INT_START - 1)] = &rel_handle_reserved,
>> + [EXCCODE_INT_START ... (EXCCODE_INT_END - 1)] = &rel_handle_vint,
>> +};
>> +
>> +void reloc_handler(unsigned long handler, struct handler_reloc *rel)
>> +{
>> + if (!rel)
>> + return;
>> +
>> + for (unsigned long i = 0; i < rel->cnt; i++) {
>> + unsigned long pc = handler + rel->entries[i].offset;
>> + union loongarch_instruction *insn =
>> + (union loongarch_instruction *)pc;
>> + u32 imm[4];
>> + unsigned long v = rel->entries[i].sym;
>> +
>> + /* GNU as >= 2.40 uses pcalau12i for la.pcrel, but GNU ld <= 2.39
>> + * uses pcaddu12i.
>> + */
>> + if (insn->reg1i20_format.opcode == pcalau12i_op) {
>> + /* Use s32 deliberately for sign extension. */
>> + s32 offset_hi20 = ((v + 0x800) & ~0xfff) -
>> + (pc & ~0xfff);
>> + unsigned long anchor = (pc & ~0xfff) + offset_hi20;
>> + unsigned long offset_rem = v - anchor;
>> +
>> + imm[0] = (offset_hi20 >> 12) & 0xfffff;
>> + imm[1] = v & 0xfff;
>> + imm[2] = (offset_rem >> 32) & 0xfffff;
>> + imm[3] = offset_rem >> 52;
>> + } else if (insn->reg1i20_format.opcode == pcaddu12i_op) {
>> + /* Use s32 deliberately for sign extension. */
>> + s32 offset_lo = v - pc;
>> + unsigned long offset_hi = v - pc - offset_lo;
>> +
>> + imm[0] = (offset_lo >> 12) & 0xfffff;
>> + imm[1] = offset_lo & 0xfff;
>> + imm[2] = (offset_hi >> 32) & 0xfffff;
>> + imm[3] = offset_hi >> 52;
>> + } else
>> + panic("Cannot fixup la.pcrel for exception handler at %lu: unexpected instruction %d!",
>> + pc, insn->word);
>> +
>> + insn[0].reg1i20_format.immediate = imm[0];
>> + insn[1].reg2i12_format.immediate = imm[1];
>> + insn[2].reg1i20_format.immediate = imm[2];
>> + insn[3].reg2i12_format.immediate = imm[3];
>> + }
>> +}
>> +
>> +/* Install CPU exception handler */
>> +static void do_set_handler(unsigned long exccode, void *addr,
>> + struct handler_reloc *rel)
>> +{
>> + unsigned long dest_addr = eentry + exccode * VECSIZE;
>> +
>> + memcpy((void *)dest_addr, addr, VECSIZE);
>> + reloc_handler(dest_addr, rel);
>> + local_flush_icache_range(dest_addr, dest_addr + VECSIZE);
>> +}
>> +
>> +/* Install CPU exception handler, with the reloc table from eentry_reloc */
>> +void set_handler(unsigned long exccode, void *addr)
>> +{
>> + do_set_handler(exccode, addr, eentry_reloc[exccode]);
>> +}
>> +
>> +static void set_handler_reserved(unsigned long exccode)
>> +{
>> + do_set_handler(exccode, handle_reserved, &rel_handle_reserved);
>> +}
>> +
>> static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,
>> const char *loglvl, bool user)
>> {
>> @@ -704,19 +825,12 @@ void per_cpu_trap_init(int cpu)
>> /* Initialise exception handlers */
>> if (cpu == 0)
>> for (i = 0; i < 64; i++)
>> - set_handler(i * VECSIZE, handle_reserved, VECSIZE);
>> + set_handler_reserved(i);
>>
>> tlb_init(cpu);
>> cpu_cache_init();
>> }
>>
>> -/* Install CPU exception handler */
>> -void set_handler(unsigned long offset, void *addr, unsigned long size)
>> -{
>> - memcpy((void *)(eentry + offset), addr, size);
>> - local_flush_icache_range(eentry + offset, eentry + offset + size);
>> -}
>> -
>> static const char panic_null_cerr[] =
>> "Trying to set NULL cache error exception handler\n";
>>
>> @@ -741,20 +855,20 @@ void __init trap_init(void)
>>
>> /* Set interrupt vector handler */
>> for (i = EXCCODE_INT_START; i < EXCCODE_INT_END; i++)
>> - set_handler(i * VECSIZE, handle_vint, VECSIZE);
>> -
>> - set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE);
>> - set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE);
>> - set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE);
>> - set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE);
>> - set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE);
>> - set_handler(EXCCODE_IPE * VECSIZE, handle_ri, VECSIZE);
>> - set_handler(EXCCODE_FPDIS * VECSIZE, handle_fpu, VECSIZE);
>> - set_handler(EXCCODE_LSXDIS * VECSIZE, handle_lsx, VECSIZE);
>> - set_handler(EXCCODE_LASXDIS * VECSIZE, handle_lasx, VECSIZE);
>> - set_handler(EXCCODE_FPE * VECSIZE, handle_fpe, VECSIZE);
>> - set_handler(EXCCODE_BTDIS * VECSIZE, handle_lbt, VECSIZE);
>> - set_handler(EXCCODE_WATCH * VECSIZE, handle_watch, VECSIZE);
>> + set_handler(i, handle_vint);
>> +
>> + set_handler(EXCCODE_ADE, handle_ade);
>> + set_handler(EXCCODE_ALE, handle_ale);
>> + set_handler(EXCCODE_SYS, handle_sys);
>> + set_handler(EXCCODE_BP, handle_bp);
>> + set_handler(EXCCODE_INE, handle_ri);
>> + set_handler(EXCCODE_IPE, handle_ri);
>> + set_handler(EXCCODE_FPDIS, handle_fpu);
>> + set_handler(EXCCODE_LSXDIS, handle_lsx);
>> + set_handler(EXCCODE_LASXDIS, handle_lasx);
>> + set_handler(EXCCODE_FPE, handle_fpe);
>> + set_handler(EXCCODE_BTDIS, handle_lbt);
>> + set_handler(EXCCODE_WATCH, handle_watch);
>>
>> cache_error_setup();
>>
>> diff --git a/arch/loongarch/mm/tlb.c b/arch/loongarch/mm/tlb.c
>> index 8bad6b0cff59..6f70aab7202a 100644
>> --- a/arch/loongarch/mm/tlb.c
>> +++ b/arch/loongarch/mm/tlb.c
>> @@ -253,7 +253,6 @@ static void output_pgtable_bits_defines(void)
>> #ifdef CONFIG_NUMA
>> unsigned long pcpu_handlers[NR_CPUS];
>> #endif
>> -extern long exception_handlers[VECSIZE * 128 / sizeof(long)];
>>
>> void setup_tlb_handler(int cpu)
>> {
>> @@ -264,19 +263,20 @@ void setup_tlb_handler(int cpu)
>> if (cpu == 0) {
>> memcpy((void *)tlbrentry, handle_tlb_refill, 0x80);
>> local_flush_icache_range(tlbrentry, tlbrentry + 0x80);
>> - set_handler(EXCCODE_TLBI * VECSIZE, handle_tlb_load, VECSIZE);
>> - set_handler(EXCCODE_TLBL * VECSIZE, handle_tlb_load, VECSIZE);
>> - set_handler(EXCCODE_TLBS * VECSIZE, handle_tlb_store, VECSIZE);
>> - set_handler(EXCCODE_TLBM * VECSIZE, handle_tlb_modify, VECSIZE);
>> - set_handler(EXCCODE_TLBNR * VECSIZE, handle_tlb_protect, VECSIZE);
>> - set_handler(EXCCODE_TLBNX * VECSIZE, handle_tlb_protect, VECSIZE);
>> - set_handler(EXCCODE_TLBPE * VECSIZE, handle_tlb_protect, VECSIZE);
>> + set_handler(EXCCODE_TLBI, handle_tlb_load);
>> + set_handler(EXCCODE_TLBL, handle_tlb_load);
>> + set_handler(EXCCODE_TLBS, handle_tlb_store);
>> + set_handler(EXCCODE_TLBM, handle_tlb_modify);
>> + set_handler(EXCCODE_TLBNR, handle_tlb_protect);
>> + set_handler(EXCCODE_TLBNX, handle_tlb_protect);
>> + set_handler(EXCCODE_TLBPE, handle_tlb_protect);
>> }
>> #ifdef CONFIG_NUMA
>> else {
>> void *addr;
>> + unsigned long addr_ul;
>> struct page *page;
>> - const int vec_sz = sizeof(exception_handlers);
>> + const int vec_sz = VECSIZE * 128;
>>
>> if (pcpu_handlers[cpu])
>> return;
>> @@ -286,8 +286,11 @@ void setup_tlb_handler(int cpu)
>> return;
>>
>> addr = page_address(page);
>> + addr_ul = (unsigned long)addr;
>> pcpu_handlers[cpu] = (unsigned long)addr;
>> - memcpy((void *)addr, (void *)eentry, vec_sz);
>> + memcpy(addr, (void *)eentry, vec_sz);
>> + for (unsigned long i = 0; i < 128; i++)
>> + reloc_handler(addr_ul + i * VECSIZE, eentry_reloc[i]);
>> local_flush_icache_range((unsigned long)addr, (unsigned long)addr + vec_sz);
>> csr_write64(pcpu_handlers[cpu], LOONGARCH_CSR_EENTRY);
>> csr_write64(pcpu_handlers[cpu], LOONGARCH_CSR_MERRENTRY);
>> diff --git a/arch/loongarch/mm/tlbex.S b/arch/loongarch/mm/tlbex.S
>> index 3dd2a9615cd9..044c2190771a 100644
>> --- a/arch/loongarch/mm/tlbex.S
>> +++ b/arch/loongarch/mm/tlbex.S
>> @@ -39,11 +39,21 @@ SYM_FUNC_START(handle_tlb_protect)
>> move a1, zero
>> csrrd a2, LOONGARCH_CSR_BADV
>> REG_S a2, sp, PT_BVADDR
>> - la.abs t0, do_page_fault
>> +1: la.pcrel t0, t1, do_page_fault
>> jirl ra, t0, 0
>> RESTORE_ALL_AND_RET
>> SYM_FUNC_END(handle_tlb_protect)
>>
>> +SYM_DATA_START(rel_handle_tlb_protect)
>> + LONG 2
>> +
>> + LONG 514b - handle_tlb_protect
>> + LONG kernelsp
>> +
>> + LONG 1b - handle_tlb_protect
>> + LONG do_page_fault
>> +SYM_DATA_END(rel_handle_tlb_protect)
>> +
>> SYM_FUNC_START(handle_tlb_load)
>> csrwr t0, EXCEPTION_KS0
>> csrwr t1, EXCEPTION_KS1
>> @@ -115,7 +125,8 @@ smp_pgtable_change_load:
>>
>> #ifdef CONFIG_64BIT
>> vmalloc_load:
>> - la.abs t1, swapper_pg_dir
>> + /* The first insn of vmalloc_done_load overwrites ra */
>> +1: la.pcrel t1, ra, swapper_pg_dir
>> b vmalloc_done_load
>> #endif
>>
>> @@ -186,10 +197,24 @@ tlb_huge_update_load:
>> nopage_tlb_load:
>> dbar 0
>> csrrd ra, EXCEPTION_KS2
>> - la.abs t0, tlb_do_page_fault_0
>> +2: la.pcrel t0, t1, tlb_do_page_fault_0
>> jr t0
>> SYM_FUNC_END(handle_tlb_load)
>>
>> +SYM_DATA_START(rel_handle_tlb_load)
>> +#ifdef CONFIG_64BIT
>> + LONG 2
>> +
>> + LONG 1b - handle_tlb_load
>> + LONG swapper_pg_dir
>> +#else
>> + LONG 1
>> +#endif
>> +
>> + LONG 2b - handle_tlb_load
>> + LONG tlb_do_page_fault_0
>> +SYM_DATA_END(rel_handle_tlb_load)
>> +
>> SYM_FUNC_START(handle_tlb_store)
>> csrwr t0, EXCEPTION_KS0
>> csrwr t1, EXCEPTION_KS1
>> @@ -262,7 +287,8 @@ smp_pgtable_change_store:
>>
>> #ifdef CONFIG_64BIT
>> vmalloc_store:
>> - la.abs t1, swapper_pg_dir
>> + /* The first insn of vmalloc_done_store overwrites ra */
>> +1: la.pcrel t1, ra, swapper_pg_dir
>> b vmalloc_done_store
>> #endif
>>
>> @@ -335,10 +361,24 @@ tlb_huge_update_store:
>> nopage_tlb_store:
>> dbar 0
>> csrrd ra, EXCEPTION_KS2
>> - la.abs t0, tlb_do_page_fault_1
>> +2: la.pcrel t0, t1, tlb_do_page_fault_1
>> jr t0
>> SYM_FUNC_END(handle_tlb_store)
>>
>> +SYM_DATA_START(rel_handle_tlb_store)
>> +#ifdef CONFIG_64BIT
>> + LONG 2
>> +
>> + LONG 1b - handle_tlb_store
>> + LONG swapper_pg_dir
>> +#else
>> + LONG 1
>> +#endif
>> +
>> + LONG 2b - handle_tlb_store
>> + LONG tlb_do_page_fault_1
>> +SYM_DATA_END(rel_handle_tlb_store)
>> +
>> SYM_FUNC_START(handle_tlb_modify)
>> csrwr t0, EXCEPTION_KS0
>> csrwr t1, EXCEPTION_KS1
>> @@ -410,7 +450,8 @@ smp_pgtable_change_modify:
>>
>> #ifdef CONFIG_64BIT
>> vmalloc_modify:
>> - la.abs t1, swapper_pg_dir
>> + /* The first insn of vmalloc_done_modify overwrites ra */
>> +1: la.pcrel t1, ra, swapper_pg_dir
>> b vmalloc_done_modify
>> #endif
>>
>> @@ -482,10 +523,24 @@ tlb_huge_update_modify:
>> nopage_tlb_modify:
>> dbar 0
>> csrrd ra, EXCEPTION_KS2
>> - la.abs t0, tlb_do_page_fault_1
>> +2: la.pcrel t0, t1, tlb_do_page_fault_1
>> jr t0
>> SYM_FUNC_END(handle_tlb_modify)
>>
>> +SYM_DATA_START(rel_handle_tlb_modify)
>> +#ifdef CONFIG_64BIT
>> + LONG 2
>> +
>> + LONG 1b - handle_tlb_modify
>> + LONG swapper_pg_dir
>> +#else
>> + LONG 1
>> +#endif
>> +
>> + LONG 2b - handle_tlb_modify
>> + LONG tlb_do_page_fault_1
>> +SYM_DATA_END(rel_handle_tlb_modify)
>> +
>> SYM_FUNC_START(handle_tlb_refill)
>> csrwr t0, LOONGARCH_CSR_TLBRSAVE
>> csrrd t0, LOONGARCH_CSR_PGD
>> --
>> 2.37.3
>>


2023-02-10 09:37:10

by Youling Tang

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] LoongArch: Add support for kernel address space layout randomization (KASLR)



On 02/10/2023 05:06 PM, Youling Tang wrote:
>
>
> On 02/10/2023 04:47 PM, Youling Tang wrote:
>> This patch adds support for relocating the kernel to a random address.
>>
>> Entropy is derived from the banner, which will change every build and
>> random_get_entropy() which should provide additional runtime entropy.
>>
>> The kernel is relocated by up to RANDOMIZE_BASE_MAX_OFFSET bytes from
>> its link address. Because relocation happens so early in the kernel boot,
>> the amount of physical memory has not yet been determined. This means
>> the only way to limit relocation within the available memory is via
>> Kconfig. Limit the maximum value of RANDOMIZE_BASE_MAX_OFFSET to
>> 256M(0x10000000) because our memory layout has many holes.
>>
>> Signed-off-by: Youling Tang <[email protected]>
>> Signed-off-by: Xi Ruoyao <[email protected]> # Fix compiler warnings
>> ---
>> arch/loongarch/Kconfig | 23 +++++
>> arch/loongarch/kernel/head.S | 14 ++-
>> arch/loongarch/kernel/relocate.c | 143 ++++++++++++++++++++++++++++++-
>> 3 files changed, 176 insertions(+), 4 deletions(-)
>>
>> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
>> index 089a4695b1b3..f0a070bd7254 100644
>> --- a/arch/loongarch/Kconfig
>> +++ b/arch/loongarch/Kconfig
>> @@ -489,6 +489,29 @@ config RELOCATABLE
>> kernel binary at runtime to a different virtual address than the
>> address it was linked at.
>>
>> +config RANDOMIZE_BASE
>> + bool "Randomize the address of the kernel image (KASLR)"
>> + depends on RELOCATABLE
>> + help
>> + Randomizes the physical and virtual address at which the
>> + kernel image is loaded, as a security feature that
>> + deters exploit attempts relying on knowledge of the location
>> + of kernel internals.
>> +
>> + The kernel will be offset by up to RANDOMIZE_BASE_MAX_OFFSET.
>> +
>> + If unsure, say N.
>> +
>> +config RANDOMIZE_BASE_MAX_OFFSET
>> + hex "Maximum KASLR offset" if EXPERT
>> + depends on RANDOMIZE_BASE
>> + range 0x0 0x10000000 if 64BIT
>> + default "0x01000000"
>> + help
>> + When KASLR is active, this provides the maximum offset that will
>> + be applied to the kernel image.
>> +
>> +
>> config SECCOMP
>> bool "Enable seccomp to safely compute untrusted bytecode"
>> depends on PROC_FS
>> diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
>> index 499edc80d8ab..b12f459ad73a 100644
>> --- a/arch/loongarch/kernel/head.S
>> +++ b/arch/loongarch/kernel/head.S
>> @@ -87,10 +87,22 @@ SYM_CODE_START(kernel_entry) # kernel
>> entry point
>> set_saved_sp sp, t0, t1
>>
>> #ifdef CONFIG_RELOCATABLE
>> +#ifdef CONFIG_RANDOMIZE_BASE
>> + bl do_kaslr
>> +
>> + /* Repoint the sp into the new kernel image */
>> + PTR_LI sp, (_THREAD_SIZE - PT_SIZE)
>> + PTR_ADD sp, sp, tp
>> + set_saved_sp sp, t0, t1
>> +
>> + /* do_kaslr returns the new kernel image entry point */
>> + jr a0
>> + ASM_BUG()
>> +#else
>> /* Apply the relocations */
>> bl relocate_kernel
>> #endif
>> -
>> +#endif
>> bl start_kernel
>> ASM_BUG()
>>
>> diff --git a/arch/loongarch/kernel/relocate.c
>> b/arch/loongarch/kernel/relocate.c
>> index 91ce92433bab..5266f23a3006 100644
>> --- a/arch/loongarch/kernel/relocate.c
>> +++ b/arch/loongarch/kernel/relocate.c
>> @@ -9,19 +9,21 @@
>> #include <linux/kernel.h>
>> #include <linux/printk.h>
>> #include <linux/panic_notifier.h>
>> +#include <linux/start_kernel.h>
>> +#include <asm/bootinfo.h>
>> +#include <asm/early_ioremap.h>
>> #include <asm/sections.h>
>>
>> #define RELOCATED(x) ((void *)((long)x + reloc_offset))
>> +#define RELOCATED_KASLR(x) ((void *)((long)x + offset))
>>
>> extern long __rela_dyn_start;
>> extern long __rela_dyn_end;
>>
>> static unsigned long reloc_offset;
>>
>> -void __init relocate_kernel(void)
>> +static inline __init void relocate_relative(void)
>> {
>> - reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
>> -
>> if (reloc_offset) {
>> Elf64_Rela *rela, *rela_end;
>> rela = (Elf64_Rela *)&__rela_dyn_start;
>> @@ -43,6 +45,141 @@ void __init relocate_kernel(void)
>> }
>> }
>>
>> +#ifdef CONFIG_RANDOMIZE_BASE
>> +static inline __init unsigned long rotate_xor(unsigned long hash,
>> + const void *area, size_t size)
>> +{
>> + size_t i;
>> + unsigned long *ptr = (unsigned long *)area;
>> +
>> + for (i = 0; i < size / sizeof(hash); i++) {
>> + /* Rotate by odd number of bits and XOR. */
>> + hash = (hash << ((sizeof(hash) * 8) - 7)) | (hash >> 7);
>> + hash ^= ptr[i];
>> + }
>> +
>> + return hash;
>> +}
>> +
>> +static inline __init unsigned long get_random_boot(void)
>> +{
>> + unsigned long entropy = random_get_entropy();
>> + unsigned long hash = 0;
>> +
>> + /* Attempt to create a simple but unpredictable starting entropy. */
>> + hash = rotate_xor(hash, linux_banner, strlen(linux_banner));
>> +
>> + /* Add in any runtime entropy we can get */
>> + hash = rotate_xor(hash, &entropy, sizeof(entropy));
>> +
>> + return hash;
>> +}
>> +
>> +static inline __init bool kaslr_disabled(void)
>> +{
>> + char *str;
>> +
>> + str = strstr(boot_command_line, "nokaslr");
>> + if (str == boot_command_line || (str > boot_command_line && *(str
>> - 1) == ' '))
>> + return true;
>> +
>> + return false;
>> +}
>> +
>> +/* Choose a new address for the kernel */
>> +static inline void __init *determine_relocation_address(void)
>> +{
>> + unsigned long kernel_length;
>> + void *dest = _text;
>> + unsigned long offset;
>> +
>> + if (kaslr_disabled())
>> + return dest;
>> +
>> + kernel_length = (long)_end - (long)_text;
>> +
>> + offset = get_random_boot() << 16;
>> + offset &= (CONFIG_RANDOMIZE_BASE_MAX_OFFSET - 1);
>> + if (offset < kernel_length)
>> + offset += ALIGN(kernel_length, 0xffff);
>> +
>> + return RELOCATED_KASLR(dest);
>> +}
>> +
>> +static inline int __init relocation_addr_valid(void *loc_new)
>> +{
>> + if ((unsigned long)loc_new & 0x00000ffff) {
>> + /* Inappropriately aligned new location */
>> + return 0;
>> + }
>> + if ((unsigned long)loc_new < (unsigned long)_end) {
>> + /* New location overlaps original kernel */
>> + return 0;
>> + }
>> + return 1;
>> +}
>> +
>> +static inline void __init update_reloc_offset(unsigned long *addr,
>> long offset)
>> +{
>> + unsigned long *new_addr = (unsigned long *)RELOCATED_KASLR(addr);
>> +
>> + *new_addr = (unsigned long)offset;
>> +}
>> +
>> +void *__init do_kaslr(void)
>> +{
>> + void *loc_new;
>> + unsigned long kernel_length;
>> + long offset = 0;
>> + /* Default to original kernel entry point */
>> + void *kernel_entry = start_kernel;
>> + char *cmdline = early_ioremap(fw_arg1, COMMAND_LINE_SIZE);
>> +
>> + /* Boot command line was passed in fw_arg1 */
>> + strscpy(boot_command_line, cmdline, COMMAND_LINE_SIZE);
>> +
>> + kernel_length = (long)(_end) - (long)(_text);
>> +
>> + loc_new = determine_relocation_address();
>> +
>> + /* Sanity check relocation address */
>> + if (relocation_addr_valid(loc_new))
>> + offset = (unsigned long)loc_new - (unsigned long)(_text);
>> +
>> + if (offset) {
>> + /* Copy the kernel to it's new location */
>> + memcpy(loc_new, _text, kernel_length);
>> +
>> + /* Sync the caches ready for execution of new kernel */
>> + __asm__ __volatile__ (
>> + "ibar 0 \t\n"
>> + "dbar 0 \t\n");
>> +
>> + reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
>> + reloc_offset += offset;
>> +
>> + relocate_relative();
>> +
>> + /* The current thread is now within the relocated image */
>> + __current_thread_info = RELOCATED_KASLR(__current_thread_info);
>> +
>> + /* Return the new kernel's entry point */
>> + kernel_entry = RELOCATED_KASLR(start_kernel);
>> +
>> + update_reloc_offset(&reloc_offset, offset);
>> + }
>
> Self review:
>
> There is a problem with do_kaslr implementation, which will be fixed in
> the next version.

When offset is 0, but reloc_offset is not 0, relocate_relative() also
needs to be executed.

It will be modified as follows:

--- a/arch/loongarch/kernel/relocate.c
+++ b/arch/loongarch/kernel/relocate.c
@@ -146,6 +146,8 @@ void *__init do_kaslr(void)
if (relocation_addr_valid(loc_new))
offset = (unsigned long)loc_new - (unsigned long)(_text);

+ reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
+
if (offset) {
/* Copy the kernel to it's new location */
memcpy(loc_new, _text, kernel_length);
@@ -155,11 +157,8 @@ void *__init do_kaslr(void)
"ibar 0 \t\n"
"dbar 0 \t\n");

- reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
reloc_offset += offset;

- relocate_relative();
-
/* The current thread is now within the relocated image */
__current_thread_info =
RELOCATED_KASLR(__current_thread_info);

@@ -169,6 +168,9 @@ void *__init do_kaslr(void)
update_reloc_offset(&reloc_offset, offset);
}

+ if (reloc_offset)
+ relocate_relative();
+
return kernel_entry;
}
#endif
@@ -177,7 +179,8 @@ void __init relocate_kernel(void)
{
reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;

- relocate_relative();
+ if (reloc_offset)
+ relocate_relative();
}

>
>> +
>> + return kernel_entry;
>> +}
>> +#endif
>> +
>> +void __init relocate_kernel(void)
>> +{
>> + reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
>> +
>> + relocate_relative();
>> +}
>> +
>> /*
>> * Show relocation information on panic.
>> */
>>
>


2023-02-10 18:02:59

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v4 4/5] LoongArch: Add support for kernel relocation

Hi Youling,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.2-rc7 next-20230210]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Youling-Tang/LoongArch-Use-la-pcrel-instead-of-la-abs-when-it-s-trivially-possible/20230210-165022
patch link: https://lore.kernel.org/r/1676018856-26520-5-git-send-email-tangyouling%40loongson.cn
patch subject: [PATCH v4 4/5] LoongArch: Add support for kernel relocation
config: loongarch-allmodconfig (https://download.01.org/0day-ci/archive/20230211/[email protected]/config)
compiler: loongarch64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/26dc7750408c7f232632db44fab905df7b48d83c
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Youling-Tang/LoongArch-Use-la-pcrel-instead-of-la-abs-when-it-s-trivially-possible/20230210-165022
git checkout 26dc7750408c7f232632db44fab905df7b48d83c
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=loongarch olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=loongarch SHELL=/bin/bash arch/loongarch/kernel/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/

Note: functions only called from assembly code should be annotated with the asmlinkage attribute
All warnings (new ones prefixed by >>):

>> arch/loongarch/kernel/relocate.c:21:13: warning: no previous prototype for 'relocate_kernel' [-Wmissing-prototypes]
21 | void __init relocate_kernel(void)
| ^~~~~~~~~~~~~~~


vim +/relocate_kernel +21 arch/loongarch/kernel/relocate.c

20
> 21 void __init relocate_kernel(void)
22 {
23 reloc_offset = (unsigned long)_text - VMLINUX_LOAD_ADDRESS;
24
25 if (reloc_offset) {
26 Elf64_Rela *rela, *rela_end;
27 rela = (Elf64_Rela *)&__rela_dyn_start;
28 rela_end = (Elf64_Rela *)&__rela_dyn_end;
29
30 for ( ; rela < rela_end; rela++) {
31 Elf64_Addr addr = rela->r_offset;
32 Elf64_Addr relocated_addr = rela->r_addend;
33
34 if (rela->r_info != R_LARCH_RELATIVE)
35 continue;
36
37 if (relocated_addr >= VMLINUX_LOAD_ADDRESS)
38 relocated_addr =
39 (Elf64_Addr)RELOCATED(relocated_addr);
40
41 *(Elf64_Addr *)RELOCATED(addr) = relocated_addr;
42 }
43 }
44 }
45

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

2023-02-16 02:33:15

by Youling Tang

[permalink] [raw]
Subject: Re: [PATCH v4 2/5] LoongArch: Use la.pcrel instead of la.abs for exception handlers

Hi folks,

On 02/10/2023 05:18 PM, Youling Tang wrote:
>
>
> On 02/10/2023 05:09 PM, Huacai Chen wrote:
>> Hi, Youling and Ruoyao,
>>
>> Thank you very much for implementing the per-node exceptions. But I
>> want to know if the per-node solution is really worthy for a PIE
>> kernel. So, could you please test the performance? Maybe we can reduce
>> the complexity if we give up the per-node solution.

Tested on Loongson-3C5000L-LL machine, using CLFS7.3 system.

- nopernode:
Based on the v1 patch method, and remove the else branch process in
setup_tlb_handler().

- pernode: Based on the v4 patch method.

- pie: Enable RANDOMIZE_BASE (KASLR).

- nopie: Disable RANDOMIZE_BASE and RELOCATABLE.


The UnixBench test results are as follows:

- nopernode-nopie: 3938.7

- pernode-nopie: 4062.2

- nopernode-pie: 4009.7

- pernode-pie: 4028.7

In general, `pernode` is higher than `nopernode`, and `nopie` is higher
than `pie`. (except that nopernode-pie is higher than nopernode-nopie,
which is not as expected, which may be caused by the instability of the
machine).

Everyone is more inclined to use `pernode` or `nopernode` to implement
in the exception handling process?

Youling.


2023-02-16 06:56:37

by Huacai Chen

[permalink] [raw]
Subject: Re: [PATCH v4 2/5] LoongArch: Use la.pcrel instead of la.abs for exception handlers

On Thu, Feb 16, 2023 at 10:32 AM Youling Tang <[email protected]> wrote:
>
> Hi folks,
>
> On 02/10/2023 05:18 PM, Youling Tang wrote:
> >
> >
> > On 02/10/2023 05:09 PM, Huacai Chen wrote:
> >> Hi, Youling and Ruoyao,
> >>
> >> Thank you very much for implementing the per-node exceptions. But I
> >> want to know if the per-node solution is really worthy for a PIE
> >> kernel. So, could you please test the performance? Maybe we can reduce
> >> the complexity if we give up the per-node solution.
>
> Tested on Loongson-3C5000L-LL machine, using CLFS7.3 system.
>
> - nopernode:
> Based on the v1 patch method, and remove the else branch process in
> setup_tlb_handler().
>
> - pernode: Based on the v4 patch method.
>
> - pie: Enable RANDOMIZE_BASE (KASLR).
>
> - nopie: Disable RANDOMIZE_BASE and RELOCATABLE.
>
>
> The UnixBench test results are as follows:
>
> - nopernode-nopie: 3938.7
>
> - pernode-nopie: 4062.2
>
> - nopernode-pie: 4009.7
>
> - pernode-pie: 4028.7
>
> In general, `pernode` is higher than `nopernode`, and `nopie` is higher
> than `pie`. (except that nopernode-pie is higher than nopernode-nopie,
> which is not as expected, which may be caused by the instability of the
> machine).
>
> Everyone is more inclined to use `pernode` or `nopernode` to implement
> in the exception handling process?
From my point of view, for the PIE kernel the performance difference
between pernode and nopoernode is negligible. On the other hand,
pernode implementation needs some compiler hackings and makes the
logic significantly complex. So I prefer to remove the pernode
exception support.

Huacai
>
> Youling.
>
>

2023-02-16 06:59:30

by Jinyang He

[permalink] [raw]
Subject: Re: [PATCH v4 2/5] LoongArch: Use la.pcrel instead of la.abs for exception handlers

On 2023-02-16 10:32, Youling Tang wrote:

> Hi folks,
>
> On 02/10/2023 05:18 PM, Youling Tang wrote:
>>
>>
>> On 02/10/2023 05:09 PM, Huacai Chen wrote:
>>> Hi, Youling and Ruoyao,
>>>
>>> Thank you very much for implementing the per-node exceptions. But I
>>> want to know if the per-node solution is really worthy for a PIE
>>> kernel. So, could you please test the performance? Maybe we can reduce
>>> the complexity if we give up the per-node solution.
>
> Tested on Loongson-3C5000L-LL machine, using CLFS7.3 system.
>
> - nopernode:
>   Based on the v1 patch method, and remove the else branch process in
>   setup_tlb_handler().
>
> - pernode: Based on the v4 patch method.
>
> - pie: Enable RANDOMIZE_BASE (KASLR).
>
> - nopie: Disable RANDOMIZE_BASE and RELOCATABLE.
>
>
> The UnixBench test results are as follows:
>
> - nopernode-nopie: 3938.7
>
> - pernode-nopie: 4062.2
>
> - nopernode-pie: 4009.7
>
> - pernode-pie: 4028.7
>
> In general, `pernode` is higher than `nopernode`, and `nopie` is higher
> than `pie`. (except that nopernode-pie is higher than nopernode-nopie,
> which is not as expected, which may be caused by the instability of the
> machine).
>
> Everyone is more inclined to use `pernode` or `nopernode` to implement
> in the exception handling process?
>
> Youling.

Hi, Youling,


Thanks for your test results.


I did an informal patch to keep la.abs, which think la.abs as a macro.
just qemu test.

To test this patch, patch the [PATCH v4 1/5] [PATCH v4 3/5] as prediction.

This following patch just provides a method. I'm busy with other things.
Hopefully it will help you simplify [PATCH v4 2/5].


Thanks,

Jinyang



diff --git a/arch/loongarch/include/asm/asmmacro.h
b/arch/loongarch/include/asm/asmmacro.h
index 328bb956f241..6ebad458d662 100644
--- a/arch/loongarch/include/asm/asmmacro.h
+++ b/arch/loongarch/include/asm/asmmacro.h
@@ -667,4 +667,19 @@
     nor    \dst, \src, zero
 .endm

+.macro la.abs reg, sym
+766:
+    nop
+    nop
+    nop
+    nop
+    .pushsection ".laabs", "aw", %progbits
+768:
+    .word 768b-766b
+    parse_r regno, \reg
+    .word regno
+    .dword \sym
+    .popsection
+.endm
+
 #endif /* _ASM_ASMMACRO_H */
diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
index d2ac26b5b22b..3b273f05be8c 100644
--- a/arch/loongarch/kernel/head.S
+++ b/arch/loongarch/kernel/head.S
@@ -86,6 +86,7 @@ SYM_CODE_START(kernel_entry)            # kernel entry
point
     PTR_ADD        sp, sp, tp
     set_saved_sp    sp, t0, t1

+    bl        relocate_laabs
     bl        start_kernel
     ASM_BUG()

diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
index 4344502c0b31..9f8833a2524a 100644
--- a/arch/loongarch/kernel/setup.c
+++ b/arch/loongarch/kernel/setup.c
@@ -582,3 +582,30 @@ void __init setup_arch(char **cmdline_p)

     paging_init();
 }
+
+void __init relocate_laabs(void)
+{
+    extern void *__laabs_begin;
+    extern void *__laabs_end;
+    struct laabs {
+        int offset;
+        int reg;
+        long symvalue;
+    } *p;
+
+    for (p = (void *)&__laabs_begin; (void *)p < (void *)&__laabs_end; p++)
+    {
+        int lu12iw, ori, lu32id, lu52id;
+        long v = p->symvalue;
+        int reg = p->reg;
+        int *insn = (void *)p - p->offset;
+        lu12iw = 0x14000000 | reg | (((v & 0xfffff000) >> 12) << 5);
+        ori = 0x03800000 | reg | (reg<<5) | ((v & 0xfff) << 10);
+        lu32id = 0x16000000 | reg | (((v & 0x000fffff00000000) >> 32)
<< 5);
+        lu52id = 0x03000000 | reg | (reg<<5) | (((v >> 52) & 0xfff) << 10);
+        insn[0] = lu12iw;
+        insn[1] = ori;
+        insn[2] = lu32id;
+        insn[3] = lu52id;
+    }
+}
diff --git a/arch/loongarch/kernel/vmlinux.lds.S
b/arch/loongarch/kernel/vmlinux.lds.S
index 733b16e8d55d..4d128e089393 100644
--- a/arch/loongarch/kernel/vmlinux.lds.S
+++ b/arch/loongarch/kernel/vmlinux.lds.S
@@ -66,6 +66,13 @@ SECTIONS
         __alt_instructions_end = .;
     }

+    . = ALIGN(4);
+    .laabs : AT(ADDR(.laabs) - LOAD_OFFSET) {
+        __laabs_begin = .;
+        *(.laabs)
+        __laabs_end = .;
+    }
+
     .got : ALIGN(16) { *(.got) }
     .plt : ALIGN(16) { *(.plt) }
     .got.plt : ALIGN(16) { *(.got.plt) }





2023-02-16 07:10:39

by Xi Ruoyao

[permalink] [raw]
Subject: Re: [PATCH v4 2/5] LoongArch: Use la.pcrel instead of la.abs for exception handlers

On Thu, 2023-02-16 at 14:59 +0800, Jinyang He wrote:
> +.macro la.abs reg, sym
> +766:
> +    nop
> +    nop
> +    nop
> +    nop

In the "formal" version we can code

lu12i.w reg, 0
ori reg, reg, 0
lu32i.d reg, 0
lu52i.d reg, reg, 0

here. Then we only need to fixup the immediate slot so we can avoid
using parse_r.


> +    .pushsection ".laabs", "aw", %progbits
> +768:
> +    .word 768b-766b
> +    parse_r regno, \reg
> +    .word regno
> +    .dword \sym
> +    .popsection
> +.endm

--
Xi Ruoyao <[email protected]>
School of Aerospace Science and Technology, Xidian University

2023-02-16 08:03:23

by Youling Tang

[permalink] [raw]
Subject: Re: [PATCH v4 2/5] LoongArch: Use la.pcrel instead of la.abs for exception handlers



On 02/16/2023 03:10 PM, Xi Ruoyao wrote:
> On Thu, 2023-02-16 at 14:59 +0800, Jinyang He wrote:
>> +.macro la.abs reg, sym
>> +766:
>> + nop
>> + nop
>> + nop
>> + nop
>
> In the "formal" version we can code
>
> lu12i.w reg, 0
> ori reg, reg, 0
> lu32i.d reg, 0
> lu52i.d reg, reg, 0
>
> here. Then we only need to fixup the immediate slot so we can avoid
> using parse_r.
>
>
>> + .pushsection ".laabs", "aw", %progbits
>> +768:
>> + .word 768b-766b
>> + parse_r regno, \reg
>> + .word regno
>> + .dword \sym
>> + .popsection
>> +.endm

I will try to modify a version for testing, using the following
definition, when the RELOCATABLE is turned on, the "la.abs macro" is
used, otherwise the "la.abs pseudo instruction" is still used as before.

#ifdef CONFIG_RELOCATABLE
.macro la.abs reg, sym
lu12i.w reg, 0
ori reg, reg, 0
lu32i.d reg, 0
lu52i.d reg, reg, 0
.endm
#endif

Youling.


2023-02-16 11:19:06

by Youling Tang

[permalink] [raw]
Subject: Re: [PATCH v4 2/5] LoongArch: Use la.pcrel instead of la.abs for exception handlers


On 02/16/2023 04:03 PM, Youling Tang wrote:
>
>
> On 02/16/2023 03:10 PM, Xi Ruoyao wrote:
>> On Thu, 2023-02-16 at 14:59 +0800, Jinyang He wrote:
>>> +.macro la.abs reg, sym
>>> +766:
>>> + nop
>>> + nop
>>> + nop
>>> + nop
>>
>> In the "formal" version we can code
>>
>> lu12i.w reg, 0
>> ori reg, reg, 0
>> lu32i.d reg, 0
>> lu52i.d reg, reg, 0
>>
>> here. Then we only need to fixup the immediate slot so we can avoid
>> using parse_r.
>>
>>
>>> + .pushsection ".laabs", "aw", %progbits
>>> +768:
>>> + .word 768b-766b
>>> + parse_r regno, \reg
>>> + .word regno
>>> + .dword \sym
>>> + .popsection
>>> +.endm
>
> I will try to modify a version for testing, using the following
> definition, when the RELOCATABLE is turned on, the "la.abs macro" is
> used, otherwise the "la.abs pseudo instruction" is still used as before.
>
> #ifdef CONFIG_RELOCATABLE
> .macro la.abs reg, sym
> lu12i.w reg, 0
> ori reg, reg, 0
> lu32i.d reg, 0
> lu52i.d reg, reg, 0
> .endm
> #endif

On the basis of the v4 patch set, remove patch2, and then add the
following patches, and the test is successful on qemu.

If this method is more acceptable to everyone, I will send v5.

diff --git a/arch/loongarch/include/asm/asmmacro.h
b/arch/loongarch/include/asm/asmmacro.h
index 328bb956f241..adb04ae6b208 100644
--- a/arch/loongarch/include/asm/asmmacro.h
+++ b/arch/loongarch/include/asm/asmmacro.h
@@ -667,4 +667,19 @@
nor \dst, \src, zero
.endm

+#ifdef CONFIG_RELOCATABLE
+.macro la.abs reg, sym
+766:
+ lu12i.w \reg, 0
+ ori \reg, \reg, 0
+ lu32i.d \reg, 0
+ lu52i.d \reg, \reg, 0
+ .pushsection ".laabs", "aw", %progbits
+768:
+ .dword 768b-766b
+ .dword \sym
+ .popsection
+.endm
+#endif
+
#endif /* _ASM_ASMMACRO_H */
diff --git a/arch/loongarch/kernel/relocate.c
b/arch/loongarch/kernel/relocate.c
index 7d19cc0d2185..7ad327a554f9 100644
--- a/arch/loongarch/kernel/relocate.c
+++ b/arch/loongarch/kernel/relocate.c
@@ -12,6 +12,7 @@
#include <linux/start_kernel.h>
#include <asm/bootinfo.h>
#include <asm/early_ioremap.h>
+#include <asm/inst.h>
#include <asm/sections.h>

#define RELOCATED(x) ((void *)((long)x + reloc_offset))
@@ -45,6 +46,32 @@ static inline __init void relocate_relative(void)
}
}

+static inline void __init relocate_laabs(long offset)
+{
+ extern void *__laabs_begin;
+ extern void *__laabs_end;
+ struct laabs {
+ long offset;
+ long symvalue;
+ } *p;
+
+ for (p = (void *)&__laabs_begin; (void *)p < (void
*)&__laabs_end; p++) {
+ long v = p->symvalue + reloc_offset;
+ union loongarch_instruction *insn = (void *)p -
p->offset + offset;
+ u32 lu12iw, ori, lu32id, lu52id;
+
+ 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;
+ }
+}
+
#ifdef CONFIG_RANDOMIZE_BASE
static inline __init unsigned long rotate_xor(unsigned long hash,
const void *area, size_t
size)
@@ -168,8 +195,10 @@ void *__init do_kaslr(void)
update_reloc_offset(&reloc_offset, offset);
}

- if (reloc_offset)
+ if (reloc_offset) {
relocate_relative();
+ relocate_laabs(offset);
+ }

return kernel_entry;
}
@@ -181,6 +210,8 @@ void __init relocate_kernel(void)

if (reloc_offset)
relocate_relative();
+
+ relocate_laabs(0);
}

/*
diff --git a/arch/loongarch/kernel/vmlinux.lds.S
b/arch/loongarch/kernel/vmlinux.lds.S
index aec0b6567d24..0e58c68bf427 100644
--- a/arch/loongarch/kernel/vmlinux.lds.S
+++ b/arch/loongarch/kernel/vmlinux.lds.S
@@ -66,6 +66,13 @@ SECTIONS
__alt_instructions_end = .;
}

+ . = ALIGN(8);
+ .laabs : AT(ADDR(.laabs) - LOAD_OFFSET) {
+ __laabs_begin = .;
+ *(.laabs)
+ __laabs_end = .;
+ }
+
.got : ALIGN(16) { *(.got) }


Youling.


2023-02-16 11:29:25

by Youling Tang

[permalink] [raw]
Subject: Re: [PATCH v4 2/5] LoongArch: Use la.pcrel instead of la.abs for exception handlers



On 02/16/2023 07:18 PM, Youling Tang wrote:
>
> On 02/16/2023 04:03 PM, Youling Tang wrote:
>>
>>
>> On 02/16/2023 03:10 PM, Xi Ruoyao wrote:
>>> On Thu, 2023-02-16 at 14:59 +0800, Jinyang He wrote:
>>>> +.macro la.abs reg, sym
>>>> +766:
>>>> + nop
>>>> + nop
>>>> + nop
>>>> + nop
>>>
>>> In the "formal" version we can code
>>>
>>> lu12i.w reg, 0
>>> ori reg, reg, 0
>>> lu32i.d reg, 0
>>> lu52i.d reg, reg, 0
>>>
>>> here. Then we only need to fixup the immediate slot so we can avoid
>>> using parse_r.
>>>
>>>
>>>> + .pushsection ".laabs", "aw", %progbits
>>>> +768:
>>>> + .word 768b-766b
>>>> + parse_r regno, \reg
>>>> + .word regno
>>>> + .dword \sym
>>>> + .popsection
>>>> +.endm
>>
>> I will try to modify a version for testing, using the following
>> definition, when the RELOCATABLE is turned on, the "la.abs macro" is
>> used, otherwise the "la.abs pseudo instruction" is still used as before.
>>
>> #ifdef CONFIG_RELOCATABLE
>> .macro la.abs reg, sym
>> lu12i.w reg, 0
>> ori reg, reg, 0
>> lu32i.d reg, 0
>> lu52i.d reg, reg, 0
>> .endm
>> #endif
>
> On the basis of the v4 patch set, remove patch2, and then add the
> following patches, and the test is successful on qemu.
>
> If this method is more acceptable to everyone, I will send v5.
>
> diff --git a/arch/loongarch/include/asm/asmmacro.h
> b/arch/loongarch/include/asm/asmmacro.h
> index 328bb956f241..adb04ae6b208 100644
> --- a/arch/loongarch/include/asm/asmmacro.h
> +++ b/arch/loongarch/include/asm/asmmacro.h
> @@ -667,4 +667,19 @@
> nor \dst, \src, zero
> .endm
>
> +#ifdef CONFIG_RELOCATABLE
> +.macro la.abs reg, sym
> +766:
> + lu12i.w \reg, 0
> + ori \reg, \reg, 0
> + lu32i.d \reg, 0
> + lu52i.d \reg, \reg, 0
> + .pushsection ".laabs", "aw", %progbits
> +768:
> + .dword 768b-766b
> + .dword \sym
> + .popsection
> +.endm
> +#endif
> +
> #endif /* _ASM_ASMMACRO_H */
> diff --git a/arch/loongarch/kernel/relocate.c
> b/arch/loongarch/kernel/relocate.c
> index 7d19cc0d2185..7ad327a554f9 100644
> --- a/arch/loongarch/kernel/relocate.c
> +++ b/arch/loongarch/kernel/relocate.c
> @@ -12,6 +12,7 @@
> #include <linux/start_kernel.h>
> #include <asm/bootinfo.h>
> #include <asm/early_ioremap.h>
> +#include <asm/inst.h>
> #include <asm/sections.h>
>
> #define RELOCATED(x) ((void *)((long)x + reloc_offset))
> @@ -45,6 +46,32 @@ static inline __init void relocate_relative(void)
> }
> }
>
> +static inline void __init relocate_laabs(long offset)
> +{
> + extern void *__laabs_begin;
> + extern void *__laabs_end;
> + struct laabs {
> + long offset;
> + long symvalue;
> + } *p;
> +
> + for (p = (void *)&__laabs_begin; (void *)p < (void
> *)&__laabs_end; p++) {
> + long v = p->symvalue + reloc_offset;
> + union loongarch_instruction *insn = (void *)p -
> p->offset + offset;
> + u32 lu12iw, ori, lu32id, lu52id;
> +
> + 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;
> + }
> +}
> +
> #ifdef CONFIG_RANDOMIZE_BASE
> static inline __init unsigned long rotate_xor(unsigned long hash,
> const void *area, size_t
> size)
> @@ -168,8 +195,10 @@ void *__init do_kaslr(void)
> update_reloc_offset(&reloc_offset, offset);
> }
>
> - if (reloc_offset)
> + if (reloc_offset) {
> relocate_relative();
> + relocate_laabs(offset);
> + }

Self review:

if (reloc_offset)
relocate_relative();

relocate_laabs(offset);

>
> return kernel_entry;
> }
> @@ -181,6 +210,8 @@ void __init relocate_kernel(void)
>
> if (reloc_offset)
> relocate_relative();
> +
> + relocate_laabs(0);
> }
>
> /*
> diff --git a/arch/loongarch/kernel/vmlinux.lds.S
> b/arch/loongarch/kernel/vmlinux.lds.S
> index aec0b6567d24..0e58c68bf427 100644
> --- a/arch/loongarch/kernel/vmlinux.lds.S
> +++ b/arch/loongarch/kernel/vmlinux.lds.S
> @@ -66,6 +66,13 @@ SECTIONS
> __alt_instructions_end = .;
> }
>
> + . = ALIGN(8);
> + .laabs : AT(ADDR(.laabs) - LOAD_OFFSET) {
> + __laabs_begin = .;
> + *(.laabs)
> + __laabs_end = .;
> + }
> +
> .got : ALIGN(16) { *(.got) }
>
>
> Youling.
>


2023-02-17 08:09:31

by Xi Ruoyao

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] LoongArch: Add support for kernel address space layout randomization (KASLR)

On Fri, 2023-02-10 at 16:47 +0800, Youling Tang wrote:

> +               /* Sync the caches ready for execution of new kernel
> */
> +               __asm__ __volatile__ (
> +                       "ibar 0 \t\n"
> +                       "dbar 0 \t\n");

I think we should add ::: "memory" here to prevent a future compiler
from being too smart.

Otherwise LGTM.

--
Xi Ruoyao <[email protected]>
School of Aerospace Science and Technology, Xidian University

2023-02-17 08:24:47

by Youling Tang

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] LoongArch: Add support for kernel address space layout randomization (KASLR)


On 02/17/2023 04:09 PM, Xi Ruoyao wrote:
> On Fri, 2023-02-10 at 16:47 +0800, Youling Tang wrote:
>
>> + /* Sync the caches ready for execution of new kernel
>> */
>> + __asm__ __volatile__ (
>> + "ibar 0 \t\n"
>> + "dbar 0 \t\n");
>
> I think we should add ::: "memory" here to prevent a future compiler
> from being too smart.
Got it.

Thanks,
Youling

>
> Otherwise LGTM.
>