Skip to content

Commit

Permalink
Merge pull request #307 from sterling-teng/linux-6.6.y
Browse files Browse the repository at this point in the history
LoongArch: Add kernel livepatching support
  • Loading branch information
Avenger-285714 authored Jul 4, 2024
2 parents bcde9be + 9911a88 commit 021d1d0
Show file tree
Hide file tree
Showing 56 changed files with 2,019 additions and 239 deletions.
6 changes: 6 additions & 0 deletions arch/loongarch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,22 @@ config LOONGARCH
select HAVE_KPROBES_ON_FTRACE
select HAVE_KRETPROBES
select HAVE_KVM
select HAVE_LIVEPATCH
select HAVE_MOD_ARCH_SPECIFIC
select HAVE_NMI
select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS
select HAVE_PCI
select HAVE_PERF_EVENTS
select HAVE_PERF_REGS
select HAVE_PERF_USER_STACK_DUMP
select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_RELIABLE_STACKTRACE if UNWINDER_ORC
select HAVE_RETHOOK
select HAVE_RSEQ
select HAVE_SAMPLE_FTRACE_DIRECT
select HAVE_SAMPLE_FTRACE_DIRECT_MULTI
select HAVE_SETUP_PER_CPU_AREA if NUMA
select HAVE_STACK_VALIDATION if HAVE_OBJTOOL
select HAVE_STACKPROTECTOR
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_TIF_NOHZ
Expand Down Expand Up @@ -619,6 +623,8 @@ config RANDOMIZE_BASE_MAX_OFFSET

This is limited by the size of the lower address memory, 256MB.

source "kernel/livepatch/Kconfig"

endmenu

config ARCH_SELECT_MEMORY_MODEL
Expand Down
11 changes: 11 additions & 0 deletions arch/loongarch/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,15 @@ config UNWINDER_PROLOGUE
Some of the addresses it reports may be incorrect (but better than the
Guess unwinder).

config UNWINDER_ORC
bool "ORC unwinder"
select OBJTOOL
help
This option enables the ORC (Oops Rewind Capability) unwinder for
unwinding kernel stack traces. It uses a custom data format which is
a simplified version of the DWARF Call Frame Information standard.

Enabling this option will increase the kernel's runtime memory usage
by roughly 2-4MB, depending on your kernel config.

endchoice
23 changes: 21 additions & 2 deletions arch/loongarch/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ endif
32bit-emul = elf32loongarch
64bit-emul = elf64loongarch

ifdef CONFIG_UNWINDER_ORC
orc_hash_h := arch/$(SRCARCH)/include/generated/asm/orc_hash.h
orc_hash_sh := $(srctree)/scripts/orc_hash.sh
targets += $(orc_hash_h)
quiet_cmd_orc_hash = GEN $@
cmd_orc_hash = mkdir -p $(dir $@); \
$(CONFIG_SHELL) $(orc_hash_sh) < $< > $@
$(orc_hash_h): $(srctree)/arch/loongarch/include/asm/orc_types.h $(orc_hash_sh) FORCE
$(call if_changed,orc_hash)
archprepare: $(orc_hash_h)
endif

ifdef CONFIG_DYNAMIC_FTRACE
KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
CC_FLAGS_FTRACE := -fpatchable-function-entry=2
Expand Down Expand Up @@ -68,8 +80,6 @@ LDFLAGS_vmlinux += -static -n -nostdlib
ifdef CONFIG_AS_HAS_EXPLICIT_RELOCS
cflags-y += $(call cc-option,-mexplicit-relocs)
KBUILD_CFLAGS_KERNEL += $(call cc-option,-mdirect-extern-access)
KBUILD_AFLAGS_MODULE += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax)
KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax)
else
cflags-y += $(call cc-option,-mno-explicit-relocs)
KBUILD_AFLAGS_KERNEL += -Wa,-mla-global-with-pcrel
Expand All @@ -78,6 +88,15 @@ KBUILD_AFLAGS_MODULE += -Wa,-mla-global-with-abs
KBUILD_CFLAGS_MODULE += -fplt -Wa,-mla-global-with-abs,-mla-local-with-abs
endif

KBUILD_AFLAGS += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax)
KBUILD_CFLAGS += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax)
KBUILD_AFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub)
KBUILD_CFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub)

ifdef CONFIG_OBJTOOL
KBUILD_CFLAGS += -fno-jump-tables
endif

ifeq ($(CONFIG_RELOCATABLE),y)
KBUILD_CFLAGS_KERNEL += -fPIE
LDFLAGS_vmlinux += -static -pie --no-dynamic-linker -z notext $(call ld-option, --apply-dynamic-relocs)
Expand Down
2 changes: 2 additions & 0 deletions arch/loongarch/include/asm/Kbuild
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
generated-y += orc_hash.h

generic-y += dma-contiguous.h
generic-y += mcs_spinlock.h
generic-y += parport.h
Expand Down
1 change: 1 addition & 0 deletions arch/loongarch/include/asm/bug.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
do { \
instrumentation_begin(); \
__BUG_FLAGS(BUGFLAG_WARNING|(flags)); \
annotate_reachable(); \
instrumentation_end(); \
} while (0)

Expand Down
2 changes: 2 additions & 0 deletions arch/loongarch/include/asm/exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <asm/ptrace.h>
#include <linux/kprobes.h>

extern void *exception_table[];

void show_registers(struct pt_regs *regs);

asmlinkage void cache_parity_error(void);
Expand Down
2 changes: 1 addition & 1 deletion arch/loongarch/include/asm/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ ftrace_regs_get_instruction_pointer(struct ftrace_regs *fregs)
static __always_inline void
ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
{
regs_set_return_value(&fregs->regs, ip);
instruction_pointer_set(&fregs->regs, ip);
}

#define ftrace_regs_get_argument(fregs, n) \
Expand Down
7 changes: 7 additions & 0 deletions arch/loongarch/include/asm/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define _ASM_MODULE_H

#include <asm/inst.h>
#include <asm/orc_types.h>
#include <asm-generic/module.h>

#define RELA_STACK_DEPTH 16
Expand All @@ -21,6 +22,12 @@ struct mod_arch_specific {
struct mod_section plt;
struct mod_section plt_idx;

#ifdef CONFIG_UNWINDER_ORC
unsigned int num_orcs;
int *orc_unwind_ip;
struct orc_entry *orc_unwind;
#endif

/* For CONFIG_DYNAMIC_FTRACE */
struct plt_entry *ftrace_trampolines;
};
Expand Down
18 changes: 18 additions & 0 deletions arch/loongarch/include/asm/orc_header.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */

#ifndef _ORC_HEADER_H
#define _ORC_HEADER_H

#include <linux/types.h>
#include <linux/compiler.h>
#include <asm/orc_hash.h>

/*
* The header is currently a 20-byte hash of the ORC entry definition; see
* scripts/orc_hash.sh.
*/
#define ORC_HEADER \
__used __section(".orc_header") __aligned(4) \
static const u8 orc_header[] = { ORC_HASH }

#endif /* _ORC_HEADER_H */
31 changes: 31 additions & 0 deletions arch/loongarch/include/asm/orc_lookup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _ORC_LOOKUP_H
#define _ORC_LOOKUP_H

/*
* This is a lookup table for speeding up access to the .orc_unwind table.
* Given an input address offset, the corresponding lookup table entry
* specifies a subset of the .orc_unwind table to search.
*
* Each block represents the end of the previous range and the start of the
* next range. An extra block is added to give the last range an end.
*
* The block size should be a power of 2 to avoid a costly 'div' instruction.
*
* A block size of 256 was chosen because it roughly doubles unwinder
* performance while only adding ~5% to the ORC data footprint.
*/
#define LOOKUP_BLOCK_ORDER 8
#define LOOKUP_BLOCK_SIZE (1 << LOOKUP_BLOCK_ORDER)

#ifndef LINKER_SCRIPT

extern unsigned int orc_lookup[];
extern unsigned int orc_lookup_end[];

#define LOOKUP_START_IP (unsigned long)_stext
#define LOOKUP_STOP_IP (unsigned long)_etext

#endif /* LINKER_SCRIPT */

#endif /* _ORC_LOOKUP_H */
58 changes: 58 additions & 0 deletions arch/loongarch/include/asm/orc_types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _ORC_TYPES_H
#define _ORC_TYPES_H

#include <linux/types.h>

/*
* The ORC_REG_* registers are base registers which are used to find other
* registers on the stack.
*
* ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
* address of the previous frame: the caller's SP before it called the current
* function.
*
* ORC_REG_UNDEFINED means the corresponding register's value didn't change in
* the current frame.
*
* The most commonly used base registers are SP and FP -- which the previous SP
* is usually based on -- and PREV_SP and UNDEFINED -- which the previous FP is
* usually based on.
*
* The rest of the base registers are needed for special cases like entry code
* and GCC realigned stacks.
*/
#define ORC_REG_UNDEFINED 0
#define ORC_REG_PREV_SP 1
#define ORC_REG_SP 2
#define ORC_REG_FP 3
#define ORC_REG_MAX 4

#define ORC_TYPE_UNDEFINED 0
#define ORC_TYPE_END_OF_STACK 1
#define ORC_TYPE_CALL 2
#define ORC_TYPE_REGS 3
#define ORC_TYPE_REGS_PARTIAL 4

#ifndef __ASSEMBLY__
/*
* This struct is more or less a vastly simplified version of the DWARF Call
* Frame Information standard. It contains only the necessary parts of DWARF
* CFI, simplified for ease of access by the in-kernel unwinder. It tells the
* unwinder how to find the previous SP and FP (and sometimes entry regs) on
* the stack for a given code address. Each instance of the struct corresponds
* to one or more code locations.
*/
struct orc_entry {
s16 sp_offset;
s16 fp_offset;
s16 ra_offset;
unsigned int sp_reg:4;
unsigned int fp_reg:4;
unsigned int ra_reg:4;
unsigned int type:3;
unsigned int signal:1;
};
#endif /* __ASSEMBLY__ */

#endif /* _ORC_TYPES_H */
3 changes: 3 additions & 0 deletions arch/loongarch/include/asm/stackframe.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <asm/asm-offsets.h>
#include <asm/loongarch.h>
#include <asm/thread_info.h>
#include <asm/unwind_hints.h>

/* Make the addition of cfi info a little easier. */
.macro cfi_rel_offset reg offset=0 docfi=0
Expand Down Expand Up @@ -162,6 +163,7 @@
li.w t0, CSR_CRMD_WE
csrxchg t0, t0, LOONGARCH_CSR_CRMD
#endif
UNWIND_HINT_REGS
.endm

.macro SAVE_ALL docfi=0
Expand Down Expand Up @@ -219,6 +221,7 @@

.macro RESTORE_SP_AND_RET docfi=0
cfi_ld sp, PT_R3, \docfi
UNWIND_HINT_FUNC
ertn
.endm

Expand Down
2 changes: 2 additions & 0 deletions arch/loongarch/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ register unsigned long current_stack_pointer __asm__("$sp");
#define TIF_LASX_CTX_LIVE 18 /* LASX context must be preserved */
#define TIF_USEDLBT 19 /* LBT was used by this task this quantum (SMP) */
#define TIF_LBT_CTX_LIVE 20 /* LBT context must be preserved */
#define TIF_PATCH_PENDING 21 /* pending live patching update */

#define _TIF_SIGPENDING (1<<TIF_SIGPENDING)
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
Expand All @@ -105,6 +106,7 @@ register unsigned long current_stack_pointer __asm__("$sp");
#define _TIF_LASX_CTX_LIVE (1<<TIF_LASX_CTX_LIVE)
#define _TIF_USEDLBT (1<<TIF_USEDLBT)
#define _TIF_LBT_CTX_LIVE (1<<TIF_LBT_CTX_LIVE)
#define _TIF_PATCH_PENDING (1<<TIF_PATCH_PENDING)

#endif /* __KERNEL__ */
#endif /* _ASM_THREAD_INFO_H */
20 changes: 18 additions & 2 deletions arch/loongarch/include/asm/unwind.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
enum unwinder_type {
UNWINDER_GUESS,
UNWINDER_PROLOGUE,
UNWINDER_ORC,
};

struct unwind_state {
Expand All @@ -24,7 +25,7 @@ struct unwind_state {
struct task_struct *task;
bool first, error, reset;
int graph_idx;
unsigned long sp, pc, ra;
unsigned long sp, fp, pc, ra;
};

bool default_next_frame(struct unwind_state *state);
Expand Down Expand Up @@ -61,14 +62,17 @@ static __always_inline void __unwind_start(struct unwind_state *state,
state->sp = regs->regs[3];
state->pc = regs->csr_era;
state->ra = regs->regs[1];
state->fp = regs->regs[22];
} else if (task && task != current) {
state->sp = thread_saved_fp(task);
state->pc = thread_saved_ra(task);
state->ra = 0;
state->fp = 0;
} else {
state->sp = (unsigned long)__builtin_frame_address(0);
state->pc = (unsigned long)__builtin_return_address(0);
state->ra = 0;
state->fp = 0;
}
state->task = task;
get_stack_info(state->sp, state->task, &state->stack_info);
Expand All @@ -77,6 +81,18 @@ static __always_inline void __unwind_start(struct unwind_state *state,

static __always_inline unsigned long __unwind_get_return_address(struct unwind_state *state)
{
return unwind_done(state) ? 0 : state->pc;
if (unwind_done(state))
return 0;

return __kernel_text_address(state->pc) ? state->pc : 0;
}

#ifdef CONFIG_UNWINDER_ORC
void unwind_init(void);
void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, void *orc, size_t orc_size);
#else
static inline void unwind_init(void) {}
static inline void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, void *orc, size_t orc_size) {}
#endif

#endif /* _ASM_UNWIND_H */
28 changes: 28 additions & 0 deletions arch/loongarch/include/asm/unwind_hints.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_LOONGARCH_UNWIND_HINTS_H
#define _ASM_LOONGARCH_UNWIND_HINTS_H

#include <linux/objtool.h>
#include <asm/orc_types.h>

#ifdef __ASSEMBLY__

.macro UNWIND_HINT_UNDEFINED
UNWIND_HINT type=UNWIND_HINT_TYPE_UNDEFINED
.endm

.macro UNWIND_HINT_END_OF_STACK
UNWIND_HINT type=UNWIND_HINT_TYPE_END_OF_STACK
.endm

.macro UNWIND_HINT_REGS
UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_REGS
.endm

.macro UNWIND_HINT_FUNC
UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_CALL
.endm

#endif /* __ASSEMBLY__ */

#endif /* _ASM_LOONGARCH_UNWIND_HINTS_H */
Loading

0 comments on commit 021d1d0

Please sign in to comment.