Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'loongarch-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson

Pull LoongArch updates from Huacai Chen:

- Add objtool support for LoongArch

- Add ORC stack unwinder support for LoongArch

- Add kernel livepatching support for LoongArch

- Select ARCH_HAS_CURRENT_STACK_POINTER in Kconfig

- Select HAVE_ARCH_USERFAULTFD_MINOR in Kconfig

- Some bug fixes and other small changes

* tag 'loongarch-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson:
LoongArch/crypto: Clean up useless assignment operations
LoongArch: Define the __io_aw() hook as mmiowb()
LoongArch: Remove superfluous flush_dcache_page() definition
LoongArch: Move {dmw,tlb}_virt_to_page() definition to page.h
LoongArch: Change __my_cpu_offset definition to avoid mis-optimization
LoongArch: Select HAVE_ARCH_USERFAULTFD_MINOR in Kconfig
LoongArch: Select ARCH_HAS_CURRENT_STACK_POINTER in Kconfig
LoongArch: Add kernel livepatching support
LoongArch: Add ORC stack unwinder support
objtool: Check local label in read_unwind_hints()
objtool: Check local label in add_dead_ends()
objtool/LoongArch: Enable orc to be built
objtool/x86: Separate arch-specific and generic parts
objtool/LoongArch: Implement instruction decoder
objtool/LoongArch: Enable objtool to be built

+2040 -266
+8
arch/loongarch/Kconfig
··· 15 15 select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE 16 16 select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI 17 17 select ARCH_HAS_CPU_FINALIZE_INIT 18 + select ARCH_HAS_CURRENT_STACK_POINTER 18 19 select ARCH_HAS_FORTIFY_SOURCE 19 20 select ARCH_HAS_KCOV 20 21 select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS ··· 105 104 select HAVE_ARCH_SECCOMP_FILTER 106 105 select HAVE_ARCH_TRACEHOOK 107 106 select HAVE_ARCH_TRANSPARENT_HUGEPAGE 107 + select HAVE_ARCH_USERFAULTFD_MINOR if USERFAULTFD 108 108 select HAVE_ASM_MODVERSIONS 109 109 select HAVE_CONTEXT_TRACKING_USER 110 110 select HAVE_C_RECORDMCOUNT ··· 135 133 select HAVE_KPROBES 136 134 select HAVE_KPROBES_ON_FTRACE 137 135 select HAVE_KRETPROBES 136 + select HAVE_LIVEPATCH 138 137 select HAVE_MOD_ARCH_SPECIFIC 139 138 select HAVE_NMI 139 + select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS 140 140 select HAVE_PCI 141 141 select HAVE_PERF_EVENTS 142 142 select HAVE_PERF_REGS 143 143 select HAVE_PERF_USER_STACK_DUMP 144 144 select HAVE_PREEMPT_DYNAMIC_KEY 145 145 select HAVE_REGS_AND_STACK_ACCESS_API 146 + select HAVE_RELIABLE_STACKTRACE if UNWINDER_ORC 146 147 select HAVE_RETHOOK 147 148 select HAVE_RSEQ 148 149 select HAVE_RUST 149 150 select HAVE_SAMPLE_FTRACE_DIRECT 150 151 select HAVE_SAMPLE_FTRACE_DIRECT_MULTI 151 152 select HAVE_SETUP_PER_CPU_AREA if NUMA 153 + select HAVE_STACK_VALIDATION if HAVE_OBJTOOL 152 154 select HAVE_STACKPROTECTOR 153 155 select HAVE_SYSCALL_TRACEPOINTS 154 156 select HAVE_TIF_NOHZ ··· 629 623 amount of physical RAM available in the target system. 630 624 631 625 This is limited by the size of the lower address memory, 256MB. 626 + 627 + source "kernel/livepatch/Kconfig" 632 628 633 629 endmenu 634 630
+11
arch/loongarch/Kconfig.debug
··· 26 26 Some of the addresses it reports may be incorrect (but better than the 27 27 Guess unwinder). 28 28 29 + config UNWINDER_ORC 30 + bool "ORC unwinder" 31 + select OBJTOOL 32 + help 33 + This option enables the ORC (Oops Rewind Capability) unwinder for 34 + unwinding kernel stack traces. It uses a custom data format which is 35 + a simplified version of the DWARF Call Frame Information standard. 36 + 37 + Enabling this option will increase the kernel's runtime memory usage 38 + by roughly 2-4MB, depending on your kernel config. 39 + 29 40 endchoice
+21 -2
arch/loongarch/Makefile
··· 26 26 32bit-emul = elf32loongarch 27 27 64bit-emul = elf64loongarch 28 28 29 + ifdef CONFIG_UNWINDER_ORC 30 + orc_hash_h := arch/$(SRCARCH)/include/generated/asm/orc_hash.h 31 + orc_hash_sh := $(srctree)/scripts/orc_hash.sh 32 + targets += $(orc_hash_h) 33 + quiet_cmd_orc_hash = GEN $@ 34 + cmd_orc_hash = mkdir -p $(dir $@); \ 35 + $(CONFIG_SHELL) $(orc_hash_sh) < $< > $@ 36 + $(orc_hash_h): $(srctree)/arch/loongarch/include/asm/orc_types.h $(orc_hash_sh) FORCE 37 + $(call if_changed,orc_hash) 38 + archprepare: $(orc_hash_h) 39 + endif 40 + 29 41 ifdef CONFIG_DYNAMIC_FTRACE 30 42 KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY 31 43 CC_FLAGS_FTRACE := -fpatchable-function-entry=2 ··· 84 72 KBUILD_CFLAGS_KERNEL += $(call cc-option,-fdirect-access-external-data) 85 73 KBUILD_AFLAGS_MODULE += $(call cc-option,-fno-direct-access-external-data) 86 74 KBUILD_CFLAGS_MODULE += $(call cc-option,-fno-direct-access-external-data) 87 - KBUILD_AFLAGS_MODULE += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) 88 - KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) 89 75 else 90 76 cflags-y += $(call cc-option,-mno-explicit-relocs) 91 77 KBUILD_AFLAGS_KERNEL += -Wa,-mla-global-with-pcrel 92 78 KBUILD_CFLAGS_KERNEL += -Wa,-mla-global-with-pcrel 93 79 KBUILD_AFLAGS_MODULE += -Wa,-mla-global-with-abs 94 80 KBUILD_CFLAGS_MODULE += -fplt -Wa,-mla-global-with-abs,-mla-local-with-abs 81 + endif 82 + 83 + KBUILD_AFLAGS += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) 84 + KBUILD_CFLAGS += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) 85 + KBUILD_AFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub) 86 + KBUILD_CFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub) 87 + 88 + ifdef CONFIG_OBJTOOL 89 + KBUILD_CFLAGS += -fno-jump-tables 95 90 endif 96 91 97 92 KBUILD_RUSTFLAGS += --target=$(objtree)/scripts/target.json
-2
arch/loongarch/crypto/crc32-loongarch.c
··· 44 44 45 45 CRC32(crc, value, w); 46 46 p += sizeof(u32); 47 - len -= sizeof(u32); 48 47 } 49 48 50 49 if (len & sizeof(u16)) { ··· 79 80 80 81 CRC32C(crc, value, w); 81 82 p += sizeof(u32); 82 - len -= sizeof(u32); 83 83 } 84 84 85 85 if (len & sizeof(u16)) {
+3
arch/loongarch/include/asm/Kbuild
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 + generated-y += orc_hash.h 3 + 2 4 generic-y += dma-contiguous.h 3 5 generic-y += mcs_spinlock.h 4 6 generic-y += parport.h 5 7 generic-y += early_ioremap.h 6 8 generic-y += qrwlock.h 9 + generic-y += qspinlock.h 7 10 generic-y += rwsem.h 8 11 generic-y += segment.h 9 12 generic-y += user.h
+1
arch/loongarch/include/asm/bug.h
··· 44 44 do { \ 45 45 instrumentation_begin(); \ 46 46 __BUG_FLAGS(BUGFLAG_WARNING|(flags)); \ 47 + annotate_reachable(); \ 47 48 instrumentation_end(); \ 48 49 } while (0) 49 50
-3
arch/loongarch/include/asm/cacheflush.h
··· 37 37 #define flush_icache_range local_flush_icache_range 38 38 #define flush_icache_user_range local_flush_icache_range 39 39 40 - #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0 41 - 42 40 #define flush_cache_all() do { } while (0) 43 41 #define flush_cache_mm(mm) do { } while (0) 44 42 #define flush_cache_dup_mm(mm) do { } while (0) ··· 45 47 #define flush_cache_vmap(start, end) do { } while (0) 46 48 #define flush_cache_vunmap(start, end) do { } while (0) 47 49 #define flush_icache_user_page(vma, page, addr, len) do { } while (0) 48 - #define flush_dcache_page(page) do { } while (0) 49 50 #define flush_dcache_mmap_lock(mapping) do { } while (0) 50 51 #define flush_dcache_mmap_unlock(mapping) do { } while (0) 51 52
+2
arch/loongarch/include/asm/exception.h
··· 6 6 #include <asm/ptrace.h> 7 7 #include <linux/kprobes.h> 8 8 9 + extern void *exception_table[]; 10 + 9 11 void show_registers(struct pt_regs *regs); 10 12 11 13 asmlinkage void cache_parity_error(void);
+2
arch/loongarch/include/asm/io.h
··· 71 71 #define memcpy_fromio(a, c, l) __memcpy_fromio((a), (c), (l)) 72 72 #define memcpy_toio(c, a, l) __memcpy_toio((c), (a), (l)) 73 73 74 + #define __io_aw() mmiowb() 75 + 74 76 #include <asm-generic/io.h> 75 77 76 78 #define ARCH_HAS_VALID_PHYS_ADDR_RANGE
+7
arch/loongarch/include/asm/module.h
··· 6 6 #define _ASM_MODULE_H 7 7 8 8 #include <asm/inst.h> 9 + #include <asm/orc_types.h> 9 10 #include <asm-generic/module.h> 10 11 11 12 #define RELA_STACK_DEPTH 16 ··· 21 20 struct mod_section got; 22 21 struct mod_section plt; 23 22 struct mod_section plt_idx; 23 + 24 + #ifdef CONFIG_UNWINDER_ORC 25 + unsigned int num_orcs; 26 + int *orc_unwind_ip; 27 + struct orc_entry *orc_unwind; 28 + #endif 24 29 25 30 /* For CONFIG_DYNAMIC_FTRACE */ 26 31 struct plt_entry *ftrace_trampolines;
+18
arch/loongarch/include/asm/orc_header.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + #ifndef _ORC_HEADER_H 4 + #define _ORC_HEADER_H 5 + 6 + #include <linux/types.h> 7 + #include <linux/compiler.h> 8 + #include <asm/orc_hash.h> 9 + 10 + /* 11 + * The header is currently a 20-byte hash of the ORC entry definition; see 12 + * scripts/orc_hash.sh. 13 + */ 14 + #define ORC_HEADER \ 15 + __used __section(".orc_header") __aligned(4) \ 16 + static const u8 orc_header[] = { ORC_HASH } 17 + 18 + #endif /* _ORC_HEADER_H */
+31
arch/loongarch/include/asm/orc_lookup.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _ORC_LOOKUP_H 3 + #define _ORC_LOOKUP_H 4 + 5 + /* 6 + * This is a lookup table for speeding up access to the .orc_unwind table. 7 + * Given an input address offset, the corresponding lookup table entry 8 + * specifies a subset of the .orc_unwind table to search. 9 + * 10 + * Each block represents the end of the previous range and the start of the 11 + * next range. An extra block is added to give the last range an end. 12 + * 13 + * The block size should be a power of 2 to avoid a costly 'div' instruction. 14 + * 15 + * A block size of 256 was chosen because it roughly doubles unwinder 16 + * performance while only adding ~5% to the ORC data footprint. 17 + */ 18 + #define LOOKUP_BLOCK_ORDER 8 19 + #define LOOKUP_BLOCK_SIZE (1 << LOOKUP_BLOCK_ORDER) 20 + 21 + #ifndef LINKER_SCRIPT 22 + 23 + extern unsigned int orc_lookup[]; 24 + extern unsigned int orc_lookup_end[]; 25 + 26 + #define LOOKUP_START_IP (unsigned long)_stext 27 + #define LOOKUP_STOP_IP (unsigned long)_etext 28 + 29 + #endif /* LINKER_SCRIPT */ 30 + 31 + #endif /* _ORC_LOOKUP_H */
+58
arch/loongarch/include/asm/orc_types.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _ORC_TYPES_H 3 + #define _ORC_TYPES_H 4 + 5 + #include <linux/types.h> 6 + 7 + /* 8 + * The ORC_REG_* registers are base registers which are used to find other 9 + * registers on the stack. 10 + * 11 + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the 12 + * address of the previous frame: the caller's SP before it called the current 13 + * function. 14 + * 15 + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in 16 + * the current frame. 17 + * 18 + * The most commonly used base registers are SP and FP -- which the previous SP 19 + * is usually based on -- and PREV_SP and UNDEFINED -- which the previous FP is 20 + * usually based on. 21 + * 22 + * The rest of the base registers are needed for special cases like entry code 23 + * and GCC realigned stacks. 24 + */ 25 + #define ORC_REG_UNDEFINED 0 26 + #define ORC_REG_PREV_SP 1 27 + #define ORC_REG_SP 2 28 + #define ORC_REG_FP 3 29 + #define ORC_REG_MAX 4 30 + 31 + #define ORC_TYPE_UNDEFINED 0 32 + #define ORC_TYPE_END_OF_STACK 1 33 + #define ORC_TYPE_CALL 2 34 + #define ORC_TYPE_REGS 3 35 + #define ORC_TYPE_REGS_PARTIAL 4 36 + 37 + #ifndef __ASSEMBLY__ 38 + /* 39 + * This struct is more or less a vastly simplified version of the DWARF Call 40 + * Frame Information standard. It contains only the necessary parts of DWARF 41 + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the 42 + * unwinder how to find the previous SP and FP (and sometimes entry regs) on 43 + * the stack for a given code address. Each instance of the struct corresponds 44 + * to one or more code locations. 45 + */ 46 + struct orc_entry { 47 + s16 sp_offset; 48 + s16 fp_offset; 49 + s16 ra_offset; 50 + unsigned int sp_reg:4; 51 + unsigned int fp_reg:4; 52 + unsigned int ra_reg:4; 53 + unsigned int type:3; 54 + unsigned int signal:1; 55 + }; 56 + #endif /* __ASSEMBLY__ */ 57 + 58 + #endif /* _ORC_TYPES_H */
+3
arch/loongarch/include/asm/page.h
··· 75 75 #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) 76 76 #define sym_to_pfn(x) __phys_to_pfn(__pa_symbol(x)) 77 77 78 + struct page *dmw_virt_to_page(unsigned long kaddr); 79 + struct page *tlb_virt_to_page(unsigned long kaddr); 80 + 78 81 #define virt_to_pfn(kaddr) PFN_DOWN(PHYSADDR(kaddr)) 79 82 80 83 #define virt_to_page(kaddr) \
+6 -1
arch/loongarch/include/asm/percpu.h
··· 29 29 __my_cpu_offset = off; 30 30 csr_write64(off, PERCPU_BASE_KS); 31 31 } 32 - #define __my_cpu_offset __my_cpu_offset 32 + 33 + #define __my_cpu_offset \ 34 + ({ \ 35 + __asm__ __volatile__("":"+r"(__my_cpu_offset)); \ 36 + __my_cpu_offset; \ 37 + }) 33 38 34 39 #define PERCPU_OP(op, asm_op, c_op) \ 35 40 static __always_inline unsigned long __percpu_##op(void *ptr, \
-3
arch/loongarch/include/asm/pgtable.h
··· 363 363 extern pgd_t swapper_pg_dir[]; 364 364 extern pgd_t invalid_pg_dir[]; 365 365 366 - struct page *dmw_virt_to_page(unsigned long kaddr); 367 - struct page *tlb_virt_to_page(unsigned long kaddr); 368 - 369 366 /* 370 367 * The following only work if pte_present() is true. 371 368 * Undefined behaviour if not..
-18
arch/loongarch/include/asm/qspinlock.h
··· 1 - /* SPDX-License-Identifier: GPL-2.0 */ 2 - #ifndef _ASM_QSPINLOCK_H 3 - #define _ASM_QSPINLOCK_H 4 - 5 - #include <asm-generic/qspinlock_types.h> 6 - 7 - #define queued_spin_unlock queued_spin_unlock 8 - 9 - static inline void queued_spin_unlock(struct qspinlock *lock) 10 - { 11 - compiletime_assert_atomic_type(lock->locked); 12 - c_sync(); 13 - WRITE_ONCE(lock->locked, 0); 14 - } 15 - 16 - #include <asm-generic/qspinlock.h> 17 - 18 - #endif /* _ASM_QSPINLOCK_H */
+3
arch/loongarch/include/asm/stackframe.h
··· 13 13 #include <asm/asm-offsets.h> 14 14 #include <asm/loongarch.h> 15 15 #include <asm/thread_info.h> 16 + #include <asm/unwind_hints.h> 16 17 17 18 /* Make the addition of cfi info a little easier. */ 18 19 .macro cfi_rel_offset reg offset=0 docfi=0 ··· 163 162 li.w t0, CSR_CRMD_WE 164 163 csrxchg t0, t0, LOONGARCH_CSR_CRMD 165 164 #endif 165 + UNWIND_HINT_REGS 166 166 .endm 167 167 168 168 .macro SAVE_ALL docfi=0 ··· 221 219 222 220 .macro RESTORE_SP_AND_RET docfi=0 223 221 cfi_ld sp, PT_R3, \docfi 222 + UNWIND_HINT_FUNC 224 223 ertn 225 224 .endm 226 225
+2
arch/loongarch/include/asm/thread_info.h
··· 86 86 #define TIF_LASX_CTX_LIVE 18 /* LASX context must be preserved */ 87 87 #define TIF_USEDLBT 19 /* LBT was used by this task this quantum (SMP) */ 88 88 #define TIF_LBT_CTX_LIVE 20 /* LBT context must be preserved */ 89 + #define TIF_PATCH_PENDING 21 /* pending live patching update */ 89 90 90 91 #define _TIF_SIGPENDING (1<<TIF_SIGPENDING) 91 92 #define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) ··· 106 105 #define _TIF_LASX_CTX_LIVE (1<<TIF_LASX_CTX_LIVE) 107 106 #define _TIF_USEDLBT (1<<TIF_USEDLBT) 108 107 #define _TIF_LBT_CTX_LIVE (1<<TIF_LBT_CTX_LIVE) 108 + #define _TIF_PATCH_PENDING (1<<TIF_PATCH_PENDING) 109 109 110 110 #endif /* __KERNEL__ */ 111 111 #endif /* _ASM_THREAD_INFO_H */
+18 -2
arch/loongarch/include/asm/unwind.h
··· 16 16 enum unwinder_type { 17 17 UNWINDER_GUESS, 18 18 UNWINDER_PROLOGUE, 19 + UNWINDER_ORC, 19 20 }; 20 21 21 22 struct unwind_state { ··· 25 24 struct task_struct *task; 26 25 bool first, error, reset; 27 26 int graph_idx; 28 - unsigned long sp, pc, ra; 27 + unsigned long sp, fp, pc, ra; 29 28 }; 30 29 31 30 bool default_next_frame(struct unwind_state *state); ··· 62 61 state->sp = regs->regs[3]; 63 62 state->pc = regs->csr_era; 64 63 state->ra = regs->regs[1]; 64 + state->fp = regs->regs[22]; 65 65 } else if (task && task != current) { 66 66 state->sp = thread_saved_fp(task); 67 67 state->pc = thread_saved_ra(task); 68 68 state->ra = 0; 69 + state->fp = 0; 69 70 } else { 70 71 state->sp = (unsigned long)__builtin_frame_address(0); 71 72 state->pc = (unsigned long)__builtin_return_address(0); 72 73 state->ra = 0; 74 + state->fp = 0; 73 75 } 74 76 state->task = task; 75 77 get_stack_info(state->sp, state->task, &state->stack_info); ··· 81 77 82 78 static __always_inline unsigned long __unwind_get_return_address(struct unwind_state *state) 83 79 { 84 - return unwind_done(state) ? 0 : state->pc; 80 + if (unwind_done(state)) 81 + return 0; 82 + 83 + return __kernel_text_address(state->pc) ? state->pc : 0; 85 84 } 85 + 86 + #ifdef CONFIG_UNWINDER_ORC 87 + void unwind_init(void); 88 + void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, void *orc, size_t orc_size); 89 + #else 90 + static inline void unwind_init(void) {} 91 + static inline void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, void *orc, size_t orc_size) {} 92 + #endif 93 + 86 94 #endif /* _ASM_UNWIND_H */
+28
arch/loongarch/include/asm/unwind_hints.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _ASM_LOONGARCH_UNWIND_HINTS_H 3 + #define _ASM_LOONGARCH_UNWIND_HINTS_H 4 + 5 + #include <linux/objtool.h> 6 + #include <asm/orc_types.h> 7 + 8 + #ifdef __ASSEMBLY__ 9 + 10 + .macro UNWIND_HINT_UNDEFINED 11 + UNWIND_HINT type=UNWIND_HINT_TYPE_UNDEFINED 12 + .endm 13 + 14 + .macro UNWIND_HINT_END_OF_STACK 15 + UNWIND_HINT type=UNWIND_HINT_TYPE_END_OF_STACK 16 + .endm 17 + 18 + .macro UNWIND_HINT_REGS 19 + UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_REGS 20 + .endm 21 + 22 + .macro UNWIND_HINT_FUNC 23 + UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_CALL 24 + .endm 25 + 26 + #endif /* __ASSEMBLY__ */ 27 + 28 + #endif /* _ASM_LOONGARCH_UNWIND_HINTS_H */
+4
arch/loongarch/kernel/Makefile
··· 3 3 # Makefile for the Linux/LoongArch kernel. 4 4 # 5 5 6 + OBJECT_FILES_NON_STANDARD_head.o := y 7 + 6 8 extra-y := vmlinux.lds 7 9 8 10 obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \ ··· 23 21 24 22 CFLAGS_module.o += $(call cc-option,-Wno-override-init,) 25 23 CFLAGS_syscall.o += $(call cc-option,-Wno-override-init,) 24 + CFLAGS_traps.o += $(call cc-option,-Wno-override-init,) 26 25 CFLAGS_perf_event.o += $(call cc-option,-Wno-override-init,) 27 26 28 27 ifdef CONFIG_FUNCTION_TRACER ··· 65 62 66 63 obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o 67 64 obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o 65 + obj-$(CONFIG_UNWINDER_ORC) += unwind_orc.o 68 66 69 67 obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_regs.o 70 68 obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
+5
arch/loongarch/kernel/entry.S
··· 14 14 #include <asm/regdef.h> 15 15 #include <asm/stackframe.h> 16 16 #include <asm/thread_info.h> 17 + #include <asm/unwind_hints.h> 17 18 18 19 .text 19 20 .cfi_sections .debug_frame 20 21 .align 5 21 22 SYM_CODE_START(handle_syscall) 23 + UNWIND_HINT_UNDEFINED 22 24 csrrd t0, PERCPU_BASE_KS 23 25 la.pcrel t1, kernelsp 24 26 add.d t1, t1, t0 ··· 59 57 cfi_st fp, PT_R22 60 58 61 59 SAVE_STATIC 60 + UNWIND_HINT_REGS 62 61 63 62 #ifdef CONFIG_KGDB 64 63 li.w t1, CSR_CRMD_WE ··· 78 75 _ASM_NOKPROBE(handle_syscall) 79 76 80 77 SYM_CODE_START(ret_from_fork) 78 + UNWIND_HINT_REGS 81 79 bl schedule_tail # a0 = struct task_struct *prev 82 80 move a0, sp 83 81 bl syscall_exit_to_user_mode ··· 88 84 SYM_CODE_END(ret_from_fork) 89 85 90 86 SYM_CODE_START(ret_from_kernel_thread) 87 + UNWIND_HINT_REGS 91 88 bl schedule_tail # a0 = struct task_struct *prev 92 89 move a0, s1 93 90 jirl ra, s0, 0
+7
arch/loongarch/kernel/fpu.S
··· 15 15 #include <asm/fpregdef.h> 16 16 #include <asm/loongarch.h> 17 17 #include <asm/regdef.h> 18 + #include <asm/unwind_hints.h> 18 19 19 20 #define FPU_REG_WIDTH 8 20 21 #define LSX_REG_WIDTH 16 ··· 527 526 .L_fpu_fault: 528 527 li.w a0, -EFAULT # failure 529 528 jr ra 529 + 530 + #ifdef CONFIG_CPU_HAS_LBT 531 + STACK_FRAME_NON_STANDARD _restore_fp 532 + STACK_FRAME_NON_STANDARD _restore_lsx 533 + STACK_FRAME_NON_STANDARD _restore_lasx 534 + #endif
+6
arch/loongarch/kernel/genex.S
··· 32 32 SYM_FUNC_END(__arch_cpu_idle) 33 33 34 34 SYM_CODE_START(handle_vint) 35 + UNWIND_HINT_UNDEFINED 35 36 BACKUP_T0T1 36 37 SAVE_ALL 37 38 la_abs t1, __arch_cpu_idle ··· 50 49 SYM_CODE_END(handle_vint) 51 50 52 51 SYM_CODE_START(except_vec_cex) 52 + UNWIND_HINT_UNDEFINED 53 53 b cache_parity_error 54 54 SYM_CODE_END(except_vec_cex) 55 55 ··· 69 67 .macro BUILD_HANDLER exception handler prep 70 68 .align 5 71 69 SYM_CODE_START(handle_\exception) 70 + UNWIND_HINT_UNDEFINED 72 71 666: 73 72 BACKUP_T0T1 74 73 SAVE_ALL ··· 80 77 668: 81 78 RESTORE_ALL_AND_RET 82 79 SYM_CODE_END(handle_\exception) 80 + .pushsection ".data", "aw", %progbits 83 81 SYM_DATA(unwind_hint_\exception, .word 668b - 666b) 82 + .popsection 84 83 .endm 85 84 86 85 BUILD_HANDLER ade ade badv ··· 99 94 BUILD_HANDLER reserved reserved none /* others */ 100 95 101 96 SYM_CODE_START(handle_sys) 97 + UNWIND_HINT_UNDEFINED 102 98 la_abs t0, handle_syscall 103 99 jr t0 104 100 SYM_CODE_END(handle_sys)
+3
arch/loongarch/kernel/lbt.S
··· 11 11 #include <asm/asm-offsets.h> 12 12 #include <asm/errno.h> 13 13 #include <asm/regdef.h> 14 + #include <asm/unwind_hints.h> 14 15 15 16 #define SCR_REG_WIDTH 8 16 17 ··· 154 153 .L_lbt_fault: 155 154 li.w a0, -EFAULT # failure 156 155 jr ra 156 + 157 + STACK_FRAME_NON_STANDARD _restore_ftop_context
+6
arch/loongarch/kernel/mcount_dyn.S
··· 73 73 SYM_FUNC_END(ftrace_stub) 74 74 75 75 SYM_CODE_START(ftrace_common) 76 + UNWIND_HINT_UNDEFINED 76 77 PTR_ADDI a0, ra, -8 /* arg0: ip */ 77 78 move a1, t0 /* arg1: parent_ip */ 78 79 la.pcrel t1, function_trace_op ··· 114 113 SYM_CODE_END(ftrace_common) 115 114 116 115 SYM_CODE_START(ftrace_caller) 116 + UNWIND_HINT_UNDEFINED 117 117 ftrace_regs_entry allregs=0 118 118 b ftrace_common 119 119 SYM_CODE_END(ftrace_caller) 120 120 121 121 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS 122 122 SYM_CODE_START(ftrace_regs_caller) 123 + UNWIND_HINT_UNDEFINED 123 124 ftrace_regs_entry allregs=1 124 125 b ftrace_common 125 126 SYM_CODE_END(ftrace_regs_caller) ··· 129 126 130 127 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 131 128 SYM_CODE_START(ftrace_graph_caller) 129 + UNWIND_HINT_UNDEFINED 132 130 PTR_L a0, sp, PT_ERA 133 131 PTR_ADDI a0, a0, -8 /* arg0: self_addr */ 134 132 PTR_ADDI a1, sp, PT_R1 /* arg1: parent */ ··· 138 134 SYM_CODE_END(ftrace_graph_caller) 139 135 140 136 SYM_CODE_START(return_to_handler) 137 + UNWIND_HINT_UNDEFINED 141 138 /* Save return value regs */ 142 139 PTR_ADDI sp, sp, -FGRET_REGS_SIZE 143 140 PTR_S a0, sp, FGRET_REGS_A0 ··· 160 155 161 156 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS 162 157 SYM_CODE_START(ftrace_stub_direct_tramp) 158 + UNWIND_HINT_UNDEFINED 163 159 jr t0 164 160 SYM_CODE_END(ftrace_stub_direct_tramp) 165 161 #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
+18 -4
arch/loongarch/kernel/module.c
··· 20 20 #include <linux/kernel.h> 21 21 #include <asm/alternative.h> 22 22 #include <asm/inst.h> 23 + #include <asm/unwind.h> 23 24 24 25 static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top) 25 26 { ··· 516 515 int module_finalize(const Elf_Ehdr *hdr, 517 516 const Elf_Shdr *sechdrs, struct module *mod) 518 517 { 519 - const Elf_Shdr *s, *se; 520 518 const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 519 + const Elf_Shdr *s, *alt = NULL, *orc = NULL, *orc_ip = NULL, *ftrace = NULL; 521 520 522 - for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) { 521 + for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { 523 522 if (!strcmp(".altinstructions", secstrs + s->sh_name)) 524 - apply_alternatives((void *)s->sh_addr, (void *)s->sh_addr + s->sh_size); 523 + alt = s; 524 + if (!strcmp(".orc_unwind", secstrs + s->sh_name)) 525 + orc = s; 526 + if (!strcmp(".orc_unwind_ip", secstrs + s->sh_name)) 527 + orc_ip = s; 525 528 if (!strcmp(".ftrace_trampoline", secstrs + s->sh_name)) 526 - module_init_ftrace_plt(hdr, s, mod); 529 + ftrace = s; 527 530 } 531 + 532 + if (alt) 533 + apply_alternatives((void *)alt->sh_addr, (void *)alt->sh_addr + alt->sh_size); 534 + 535 + if (orc && orc_ip) 536 + unwind_module_init(mod, (void *)orc_ip->sh_addr, orc_ip->sh_size, (void *)orc->sh_addr, orc->sh_size); 537 + 538 + if (ftrace) 539 + module_init_ftrace_plt(hdr, ftrace, mod); 528 540 529 541 return 0; 530 542 }
+4 -3
arch/loongarch/kernel/relocate_kernel.S
··· 15 15 #include <asm/addrspace.h> 16 16 17 17 SYM_CODE_START(relocate_new_kernel) 18 + UNWIND_HINT_UNDEFINED 18 19 /* 19 20 * a0: EFI boot flag for the new kernel 20 21 * a1: Command line pointer for the new kernel ··· 91 90 * then start at the entry point from LOONGARCH_IOCSR_MBUF0. 92 91 */ 93 92 SYM_CODE_START(kexec_smp_wait) 93 + UNWIND_HINT_UNDEFINED 94 94 1: li.w t0, 0x100 /* wait for init loop */ 95 95 2: addi.w t0, t0, -1 /* limit mailbox access */ 96 96 bnez t0, 2b ··· 108 106 109 107 relocate_new_kernel_end: 110 108 111 - SYM_DATA_START(relocate_new_kernel_size) 112 - PTR relocate_new_kernel_end - relocate_new_kernel 113 - SYM_DATA_END(relocate_new_kernel_size) 109 + .section ".data" 110 + SYM_DATA(relocate_new_kernel_size, .long relocate_new_kernel_end - relocate_new_kernel)
+1
arch/loongarch/kernel/rethook_trampoline.S
··· 76 76 .endm 77 77 78 78 SYM_CODE_START(arch_rethook_trampoline) 79 + UNWIND_HINT_UNDEFINED 79 80 addi.d sp, sp, -PT_SIZE 80 81 save_all_base_regs 81 82
+2
arch/loongarch/kernel/setup.c
··· 47 47 #include <asm/sections.h> 48 48 #include <asm/setup.h> 49 49 #include <asm/time.h> 50 + #include <asm/unwind.h> 50 51 51 52 #define SMBIOS_BIOSSIZE_OFFSET 0x09 52 53 #define SMBIOS_BIOSEXTERN_OFFSET 0x13 ··· 588 587 void __init setup_arch(char **cmdline_p) 589 588 { 590 589 cpu_probe(); 590 + unwind_init(); 591 591 592 592 init_environ(); 593 593 efi_init();
+41
arch/loongarch/kernel/stacktrace.c
··· 29 29 regs->csr_era = thread_saved_ra(task); 30 30 } 31 31 regs->regs[1] = 0; 32 + regs->regs[22] = 0; 32 33 } 33 34 34 35 for (unwind_start(&state, task, regs); ··· 38 37 if (!addr || !consume_entry(cookie, addr)) 39 38 break; 40 39 } 40 + } 41 + 42 + int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry, 43 + void *cookie, struct task_struct *task) 44 + { 45 + unsigned long addr; 46 + struct pt_regs dummyregs; 47 + struct pt_regs *regs = &dummyregs; 48 + struct unwind_state state; 49 + 50 + if (task == current) { 51 + regs->regs[3] = (unsigned long)__builtin_frame_address(0); 52 + regs->csr_era = (unsigned long)__builtin_return_address(0); 53 + } else { 54 + regs->regs[3] = thread_saved_fp(task); 55 + regs->csr_era = thread_saved_ra(task); 56 + } 57 + regs->regs[1] = 0; 58 + regs->regs[22] = 0; 59 + 60 + for (unwind_start(&state, task, regs); 61 + !unwind_done(&state) && !unwind_error(&state); unwind_next_frame(&state)) { 62 + addr = unwind_get_return_address(&state); 63 + 64 + /* 65 + * A NULL or invalid return address probably means there's some 66 + * generated code which __kernel_text_address() doesn't know about. 67 + */ 68 + if (!addr) 69 + return -EINVAL; 70 + 71 + if (!consume_entry(cookie, addr)) 72 + return -EINVAL; 73 + } 74 + 75 + /* Check for stack corruption */ 76 + if (unwind_error(&state)) 77 + return -EINVAL; 78 + 79 + return 0; 41 80 } 42 81 43 82 static int
+29 -13
arch/loongarch/kernel/traps.c
··· 53 53 54 54 #include "access-helper.h" 55 55 56 + void *exception_table[EXCCODE_INT_START] = { 57 + [0 ... EXCCODE_INT_START - 1] = handle_reserved, 58 + 59 + [EXCCODE_TLBI] = handle_tlb_load, 60 + [EXCCODE_TLBL] = handle_tlb_load, 61 + [EXCCODE_TLBS] = handle_tlb_store, 62 + [EXCCODE_TLBM] = handle_tlb_modify, 63 + [EXCCODE_TLBNR] = handle_tlb_protect, 64 + [EXCCODE_TLBNX] = handle_tlb_protect, 65 + [EXCCODE_TLBPE] = handle_tlb_protect, 66 + [EXCCODE_ADE] = handle_ade, 67 + [EXCCODE_ALE] = handle_ale, 68 + [EXCCODE_BCE] = handle_bce, 69 + [EXCCODE_SYS] = handle_sys, 70 + [EXCCODE_BP] = handle_bp, 71 + [EXCCODE_INE] = handle_ri, 72 + [EXCCODE_IPE] = handle_ri, 73 + [EXCCODE_FPDIS] = handle_fpu, 74 + [EXCCODE_LSXDIS] = handle_lsx, 75 + [EXCCODE_LASXDIS] = handle_lasx, 76 + [EXCCODE_FPE] = handle_fpe, 77 + [EXCCODE_WATCH] = handle_watch, 78 + [EXCCODE_BTDIS] = handle_lbt, 79 + }; 80 + EXPORT_SYMBOL_GPL(exception_table); 81 + 56 82 static void show_backtrace(struct task_struct *task, const struct pt_regs *regs, 57 83 const char *loglvl, bool user) 58 84 { ··· 1176 1150 for (i = EXCCODE_INT_START; i <= EXCCODE_INT_END; i++) 1177 1151 set_handler(i * VECSIZE, handle_vint, VECSIZE); 1178 1152 1179 - set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE); 1180 - set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE); 1181 - set_handler(EXCCODE_BCE * VECSIZE, handle_bce, VECSIZE); 1182 - set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE); 1183 - set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE); 1184 - set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE); 1185 - set_handler(EXCCODE_IPE * VECSIZE, handle_ri, VECSIZE); 1186 - set_handler(EXCCODE_FPDIS * VECSIZE, handle_fpu, VECSIZE); 1187 - set_handler(EXCCODE_LSXDIS * VECSIZE, handle_lsx, VECSIZE); 1188 - set_handler(EXCCODE_LASXDIS * VECSIZE, handle_lasx, VECSIZE); 1189 - set_handler(EXCCODE_FPE * VECSIZE, handle_fpe, VECSIZE); 1190 - set_handler(EXCCODE_BTDIS * VECSIZE, handle_lbt, VECSIZE); 1191 - set_handler(EXCCODE_WATCH * VECSIZE, handle_watch, VECSIZE); 1153 + /* Set exception vector handler */ 1154 + for (i = EXCCODE_ADE; i <= EXCCODE_BTDIS; i++) 1155 + set_handler(i * VECSIZE, exception_table[i], VECSIZE); 1192 1156 1193 1157 cache_error_setup(); 1194 1158
+528
arch/loongarch/kernel/unwind_orc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + #include <linux/objtool.h> 3 + #include <linux/module.h> 4 + #include <linux/sort.h> 5 + #include <asm/exception.h> 6 + #include <asm/orc_header.h> 7 + #include <asm/orc_lookup.h> 8 + #include <asm/orc_types.h> 9 + #include <asm/ptrace.h> 10 + #include <asm/setup.h> 11 + #include <asm/stacktrace.h> 12 + #include <asm/tlb.h> 13 + #include <asm/unwind.h> 14 + 15 + ORC_HEADER; 16 + 17 + #define orc_warn(fmt, ...) \ 18 + printk_deferred_once(KERN_WARNING "WARNING: " fmt, ##__VA_ARGS__) 19 + 20 + extern int __start_orc_unwind_ip[]; 21 + extern int __stop_orc_unwind_ip[]; 22 + extern struct orc_entry __start_orc_unwind[]; 23 + extern struct orc_entry __stop_orc_unwind[]; 24 + 25 + static bool orc_init __ro_after_init; 26 + static unsigned int lookup_num_blocks __ro_after_init; 27 + 28 + /* Fake frame pointer entry -- used as a fallback for generated code */ 29 + static struct orc_entry orc_fp_entry = { 30 + .sp_reg = ORC_REG_FP, 31 + .sp_offset = 16, 32 + .fp_reg = ORC_REG_PREV_SP, 33 + .fp_offset = -16, 34 + .ra_reg = ORC_REG_PREV_SP, 35 + .ra_offset = -8, 36 + .type = ORC_TYPE_CALL 37 + }; 38 + 39 + /* 40 + * If we crash with IP==0, the last successfully executed instruction 41 + * was probably an indirect function call with a NULL function pointer, 42 + * and we don't have unwind information for NULL. 43 + * This hardcoded ORC entry for IP==0 allows us to unwind from a NULL function 44 + * pointer into its parent and then continue normally from there. 45 + */ 46 + static struct orc_entry orc_null_entry = { 47 + .sp_reg = ORC_REG_SP, 48 + .sp_offset = sizeof(long), 49 + .fp_reg = ORC_REG_UNDEFINED, 50 + .type = ORC_TYPE_CALL 51 + }; 52 + 53 + static inline unsigned long orc_ip(const int *ip) 54 + { 55 + return (unsigned long)ip + *ip; 56 + } 57 + 58 + static struct orc_entry *__orc_find(int *ip_table, struct orc_entry *u_table, 59 + unsigned int num_entries, unsigned long ip) 60 + { 61 + int *first = ip_table; 62 + int *mid = first, *found = first; 63 + int *last = ip_table + num_entries - 1; 64 + 65 + if (!num_entries) 66 + return NULL; 67 + 68 + /* 69 + * Do a binary range search to find the rightmost duplicate of a given 70 + * starting address. Some entries are section terminators which are 71 + * "weak" entries for ensuring there are no gaps. They should be 72 + * ignored when they conflict with a real entry. 73 + */ 74 + while (first <= last) { 75 + mid = first + ((last - first) / 2); 76 + 77 + if (orc_ip(mid) <= ip) { 78 + found = mid; 79 + first = mid + 1; 80 + } else 81 + last = mid - 1; 82 + } 83 + 84 + return u_table + (found - ip_table); 85 + } 86 + 87 + #ifdef CONFIG_MODULES 88 + static struct orc_entry *orc_module_find(unsigned long ip) 89 + { 90 + struct module *mod; 91 + 92 + mod = __module_address(ip); 93 + if (!mod || !mod->arch.orc_unwind || !mod->arch.orc_unwind_ip) 94 + return NULL; 95 + 96 + return __orc_find(mod->arch.orc_unwind_ip, mod->arch.orc_unwind, mod->arch.num_orcs, ip); 97 + } 98 + #else 99 + static struct orc_entry *orc_module_find(unsigned long ip) 100 + { 101 + return NULL; 102 + } 103 + #endif 104 + 105 + #ifdef CONFIG_DYNAMIC_FTRACE 106 + static struct orc_entry *orc_find(unsigned long ip); 107 + 108 + /* 109 + * Ftrace dynamic trampolines do not have orc entries of their own. 110 + * But they are copies of the ftrace entries that are static and 111 + * defined in ftrace_*.S, which do have orc entries. 112 + * 113 + * If the unwinder comes across a ftrace trampoline, then find the 114 + * ftrace function that was used to create it, and use that ftrace 115 + * function's orc entry, as the placement of the return code in 116 + * the stack will be identical. 117 + */ 118 + static struct orc_entry *orc_ftrace_find(unsigned long ip) 119 + { 120 + struct ftrace_ops *ops; 121 + unsigned long tramp_addr, offset; 122 + 123 + ops = ftrace_ops_trampoline(ip); 124 + if (!ops) 125 + return NULL; 126 + 127 + /* Set tramp_addr to the start of the code copied by the trampoline */ 128 + if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) 129 + tramp_addr = (unsigned long)ftrace_regs_caller; 130 + else 131 + tramp_addr = (unsigned long)ftrace_caller; 132 + 133 + /* Now place tramp_addr to the location within the trampoline ip is at */ 134 + offset = ip - ops->trampoline; 135 + tramp_addr += offset; 136 + 137 + /* Prevent unlikely recursion */ 138 + if (ip == tramp_addr) 139 + return NULL; 140 + 141 + return orc_find(tramp_addr); 142 + } 143 + #else 144 + static struct orc_entry *orc_ftrace_find(unsigned long ip) 145 + { 146 + return NULL; 147 + } 148 + #endif 149 + 150 + static struct orc_entry *orc_find(unsigned long ip) 151 + { 152 + static struct orc_entry *orc; 153 + 154 + if (ip == 0) 155 + return &orc_null_entry; 156 + 157 + /* For non-init vmlinux addresses, use the fast lookup table: */ 158 + if (ip >= LOOKUP_START_IP && ip < LOOKUP_STOP_IP) { 159 + unsigned int idx, start, stop; 160 + 161 + idx = (ip - LOOKUP_START_IP) / LOOKUP_BLOCK_SIZE; 162 + 163 + if (unlikely((idx >= lookup_num_blocks-1))) { 164 + orc_warn("WARNING: bad lookup idx: idx=%u num=%u ip=%pB\n", 165 + idx, lookup_num_blocks, (void *)ip); 166 + return NULL; 167 + } 168 + 169 + start = orc_lookup[idx]; 170 + stop = orc_lookup[idx + 1] + 1; 171 + 172 + if (unlikely((__start_orc_unwind + start >= __stop_orc_unwind) || 173 + (__start_orc_unwind + stop > __stop_orc_unwind))) { 174 + orc_warn("WARNING: bad lookup value: idx=%u num=%u start=%u stop=%u ip=%pB\n", 175 + idx, lookup_num_blocks, start, stop, (void *)ip); 176 + return NULL; 177 + } 178 + 179 + return __orc_find(__start_orc_unwind_ip + start, 180 + __start_orc_unwind + start, stop - start, ip); 181 + } 182 + 183 + /* vmlinux .init slow lookup: */ 184 + if (is_kernel_inittext(ip)) 185 + return __orc_find(__start_orc_unwind_ip, __start_orc_unwind, 186 + __stop_orc_unwind_ip - __start_orc_unwind_ip, ip); 187 + 188 + /* Module lookup: */ 189 + orc = orc_module_find(ip); 190 + if (orc) 191 + return orc; 192 + 193 + return orc_ftrace_find(ip); 194 + } 195 + 196 + #ifdef CONFIG_MODULES 197 + 198 + static DEFINE_MUTEX(sort_mutex); 199 + static int *cur_orc_ip_table = __start_orc_unwind_ip; 200 + static struct orc_entry *cur_orc_table = __start_orc_unwind; 201 + 202 + static void orc_sort_swap(void *_a, void *_b, int size) 203 + { 204 + int delta = _b - _a; 205 + int *a = _a, *b = _b, tmp; 206 + struct orc_entry *orc_a, *orc_b; 207 + 208 + /* Swap the .orc_unwind_ip entries: */ 209 + tmp = *a; 210 + *a = *b + delta; 211 + *b = tmp - delta; 212 + 213 + /* Swap the corresponding .orc_unwind entries: */ 214 + orc_a = cur_orc_table + (a - cur_orc_ip_table); 215 + orc_b = cur_orc_table + (b - cur_orc_ip_table); 216 + swap(*orc_a, *orc_b); 217 + } 218 + 219 + static int orc_sort_cmp(const void *_a, const void *_b) 220 + { 221 + const int *a = _a, *b = _b; 222 + unsigned long a_val = orc_ip(a); 223 + unsigned long b_val = orc_ip(b); 224 + struct orc_entry *orc_a; 225 + 226 + if (a_val > b_val) 227 + return 1; 228 + if (a_val < b_val) 229 + return -1; 230 + 231 + /* 232 + * The "weak" section terminator entries need to always be first 233 + * to ensure the lookup code skips them in favor of real entries. 234 + * These terminator entries exist to handle any gaps created by 235 + * whitelisted .o files which didn't get objtool generation. 236 + */ 237 + orc_a = cur_orc_table + (a - cur_orc_ip_table); 238 + 239 + return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1; 240 + } 241 + 242 + void unwind_module_init(struct module *mod, void *_orc_ip, size_t orc_ip_size, 243 + void *_orc, size_t orc_size) 244 + { 245 + int *orc_ip = _orc_ip; 246 + struct orc_entry *orc = _orc; 247 + unsigned int num_entries = orc_ip_size / sizeof(int); 248 + 249 + WARN_ON_ONCE(orc_ip_size % sizeof(int) != 0 || 250 + orc_size % sizeof(*orc) != 0 || 251 + num_entries != orc_size / sizeof(*orc)); 252 + 253 + /* 254 + * The 'cur_orc_*' globals allow the orc_sort_swap() callback to 255 + * associate an .orc_unwind_ip table entry with its corresponding 256 + * .orc_unwind entry so they can both be swapped. 257 + */ 258 + mutex_lock(&sort_mutex); 259 + cur_orc_ip_table = orc_ip; 260 + cur_orc_table = orc; 261 + sort(orc_ip, num_entries, sizeof(int), orc_sort_cmp, orc_sort_swap); 262 + mutex_unlock(&sort_mutex); 263 + 264 + mod->arch.orc_unwind_ip = orc_ip; 265 + mod->arch.orc_unwind = orc; 266 + mod->arch.num_orcs = num_entries; 267 + } 268 + #endif 269 + 270 + void __init unwind_init(void) 271 + { 272 + int i; 273 + size_t orc_size = (void *)__stop_orc_unwind - (void *)__start_orc_unwind; 274 + size_t orc_ip_size = (void *)__stop_orc_unwind_ip - (void *)__start_orc_unwind_ip; 275 + size_t num_entries = orc_ip_size / sizeof(int); 276 + struct orc_entry *orc; 277 + 278 + if (!num_entries || orc_ip_size % sizeof(int) != 0 || 279 + orc_size % sizeof(struct orc_entry) != 0 || 280 + num_entries != orc_size / sizeof(struct orc_entry)) { 281 + orc_warn("WARNING: Bad or missing .orc_unwind table. Disabling unwinder.\n"); 282 + return; 283 + } 284 + 285 + /* 286 + * Note, the orc_unwind and orc_unwind_ip tables were already 287 + * sorted at build time via the 'sorttable' tool. 288 + * It's ready for binary search straight away, no need to sort it. 289 + */ 290 + 291 + /* Initialize the fast lookup table: */ 292 + lookup_num_blocks = orc_lookup_end - orc_lookup; 293 + for (i = 0; i < lookup_num_blocks-1; i++) { 294 + orc = __orc_find(__start_orc_unwind_ip, __start_orc_unwind, 295 + num_entries, LOOKUP_START_IP + (LOOKUP_BLOCK_SIZE * i)); 296 + if (!orc) { 297 + orc_warn("WARNING: Corrupt .orc_unwind table. Disabling unwinder.\n"); 298 + return; 299 + } 300 + 301 + orc_lookup[i] = orc - __start_orc_unwind; 302 + } 303 + 304 + /* Initialize the ending block: */ 305 + orc = __orc_find(__start_orc_unwind_ip, __start_orc_unwind, num_entries, LOOKUP_STOP_IP); 306 + if (!orc) { 307 + orc_warn("WARNING: Corrupt .orc_unwind table. Disabling unwinder.\n"); 308 + return; 309 + } 310 + orc_lookup[lookup_num_blocks-1] = orc - __start_orc_unwind; 311 + 312 + orc_init = true; 313 + } 314 + 315 + static inline bool on_stack(struct stack_info *info, unsigned long addr, size_t len) 316 + { 317 + unsigned long begin = info->begin; 318 + unsigned long end = info->end; 319 + 320 + return (info->type != STACK_TYPE_UNKNOWN && 321 + addr >= begin && addr < end && addr + len > begin && addr + len <= end); 322 + } 323 + 324 + static bool stack_access_ok(struct unwind_state *state, unsigned long addr, size_t len) 325 + { 326 + struct stack_info *info = &state->stack_info; 327 + 328 + if (on_stack(info, addr, len)) 329 + return true; 330 + 331 + return !get_stack_info(addr, state->task, info) && on_stack(info, addr, len); 332 + } 333 + 334 + unsigned long unwind_get_return_address(struct unwind_state *state) 335 + { 336 + return __unwind_get_return_address(state); 337 + } 338 + EXPORT_SYMBOL_GPL(unwind_get_return_address); 339 + 340 + void unwind_start(struct unwind_state *state, struct task_struct *task, 341 + struct pt_regs *regs) 342 + { 343 + __unwind_start(state, task, regs); 344 + state->type = UNWINDER_ORC; 345 + if (!unwind_done(state) && !__kernel_text_address(state->pc)) 346 + unwind_next_frame(state); 347 + } 348 + EXPORT_SYMBOL_GPL(unwind_start); 349 + 350 + static bool is_entry_func(unsigned long addr) 351 + { 352 + extern u32 kernel_entry; 353 + extern u32 kernel_entry_end; 354 + 355 + return addr >= (unsigned long)&kernel_entry && addr < (unsigned long)&kernel_entry_end; 356 + } 357 + 358 + static inline unsigned long bt_address(unsigned long ra) 359 + { 360 + extern unsigned long eentry; 361 + 362 + if (__kernel_text_address(ra)) 363 + return ra; 364 + 365 + if (__module_text_address(ra)) 366 + return ra; 367 + 368 + if (ra >= eentry && ra < eentry + EXCCODE_INT_END * VECSIZE) { 369 + unsigned long func; 370 + unsigned long type = (ra - eentry) / VECSIZE; 371 + unsigned long offset = (ra - eentry) % VECSIZE; 372 + 373 + switch (type) { 374 + case 0 ... EXCCODE_INT_START - 1: 375 + func = (unsigned long)exception_table[type]; 376 + break; 377 + case EXCCODE_INT_START ... EXCCODE_INT_END: 378 + func = (unsigned long)handle_vint; 379 + break; 380 + default: 381 + func = (unsigned long)handle_reserved; 382 + break; 383 + } 384 + 385 + return func + offset; 386 + } 387 + 388 + return ra; 389 + } 390 + 391 + bool unwind_next_frame(struct unwind_state *state) 392 + { 393 + unsigned long *p, pc; 394 + struct pt_regs *regs; 395 + struct orc_entry *orc; 396 + struct stack_info *info = &state->stack_info; 397 + 398 + if (unwind_done(state)) 399 + return false; 400 + 401 + /* Don't let modules unload while we're reading their ORC data. */ 402 + preempt_disable(); 403 + 404 + if (is_entry_func(state->pc)) 405 + goto end; 406 + 407 + orc = orc_find(state->pc); 408 + if (!orc) { 409 + /* 410 + * As a fallback, try to assume this code uses a frame pointer. 411 + * This is useful for generated code, like BPF, which ORC 412 + * doesn't know about. This is just a guess, so the rest of 413 + * the unwind is no longer considered reliable. 414 + */ 415 + orc = &orc_fp_entry; 416 + state->error = true; 417 + } else { 418 + if (orc->type == ORC_TYPE_UNDEFINED) 419 + goto err; 420 + 421 + if (orc->type == ORC_TYPE_END_OF_STACK) 422 + goto end; 423 + } 424 + 425 + switch (orc->sp_reg) { 426 + case ORC_REG_SP: 427 + if (info->type == STACK_TYPE_IRQ && state->sp == info->end) 428 + orc->type = ORC_TYPE_REGS; 429 + else 430 + state->sp = state->sp + orc->sp_offset; 431 + break; 432 + case ORC_REG_FP: 433 + state->sp = state->fp; 434 + break; 435 + default: 436 + orc_warn("unknown SP base reg %d at %pB\n", orc->sp_reg, (void *)state->pc); 437 + goto err; 438 + } 439 + 440 + switch (orc->fp_reg) { 441 + case ORC_REG_PREV_SP: 442 + p = (unsigned long *)(state->sp + orc->fp_offset); 443 + if (!stack_access_ok(state, (unsigned long)p, sizeof(unsigned long))) 444 + goto err; 445 + 446 + state->fp = *p; 447 + break; 448 + case ORC_REG_UNDEFINED: 449 + /* Nothing. */ 450 + break; 451 + default: 452 + orc_warn("unknown FP base reg %d at %pB\n", orc->fp_reg, (void *)state->pc); 453 + goto err; 454 + } 455 + 456 + switch (orc->type) { 457 + case ORC_TYPE_CALL: 458 + if (orc->ra_reg == ORC_REG_PREV_SP) { 459 + p = (unsigned long *)(state->sp + orc->ra_offset); 460 + if (!stack_access_ok(state, (unsigned long)p, sizeof(unsigned long))) 461 + goto err; 462 + 463 + pc = unwind_graph_addr(state, *p, state->sp); 464 + pc -= LOONGARCH_INSN_SIZE; 465 + } else if (orc->ra_reg == ORC_REG_UNDEFINED) { 466 + if (!state->ra || state->ra == state->pc) 467 + goto err; 468 + 469 + pc = unwind_graph_addr(state, state->ra, state->sp); 470 + pc -= LOONGARCH_INSN_SIZE; 471 + state->ra = 0; 472 + } else { 473 + orc_warn("unknown ra base reg %d at %pB\n", orc->ra_reg, (void *)state->pc); 474 + goto err; 475 + } 476 + break; 477 + case ORC_TYPE_REGS: 478 + if (info->type == STACK_TYPE_IRQ && state->sp == info->end) 479 + regs = (struct pt_regs *)info->next_sp; 480 + else 481 + regs = (struct pt_regs *)state->sp; 482 + 483 + if (!stack_access_ok(state, (unsigned long)regs, sizeof(*regs))) 484 + goto err; 485 + 486 + if ((info->end == (unsigned long)regs + sizeof(*regs)) && 487 + !regs->regs[3] && !regs->regs[1]) 488 + goto end; 489 + 490 + if (user_mode(regs)) 491 + goto end; 492 + 493 + pc = regs->csr_era; 494 + if (!__kernel_text_address(pc)) 495 + goto err; 496 + 497 + state->sp = regs->regs[3]; 498 + state->ra = regs->regs[1]; 499 + state->fp = regs->regs[22]; 500 + get_stack_info(state->sp, state->task, info); 501 + 502 + break; 503 + default: 504 + orc_warn("unknown .orc_unwind entry type %d at %pB\n", orc->type, (void *)state->pc); 505 + goto err; 506 + } 507 + 508 + state->pc = bt_address(pc); 509 + if (!state->pc) { 510 + pr_err("cannot find unwind pc at %pK\n", (void *)pc); 511 + goto err; 512 + } 513 + 514 + if (!__kernel_text_address(state->pc)) 515 + goto err; 516 + 517 + preempt_enable(); 518 + return true; 519 + 520 + err: 521 + state->error = true; 522 + 523 + end: 524 + preempt_enable(); 525 + state->stack_info.type = STACK_TYPE_UNKNOWN; 526 + return false; 527 + } 528 + EXPORT_SYMBOL_GPL(unwind_next_frame);
+3
arch/loongarch/kernel/vmlinux.lds.S
··· 2 2 #include <linux/sizes.h> 3 3 #include <asm/asm-offsets.h> 4 4 #include <asm/thread_info.h> 5 + #include <asm/orc_lookup.h> 5 6 6 7 #define PAGE_SIZE _PAGE_SIZE 7 8 #define RO_EXCEPTION_TABLE_ALIGN 4 ··· 122 121 __la_abs_end = .; 123 122 } 124 123 #endif 124 + 125 + ORC_UNWIND_TABLE 125 126 126 127 .sdata : { 127 128 *(.sdata)
+8 -1
arch/loongarch/kvm/switch.S
··· 8 8 #include <asm/asmmacro.h> 9 9 #include <asm/loongarch.h> 10 10 #include <asm/regdef.h> 11 - #include <asm/stackframe.h> 11 + #include <asm/unwind_hints.h> 12 12 13 13 #define HGPR_OFFSET(x) (PT_R0 + 8*x) 14 14 #define GGPR_OFFSET(x) (KVM_ARCH_GGPR + 8*x) ··· 112 112 .text 113 113 .cfi_sections .debug_frame 114 114 SYM_CODE_START(kvm_exc_entry) 115 + UNWIND_HINT_UNDEFINED 115 116 csrwr a2, KVM_TEMP_KS 116 117 csrrd a2, KVM_VCPU_KS 117 118 addi.d a2, a2, KVM_VCPU_ARCH ··· 274 273 .section ".rodata" 275 274 SYM_DATA(kvm_exception_size, .quad kvm_exc_entry_end - kvm_exc_entry) 276 275 SYM_DATA(kvm_enter_guest_size, .quad kvm_enter_guest_end - kvm_enter_guest) 276 + 277 + #ifdef CONFIG_CPU_HAS_LBT 278 + STACK_FRAME_NON_STANDARD kvm_restore_fpu 279 + STACK_FRAME_NON_STANDARD kvm_restore_lsx 280 + STACK_FRAME_NON_STANDARD kvm_restore_lasx 281 + #endif
+3
arch/loongarch/lib/clear_user.S
··· 10 10 #include <asm/asm-extable.h> 11 11 #include <asm/cpu.h> 12 12 #include <asm/regdef.h> 13 + #include <asm/unwind_hints.h> 13 14 14 15 SYM_FUNC_START(__clear_user) 15 16 /* ··· 205 204 _asm_extable 28b, .Lsmall_fixup 206 205 _asm_extable 29b, .Lexit 207 206 SYM_FUNC_END(__clear_user_fast) 207 + 208 + STACK_FRAME_NON_STANDARD __clear_user_fast
+3
arch/loongarch/lib/copy_user.S
··· 10 10 #include <asm/asm-extable.h> 11 11 #include <asm/cpu.h> 12 12 #include <asm/regdef.h> 13 + #include <asm/unwind_hints.h> 13 14 14 15 SYM_FUNC_START(__copy_user) 15 16 /* ··· 279 278 _asm_extable 58b, .Lexit 280 279 _asm_extable 59b, .Lexit 281 280 SYM_FUNC_END(__copy_user_fast) 281 + 282 + STACK_FRAME_NON_STANDARD __copy_user_fast
+3
arch/loongarch/lib/memcpy.S
··· 9 9 #include <asm/asmmacro.h> 10 10 #include <asm/cpu.h> 11 11 #include <asm/regdef.h> 12 + #include <asm/unwind_hints.h> 12 13 13 14 .section .noinstr.text, "ax" 14 15 ··· 198 197 jr ra 199 198 SYM_FUNC_END(__memcpy_fast) 200 199 _ASM_NOKPROBE(__memcpy_fast) 200 + 201 + STACK_FRAME_NON_STANDARD __memcpy_small
+3
arch/loongarch/lib/memset.S
··· 9 9 #include <asm/asmmacro.h> 10 10 #include <asm/cpu.h> 11 11 #include <asm/regdef.h> 12 + #include <asm/unwind_hints.h> 12 13 13 14 .macro fill_to_64 r0 14 15 bstrins.d \r0, \r0, 15, 8 ··· 167 166 jr ra 168 167 SYM_FUNC_END(__memset_fast) 169 168 _ASM_NOKPROBE(__memset_fast) 169 + 170 + STACK_FRAME_NON_STANDARD __memset_fast
+12 -15
arch/loongarch/mm/tlb.c
··· 9 9 #include <linux/hugetlb.h> 10 10 #include <linux/export.h> 11 11 12 - #include <asm/cpu.h> 13 12 #include <asm/bootinfo.h> 13 + #include <asm/cpu.h> 14 + #include <asm/exception.h> 14 15 #include <asm/mmu_context.h> 15 16 #include <asm/pgtable.h> 16 17 #include <asm/tlb.h> ··· 267 266 setup_ptwalker(); 268 267 local_flush_tlb_all(); 269 268 269 + if (cpu_has_ptw) { 270 + exception_table[EXCCODE_TLBI] = handle_tlb_load_ptw; 271 + exception_table[EXCCODE_TLBL] = handle_tlb_load_ptw; 272 + exception_table[EXCCODE_TLBS] = handle_tlb_store_ptw; 273 + exception_table[EXCCODE_TLBM] = handle_tlb_modify_ptw; 274 + } 275 + 270 276 /* The tlb handlers are generated only once */ 271 277 if (cpu == 0) { 272 278 memcpy((void *)tlbrentry, handle_tlb_refill, 0x80); 273 279 local_flush_icache_range(tlbrentry, tlbrentry + 0x80); 274 - if (!cpu_has_ptw) { 275 - set_handler(EXCCODE_TLBI * VECSIZE, handle_tlb_load, VECSIZE); 276 - set_handler(EXCCODE_TLBL * VECSIZE, handle_tlb_load, VECSIZE); 277 - set_handler(EXCCODE_TLBS * VECSIZE, handle_tlb_store, VECSIZE); 278 - set_handler(EXCCODE_TLBM * VECSIZE, handle_tlb_modify, VECSIZE); 279 - } else { 280 - set_handler(EXCCODE_TLBI * VECSIZE, handle_tlb_load_ptw, VECSIZE); 281 - set_handler(EXCCODE_TLBL * VECSIZE, handle_tlb_load_ptw, VECSIZE); 282 - set_handler(EXCCODE_TLBS * VECSIZE, handle_tlb_store_ptw, VECSIZE); 283 - set_handler(EXCCODE_TLBM * VECSIZE, handle_tlb_modify_ptw, VECSIZE); 284 - } 285 - set_handler(EXCCODE_TLBNR * VECSIZE, handle_tlb_protect, VECSIZE); 286 - set_handler(EXCCODE_TLBNX * VECSIZE, handle_tlb_protect, VECSIZE); 287 - set_handler(EXCCODE_TLBPE * VECSIZE, handle_tlb_protect, VECSIZE); 280 + 281 + for (int i = EXCCODE_TLBL; i <= EXCCODE_TLBPE; i++) 282 + set_handler(i * VECSIZE, exception_table[i], VECSIZE); 288 283 } else { 289 284 int vec_sz __maybe_unused; 290 285 void *addr __maybe_unused;
+9
arch/loongarch/mm/tlbex.S
··· 18 18 19 19 .macro tlb_do_page_fault, write 20 20 SYM_CODE_START(tlb_do_page_fault_\write) 21 + UNWIND_HINT_UNDEFINED 21 22 SAVE_ALL 22 23 csrrd a2, LOONGARCH_CSR_BADV 23 24 move a0, sp ··· 33 32 tlb_do_page_fault 1 34 33 35 34 SYM_CODE_START(handle_tlb_protect) 35 + UNWIND_HINT_UNDEFINED 36 36 BACKUP_T0T1 37 37 SAVE_ALL 38 38 move a0, sp ··· 46 44 SYM_CODE_END(handle_tlb_protect) 47 45 48 46 SYM_CODE_START(handle_tlb_load) 47 + UNWIND_HINT_UNDEFINED 49 48 csrwr t0, EXCEPTION_KS0 50 49 csrwr t1, EXCEPTION_KS1 51 50 csrwr ra, EXCEPTION_KS2 ··· 193 190 SYM_CODE_END(handle_tlb_load) 194 191 195 192 SYM_CODE_START(handle_tlb_load_ptw) 193 + UNWIND_HINT_UNDEFINED 196 194 csrwr t0, LOONGARCH_CSR_KS0 197 195 csrwr t1, LOONGARCH_CSR_KS1 198 196 la_abs t0, tlb_do_page_fault_0 ··· 201 197 SYM_CODE_END(handle_tlb_load_ptw) 202 198 203 199 SYM_CODE_START(handle_tlb_store) 200 + UNWIND_HINT_UNDEFINED 204 201 csrwr t0, EXCEPTION_KS0 205 202 csrwr t1, EXCEPTION_KS1 206 203 csrwr ra, EXCEPTION_KS2 ··· 351 346 SYM_CODE_END(handle_tlb_store) 352 347 353 348 SYM_CODE_START(handle_tlb_store_ptw) 349 + UNWIND_HINT_UNDEFINED 354 350 csrwr t0, LOONGARCH_CSR_KS0 355 351 csrwr t1, LOONGARCH_CSR_KS1 356 352 la_abs t0, tlb_do_page_fault_1 ··· 359 353 SYM_CODE_END(handle_tlb_store_ptw) 360 354 361 355 SYM_CODE_START(handle_tlb_modify) 356 + UNWIND_HINT_UNDEFINED 362 357 csrwr t0, EXCEPTION_KS0 363 358 csrwr t1, EXCEPTION_KS1 364 359 csrwr ra, EXCEPTION_KS2 ··· 507 500 SYM_CODE_END(handle_tlb_modify) 508 501 509 502 SYM_CODE_START(handle_tlb_modify_ptw) 503 + UNWIND_HINT_UNDEFINED 510 504 csrwr t0, LOONGARCH_CSR_KS0 511 505 csrwr t1, LOONGARCH_CSR_KS1 512 506 la_abs t0, tlb_do_page_fault_1 ··· 515 507 SYM_CODE_END(handle_tlb_modify_ptw) 516 508 517 509 SYM_CODE_START(handle_tlb_refill) 510 + UNWIND_HINT_UNDEFINED 518 511 csrwr t0, LOONGARCH_CSR_TLBRSAVE 519 512 csrrd t0, LOONGARCH_CSR_PGD 520 513 lddir t0, t0, 3
+1
arch/loongarch/vdso/Makefile
··· 4 4 KASAN_SANITIZE := n 5 5 UBSAN_SANITIZE := n 6 6 KCOV_INSTRUMENT := n 7 + OBJECT_FILES_NON_STANDARD := y 7 8 8 9 # Include the generic Makefile to check the built vdso. 9 10 include $(srctree)/lib/vdso/Makefile
+9
include/linux/compiler.h
··· 116 116 */ 117 117 #define __stringify_label(n) #n 118 118 119 + #define __annotate_reachable(c) ({ \ 120 + asm volatile(__stringify_label(c) ":\n\t" \ 121 + ".pushsection .discard.reachable\n\t" \ 122 + ".long " __stringify_label(c) "b - .\n\t" \ 123 + ".popsection\n\t"); \ 124 + }) 125 + #define annotate_reachable() __annotate_reachable(__COUNTER__) 126 + 119 127 #define __annotate_unreachable(c) ({ \ 120 128 asm volatile(__stringify_label(c) ":\n\t" \ 121 129 ".pushsection .discard.unreachable\n\t" \ ··· 136 128 #define __annotate_jump_table __section(".rodata..c_jump_table") 137 129 138 130 #else /* !CONFIG_OBJTOOL */ 131 + #define annotate_reachable() 139 132 #define annotate_unreachable() 140 133 #define __annotate_jump_table 141 134 #endif /* CONFIG_OBJTOOL */
+5 -2
scripts/Makefile
··· 33 33 34 34 ifdef CONFIG_UNWINDER_ORC 35 35 ifeq ($(ARCH),x86_64) 36 - ARCH := x86 36 + SRCARCH := x86 37 37 endif 38 - HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/x86/include 38 + ifeq ($(ARCH),loongarch) 39 + SRCARCH := loongarch 40 + endif 41 + HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/$(SRCARCH)/include 39 42 HOSTCFLAGS_sorttable.o += -DUNWINDER_ORC_ENABLED 40 43 endif 41 44
+161
tools/arch/loongarch/include/asm/inst.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 4 + */ 5 + #ifndef _ASM_INST_H 6 + #define _ASM_INST_H 7 + 8 + #include <linux/bitops.h> 9 + 10 + #define LOONGARCH_INSN_NOP 0x03400000 11 + 12 + enum reg0i15_op { 13 + break_op = 0x54, 14 + }; 15 + 16 + enum reg0i26_op { 17 + b_op = 0x14, 18 + bl_op = 0x15, 19 + }; 20 + 21 + enum reg1i21_op { 22 + beqz_op = 0x10, 23 + bnez_op = 0x11, 24 + bceqz_op = 0x12, /* bits[9:8] = 0x00 */ 25 + bcnez_op = 0x12, /* bits[9:8] = 0x01 */ 26 + }; 27 + 28 + enum reg2_op { 29 + ertn_op = 0x1920e, 30 + }; 31 + 32 + enum reg2i12_op { 33 + addid_op = 0x0b, 34 + andi_op = 0x0d, 35 + ldd_op = 0xa3, 36 + std_op = 0xa7, 37 + }; 38 + 39 + enum reg2i14_op { 40 + ldptrd_op = 0x26, 41 + stptrd_op = 0x27, 42 + }; 43 + 44 + enum reg2i16_op { 45 + jirl_op = 0x13, 46 + beq_op = 0x16, 47 + bne_op = 0x17, 48 + blt_op = 0x18, 49 + bge_op = 0x19, 50 + bltu_op = 0x1a, 51 + bgeu_op = 0x1b, 52 + }; 53 + 54 + struct reg0i15_format { 55 + unsigned int immediate : 15; 56 + unsigned int opcode : 17; 57 + }; 58 + 59 + struct reg0i26_format { 60 + unsigned int immediate_h : 10; 61 + unsigned int immediate_l : 16; 62 + unsigned int opcode : 6; 63 + }; 64 + 65 + struct reg1i21_format { 66 + unsigned int immediate_h : 5; 67 + unsigned int rj : 5; 68 + unsigned int immediate_l : 16; 69 + unsigned int opcode : 6; 70 + }; 71 + 72 + struct reg2_format { 73 + unsigned int rd : 5; 74 + unsigned int rj : 5; 75 + unsigned int opcode : 22; 76 + }; 77 + 78 + struct reg2i12_format { 79 + unsigned int rd : 5; 80 + unsigned int rj : 5; 81 + unsigned int immediate : 12; 82 + unsigned int opcode : 10; 83 + }; 84 + 85 + struct reg2i14_format { 86 + unsigned int rd : 5; 87 + unsigned int rj : 5; 88 + unsigned int immediate : 14; 89 + unsigned int opcode : 8; 90 + }; 91 + 92 + struct reg2i16_format { 93 + unsigned int rd : 5; 94 + unsigned int rj : 5; 95 + unsigned int immediate : 16; 96 + unsigned int opcode : 6; 97 + }; 98 + 99 + union loongarch_instruction { 100 + unsigned int word; 101 + struct reg0i15_format reg0i15_format; 102 + struct reg0i26_format reg0i26_format; 103 + struct reg1i21_format reg1i21_format; 104 + struct reg2_format reg2_format; 105 + struct reg2i12_format reg2i12_format; 106 + struct reg2i14_format reg2i14_format; 107 + struct reg2i16_format reg2i16_format; 108 + }; 109 + 110 + #define LOONGARCH_INSN_SIZE sizeof(union loongarch_instruction) 111 + 112 + enum loongarch_gpr { 113 + LOONGARCH_GPR_ZERO = 0, 114 + LOONGARCH_GPR_RA = 1, 115 + LOONGARCH_GPR_TP = 2, 116 + LOONGARCH_GPR_SP = 3, 117 + LOONGARCH_GPR_A0 = 4, /* Reused as V0 for return value */ 118 + LOONGARCH_GPR_A1, /* Reused as V1 for return value */ 119 + LOONGARCH_GPR_A2, 120 + LOONGARCH_GPR_A3, 121 + LOONGARCH_GPR_A4, 122 + LOONGARCH_GPR_A5, 123 + LOONGARCH_GPR_A6, 124 + LOONGARCH_GPR_A7, 125 + LOONGARCH_GPR_T0 = 12, 126 + LOONGARCH_GPR_T1, 127 + LOONGARCH_GPR_T2, 128 + LOONGARCH_GPR_T3, 129 + LOONGARCH_GPR_T4, 130 + LOONGARCH_GPR_T5, 131 + LOONGARCH_GPR_T6, 132 + LOONGARCH_GPR_T7, 133 + LOONGARCH_GPR_T8, 134 + LOONGARCH_GPR_FP = 22, 135 + LOONGARCH_GPR_S0 = 23, 136 + LOONGARCH_GPR_S1, 137 + LOONGARCH_GPR_S2, 138 + LOONGARCH_GPR_S3, 139 + LOONGARCH_GPR_S4, 140 + LOONGARCH_GPR_S5, 141 + LOONGARCH_GPR_S6, 142 + LOONGARCH_GPR_S7, 143 + LOONGARCH_GPR_S8, 144 + LOONGARCH_GPR_MAX 145 + }; 146 + 147 + #define DEF_EMIT_REG2I16_FORMAT(NAME, OP) \ 148 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 149 + enum loongarch_gpr rj, \ 150 + enum loongarch_gpr rd, \ 151 + int offset) \ 152 + { \ 153 + insn->reg2i16_format.opcode = OP; \ 154 + insn->reg2i16_format.immediate = offset; \ 155 + insn->reg2i16_format.rj = rj; \ 156 + insn->reg2i16_format.rd = rd; \ 157 + } 158 + 159 + DEF_EMIT_REG2I16_FORMAT(jirl, jirl_op) 160 + 161 + #endif /* _ASM_INST_H */
+58
tools/arch/loongarch/include/asm/orc_types.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _ORC_TYPES_H 3 + #define _ORC_TYPES_H 4 + 5 + #include <linux/types.h> 6 + 7 + /* 8 + * The ORC_REG_* registers are base registers which are used to find other 9 + * registers on the stack. 10 + * 11 + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the 12 + * address of the previous frame: the caller's SP before it called the current 13 + * function. 14 + * 15 + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in 16 + * the current frame. 17 + * 18 + * The most commonly used base registers are SP and FP -- which the previous SP 19 + * is usually based on -- and PREV_SP and UNDEFINED -- which the previous FP is 20 + * usually based on. 21 + * 22 + * The rest of the base registers are needed for special cases like entry code 23 + * and GCC realigned stacks. 24 + */ 25 + #define ORC_REG_UNDEFINED 0 26 + #define ORC_REG_PREV_SP 1 27 + #define ORC_REG_SP 2 28 + #define ORC_REG_FP 3 29 + #define ORC_REG_MAX 4 30 + 31 + #define ORC_TYPE_UNDEFINED 0 32 + #define ORC_TYPE_END_OF_STACK 1 33 + #define ORC_TYPE_CALL 2 34 + #define ORC_TYPE_REGS 3 35 + #define ORC_TYPE_REGS_PARTIAL 4 36 + 37 + #ifndef __ASSEMBLY__ 38 + /* 39 + * This struct is more or less a vastly simplified version of the DWARF Call 40 + * Frame Information standard. It contains only the necessary parts of DWARF 41 + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the 42 + * unwinder how to find the previous SP and FP (and sometimes entry regs) on 43 + * the stack for a given code address. Each instance of the struct corresponds 44 + * to one or more code locations. 45 + */ 46 + struct orc_entry { 47 + s16 sp_offset; 48 + s16 fp_offset; 49 + s16 ra_offset; 50 + unsigned int sp_reg:4; 51 + unsigned int fp_reg:4; 52 + unsigned int ra_reg:4; 53 + unsigned int type:3; 54 + unsigned int signal:1; 55 + }; 56 + #endif /* __ASSEMBLY__ */ 57 + 58 + #endif /* _ORC_TYPES_H */
+11
tools/include/linux/bitops.h
··· 87 87 return (word << shift) | (word >> ((-shift) & 31)); 88 88 } 89 89 90 + /** 91 + * sign_extend64 - sign extend a 64-bit value using specified bit as sign-bit 92 + * @value: value to sign extend 93 + * @index: 0 based bit index (0<=index<64) to sign bit 94 + */ 95 + static __always_inline __s64 sign_extend64(__u64 value, int index) 96 + { 97 + __u8 shift = 63 - index; 98 + return (__s64)(value << shift) >> shift; 99 + } 100 + 90 101 #endif
+4
tools/objtool/Makefile
··· 57 57 BUILD_ORC := y 58 58 endif 59 59 60 + ifeq ($(SRCARCH),loongarch) 61 + BUILD_ORC := y 62 + endif 63 + 60 64 export BUILD_ORC 61 65 export srctree OUTPUT CFLAGS SRCARCH AWK 62 66 include $(srctree)/tools/build/Makefile.include
+3
tools/objtool/arch/loongarch/Build
··· 1 + objtool-y += decode.o 2 + objtool-y += special.o 3 + objtool-y += orc.o
+356
tools/objtool/arch/loongarch/decode.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + #include <string.h> 3 + #include <objtool/check.h> 4 + #include <objtool/warn.h> 5 + #include <asm/inst.h> 6 + #include <asm/orc_types.h> 7 + #include <linux/objtool_types.h> 8 + 9 + #ifndef EM_LOONGARCH 10 + #define EM_LOONGARCH 258 11 + #endif 12 + 13 + int arch_ftrace_match(char *name) 14 + { 15 + return !strcmp(name, "_mcount"); 16 + } 17 + 18 + unsigned long arch_jump_destination(struct instruction *insn) 19 + { 20 + return insn->offset + (insn->immediate << 2); 21 + } 22 + 23 + unsigned long arch_dest_reloc_offset(int addend) 24 + { 25 + return addend; 26 + } 27 + 28 + bool arch_pc_relative_reloc(struct reloc *reloc) 29 + { 30 + return false; 31 + } 32 + 33 + bool arch_callee_saved_reg(unsigned char reg) 34 + { 35 + switch (reg) { 36 + case CFI_RA: 37 + case CFI_FP: 38 + case CFI_S0 ... CFI_S8: 39 + return true; 40 + default: 41 + return false; 42 + } 43 + } 44 + 45 + int arch_decode_hint_reg(u8 sp_reg, int *base) 46 + { 47 + switch (sp_reg) { 48 + case ORC_REG_UNDEFINED: 49 + *base = CFI_UNDEFINED; 50 + break; 51 + case ORC_REG_SP: 52 + *base = CFI_SP; 53 + break; 54 + case ORC_REG_FP: 55 + *base = CFI_FP; 56 + break; 57 + default: 58 + return -1; 59 + } 60 + 61 + return 0; 62 + } 63 + 64 + static bool is_loongarch(const struct elf *elf) 65 + { 66 + if (elf->ehdr.e_machine == EM_LOONGARCH) 67 + return true; 68 + 69 + WARN("unexpected ELF machine type %d", elf->ehdr.e_machine); 70 + return false; 71 + } 72 + 73 + #define ADD_OP(op) \ 74 + if (!(op = calloc(1, sizeof(*op)))) \ 75 + return -1; \ 76 + else for (*ops_list = op, ops_list = &op->next; op; op = NULL) 77 + 78 + static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst, 79 + struct instruction *insn) 80 + { 81 + switch (inst.reg0i26_format.opcode) { 82 + case b_op: 83 + insn->type = INSN_JUMP_UNCONDITIONAL; 84 + insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 | 85 + inst.reg0i26_format.immediate_l, 25); 86 + break; 87 + case bl_op: 88 + insn->type = INSN_CALL; 89 + insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 | 90 + inst.reg0i26_format.immediate_l, 25); 91 + break; 92 + default: 93 + return false; 94 + } 95 + 96 + return true; 97 + } 98 + 99 + static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst, 100 + struct instruction *insn) 101 + { 102 + switch (inst.reg1i21_format.opcode) { 103 + case beqz_op: 104 + case bnez_op: 105 + case bceqz_op: 106 + insn->type = INSN_JUMP_CONDITIONAL; 107 + insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 | 108 + inst.reg1i21_format.immediate_l, 20); 109 + break; 110 + default: 111 + return false; 112 + } 113 + 114 + return true; 115 + } 116 + 117 + static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst, 118 + struct instruction *insn, 119 + struct stack_op **ops_list, 120 + struct stack_op *op) 121 + { 122 + switch (inst.reg2i12_format.opcode) { 123 + case addid_op: 124 + if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) { 125 + /* addi.d sp,sp,si12 or addi.d fp,sp,si12 */ 126 + insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); 127 + ADD_OP(op) { 128 + op->src.type = OP_SRC_ADD; 129 + op->src.reg = inst.reg2i12_format.rj; 130 + op->src.offset = insn->immediate; 131 + op->dest.type = OP_DEST_REG; 132 + op->dest.reg = inst.reg2i12_format.rd; 133 + } 134 + } 135 + break; 136 + case ldd_op: 137 + if (inst.reg2i12_format.rj == CFI_SP) { 138 + /* ld.d rd,sp,si12 */ 139 + insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); 140 + ADD_OP(op) { 141 + op->src.type = OP_SRC_REG_INDIRECT; 142 + op->src.reg = CFI_SP; 143 + op->src.offset = insn->immediate; 144 + op->dest.type = OP_DEST_REG; 145 + op->dest.reg = inst.reg2i12_format.rd; 146 + } 147 + } 148 + break; 149 + case std_op: 150 + if (inst.reg2i12_format.rj == CFI_SP) { 151 + /* st.d rd,sp,si12 */ 152 + insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); 153 + ADD_OP(op) { 154 + op->src.type = OP_SRC_REG; 155 + op->src.reg = inst.reg2i12_format.rd; 156 + op->dest.type = OP_DEST_REG_INDIRECT; 157 + op->dest.reg = CFI_SP; 158 + op->dest.offset = insn->immediate; 159 + } 160 + } 161 + break; 162 + case andi_op: 163 + if (inst.reg2i12_format.rd == 0 && 164 + inst.reg2i12_format.rj == 0 && 165 + inst.reg2i12_format.immediate == 0) 166 + /* andi r0,r0,0 */ 167 + insn->type = INSN_NOP; 168 + break; 169 + default: 170 + return false; 171 + } 172 + 173 + return true; 174 + } 175 + 176 + static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst, 177 + struct instruction *insn, 178 + struct stack_op **ops_list, 179 + struct stack_op *op) 180 + { 181 + switch (inst.reg2i14_format.opcode) { 182 + case ldptrd_op: 183 + if (inst.reg2i14_format.rj == CFI_SP) { 184 + /* ldptr.d rd,sp,si14 */ 185 + insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13); 186 + ADD_OP(op) { 187 + op->src.type = OP_SRC_REG_INDIRECT; 188 + op->src.reg = CFI_SP; 189 + op->src.offset = insn->immediate; 190 + op->dest.type = OP_DEST_REG; 191 + op->dest.reg = inst.reg2i14_format.rd; 192 + } 193 + } 194 + break; 195 + case stptrd_op: 196 + if (inst.reg2i14_format.rj == CFI_SP) { 197 + /* stptr.d ra,sp,0 */ 198 + if (inst.reg2i14_format.rd == LOONGARCH_GPR_RA && 199 + inst.reg2i14_format.immediate == 0) 200 + break; 201 + 202 + /* stptr.d rd,sp,si14 */ 203 + insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13); 204 + ADD_OP(op) { 205 + op->src.type = OP_SRC_REG; 206 + op->src.reg = inst.reg2i14_format.rd; 207 + op->dest.type = OP_DEST_REG_INDIRECT; 208 + op->dest.reg = CFI_SP; 209 + op->dest.offset = insn->immediate; 210 + } 211 + } 212 + break; 213 + default: 214 + return false; 215 + } 216 + 217 + return true; 218 + } 219 + 220 + static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst, 221 + struct instruction *insn) 222 + { 223 + switch (inst.reg2i16_format.opcode) { 224 + case jirl_op: 225 + if (inst.reg2i16_format.rd == 0 && 226 + inst.reg2i16_format.rj == CFI_RA && 227 + inst.reg2i16_format.immediate == 0) { 228 + /* jirl r0,ra,0 */ 229 + insn->type = INSN_RETURN; 230 + } else if (inst.reg2i16_format.rd == CFI_RA) { 231 + /* jirl ra,rj,offs16 */ 232 + insn->type = INSN_CALL_DYNAMIC; 233 + } else if (inst.reg2i16_format.rd == CFI_A0 && 234 + inst.reg2i16_format.immediate == 0) { 235 + /* 236 + * jirl a0,t0,0 237 + * this is a special case in loongarch_suspend_enter, 238 + * just treat it as a call instruction. 239 + */ 240 + insn->type = INSN_CALL_DYNAMIC; 241 + } else if (inst.reg2i16_format.rd == 0 && 242 + inst.reg2i16_format.immediate == 0) { 243 + /* jirl r0,rj,0 */ 244 + insn->type = INSN_JUMP_DYNAMIC; 245 + } else if (inst.reg2i16_format.rd == 0 && 246 + inst.reg2i16_format.immediate != 0) { 247 + /* 248 + * jirl r0,t0,12 249 + * this is a rare case in JUMP_VIRT_ADDR, 250 + * just ignore it due to it is harmless for tracing. 251 + */ 252 + break; 253 + } else { 254 + /* jirl rd,rj,offs16 */ 255 + insn->type = INSN_JUMP_UNCONDITIONAL; 256 + insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15); 257 + } 258 + break; 259 + case beq_op: 260 + case bne_op: 261 + case blt_op: 262 + case bge_op: 263 + case bltu_op: 264 + case bgeu_op: 265 + insn->type = INSN_JUMP_CONDITIONAL; 266 + insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15); 267 + break; 268 + default: 269 + return false; 270 + } 271 + 272 + return true; 273 + } 274 + 275 + int arch_decode_instruction(struct objtool_file *file, const struct section *sec, 276 + unsigned long offset, unsigned int maxlen, 277 + struct instruction *insn) 278 + { 279 + struct stack_op **ops_list = &insn->stack_ops; 280 + const struct elf *elf = file->elf; 281 + struct stack_op *op = NULL; 282 + union loongarch_instruction inst; 283 + 284 + if (!is_loongarch(elf)) 285 + return -1; 286 + 287 + if (maxlen < LOONGARCH_INSN_SIZE) 288 + return 0; 289 + 290 + insn->len = LOONGARCH_INSN_SIZE; 291 + insn->type = INSN_OTHER; 292 + insn->immediate = 0; 293 + 294 + inst = *(union loongarch_instruction *)(sec->data->d_buf + offset); 295 + 296 + if (decode_insn_reg0i26_fomat(inst, insn)) 297 + return 0; 298 + if (decode_insn_reg1i21_fomat(inst, insn)) 299 + return 0; 300 + if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op)) 301 + return 0; 302 + if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op)) 303 + return 0; 304 + if (decode_insn_reg2i16_fomat(inst, insn)) 305 + return 0; 306 + 307 + if (inst.word == 0) 308 + insn->type = INSN_NOP; 309 + else if (inst.reg0i15_format.opcode == break_op) { 310 + /* break */ 311 + insn->type = INSN_BUG; 312 + } else if (inst.reg2_format.opcode == ertn_op) { 313 + /* ertn */ 314 + insn->type = INSN_RETURN; 315 + } 316 + 317 + return 0; 318 + } 319 + 320 + const char *arch_nop_insn(int len) 321 + { 322 + static u32 nop; 323 + 324 + if (len != LOONGARCH_INSN_SIZE) 325 + WARN("invalid NOP size: %d\n", len); 326 + 327 + nop = LOONGARCH_INSN_NOP; 328 + 329 + return (const char *)&nop; 330 + } 331 + 332 + const char *arch_ret_insn(int len) 333 + { 334 + static u32 ret; 335 + 336 + if (len != LOONGARCH_INSN_SIZE) 337 + WARN("invalid RET size: %d\n", len); 338 + 339 + emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0); 340 + 341 + return (const char *)&ret; 342 + } 343 + 344 + void arch_initial_func_cfi_state(struct cfi_init_state *state) 345 + { 346 + int i; 347 + 348 + for (i = 0; i < CFI_NUM_REGS; i++) { 349 + state->regs[i].base = CFI_UNDEFINED; 350 + state->regs[i].offset = 0; 351 + } 352 + 353 + /* initial CFA (call frame address) */ 354 + state->cfa.base = CFI_SP; 355 + state->cfa.offset = 0; 356 + }
+22
tools/objtool/arch/loongarch/include/arch/cfi_regs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _OBJTOOL_ARCH_CFI_REGS_H 3 + #define _OBJTOOL_ARCH_CFI_REGS_H 4 + 5 + #define CFI_RA 1 6 + #define CFI_SP 3 7 + #define CFI_A0 4 8 + #define CFI_FP 22 9 + #define CFI_S0 23 10 + #define CFI_S1 24 11 + #define CFI_S2 25 12 + #define CFI_S3 26 13 + #define CFI_S4 27 14 + #define CFI_S5 28 15 + #define CFI_S6 29 16 + #define CFI_S7 30 17 + #define CFI_S8 31 18 + #define CFI_NUM_REGS 32 19 + 20 + #define CFI_BP CFI_FP 21 + 22 + #endif /* _OBJTOOL_ARCH_CFI_REGS_H */
+30
tools/objtool/arch/loongarch/include/arch/elf.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _OBJTOOL_ARCH_ELF_H 3 + #define _OBJTOOL_ARCH_ELF_H 4 + 5 + /* 6 + * See the following link for more info about ELF Relocation types: 7 + * https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html#_relocations 8 + */ 9 + #ifndef R_LARCH_NONE 10 + #define R_LARCH_NONE 0 11 + #endif 12 + #ifndef R_LARCH_32 13 + #define R_LARCH_32 1 14 + #endif 15 + #ifndef R_LARCH_64 16 + #define R_LARCH_64 2 17 + #endif 18 + #ifndef R_LARCH_32_PCREL 19 + #define R_LARCH_32_PCREL 99 20 + #endif 21 + 22 + #define R_NONE R_LARCH_NONE 23 + #define R_ABS32 R_LARCH_32 24 + #define R_ABS64 R_LARCH_64 25 + #define R_DATA32 R_LARCH_32_PCREL 26 + #define R_DATA64 R_LARCH_32_PCREL 27 + #define R_TEXT32 R_LARCH_32_PCREL 28 + #define R_TEXT64 R_LARCH_32_PCREL 29 + 30 + #endif /* _OBJTOOL_ARCH_ELF_H */
+33
tools/objtool/arch/loongarch/include/arch/special.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _OBJTOOL_ARCH_SPECIAL_H 3 + #define _OBJTOOL_ARCH_SPECIAL_H 4 + 5 + /* 6 + * See more info about struct exception_table_entry 7 + * in arch/loongarch/include/asm/extable.h 8 + */ 9 + #define EX_ENTRY_SIZE 12 10 + #define EX_ORIG_OFFSET 0 11 + #define EX_NEW_OFFSET 4 12 + 13 + /* 14 + * See more info about struct jump_entry 15 + * in include/linux/jump_label.h 16 + */ 17 + #define JUMP_ENTRY_SIZE 16 18 + #define JUMP_ORIG_OFFSET 0 19 + #define JUMP_NEW_OFFSET 4 20 + #define JUMP_KEY_OFFSET 8 21 + 22 + /* 23 + * See more info about struct alt_instr 24 + * in arch/loongarch/include/asm/alternative.h 25 + */ 26 + #define ALT_ENTRY_SIZE 12 27 + #define ALT_ORIG_OFFSET 0 28 + #define ALT_NEW_OFFSET 4 29 + #define ALT_FEATURE_OFFSET 8 30 + #define ALT_ORIG_LEN_OFFSET 10 31 + #define ALT_NEW_LEN_OFFSET 11 32 + 33 + #endif /* _OBJTOOL_ARCH_SPECIAL_H */
+171
tools/objtool/arch/loongarch/orc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + #include <linux/objtool_types.h> 3 + #include <asm/orc_types.h> 4 + 5 + #include <objtool/check.h> 6 + #include <objtool/orc.h> 7 + #include <objtool/warn.h> 8 + #include <objtool/endianness.h> 9 + 10 + int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn) 11 + { 12 + struct cfi_reg *fp = &cfi->regs[CFI_FP]; 13 + struct cfi_reg *ra = &cfi->regs[CFI_RA]; 14 + 15 + memset(orc, 0, sizeof(*orc)); 16 + 17 + if (!cfi) { 18 + /* 19 + * This is usually either unreachable nops/traps (which don't 20 + * trigger unreachable instruction warnings), or 21 + * STACK_FRAME_NON_STANDARD functions. 22 + */ 23 + orc->type = ORC_TYPE_UNDEFINED; 24 + return 0; 25 + } 26 + 27 + switch (cfi->type) { 28 + case UNWIND_HINT_TYPE_UNDEFINED: 29 + orc->type = ORC_TYPE_UNDEFINED; 30 + return 0; 31 + case UNWIND_HINT_TYPE_END_OF_STACK: 32 + orc->type = ORC_TYPE_END_OF_STACK; 33 + return 0; 34 + case UNWIND_HINT_TYPE_CALL: 35 + orc->type = ORC_TYPE_CALL; 36 + break; 37 + case UNWIND_HINT_TYPE_REGS: 38 + orc->type = ORC_TYPE_REGS; 39 + break; 40 + case UNWIND_HINT_TYPE_REGS_PARTIAL: 41 + orc->type = ORC_TYPE_REGS_PARTIAL; 42 + break; 43 + default: 44 + WARN_INSN(insn, "unknown unwind hint type %d", cfi->type); 45 + return -1; 46 + } 47 + 48 + orc->signal = cfi->signal; 49 + 50 + switch (cfi->cfa.base) { 51 + case CFI_SP: 52 + orc->sp_reg = ORC_REG_SP; 53 + break; 54 + case CFI_FP: 55 + orc->sp_reg = ORC_REG_FP; 56 + break; 57 + default: 58 + WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); 59 + return -1; 60 + } 61 + 62 + switch (fp->base) { 63 + case CFI_UNDEFINED: 64 + orc->fp_reg = ORC_REG_UNDEFINED; 65 + orc->fp_offset = 0; 66 + break; 67 + case CFI_CFA: 68 + orc->fp_reg = ORC_REG_PREV_SP; 69 + orc->fp_offset = fp->offset; 70 + break; 71 + case CFI_FP: 72 + orc->fp_reg = ORC_REG_FP; 73 + break; 74 + default: 75 + WARN_INSN(insn, "unknown FP base reg %d", fp->base); 76 + return -1; 77 + } 78 + 79 + switch (ra->base) { 80 + case CFI_UNDEFINED: 81 + orc->ra_reg = ORC_REG_UNDEFINED; 82 + orc->ra_offset = 0; 83 + break; 84 + case CFI_CFA: 85 + orc->ra_reg = ORC_REG_PREV_SP; 86 + orc->ra_offset = ra->offset; 87 + break; 88 + case CFI_FP: 89 + orc->ra_reg = ORC_REG_FP; 90 + break; 91 + default: 92 + WARN_INSN(insn, "unknown RA base reg %d", ra->base); 93 + return -1; 94 + } 95 + 96 + orc->sp_offset = cfi->cfa.offset; 97 + 98 + return 0; 99 + } 100 + 101 + int write_orc_entry(struct elf *elf, struct section *orc_sec, 102 + struct section *ip_sec, unsigned int idx, 103 + struct section *insn_sec, unsigned long insn_off, 104 + struct orc_entry *o) 105 + { 106 + struct orc_entry *orc; 107 + 108 + /* populate ORC data */ 109 + orc = (struct orc_entry *)orc_sec->data->d_buf + idx; 110 + memcpy(orc, o, sizeof(*orc)); 111 + 112 + /* populate reloc for ip */ 113 + if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, 114 + insn_sec, insn_off)) 115 + return -1; 116 + 117 + return 0; 118 + } 119 + 120 + static const char *reg_name(unsigned int reg) 121 + { 122 + switch (reg) { 123 + case ORC_REG_SP: 124 + return "sp"; 125 + case ORC_REG_FP: 126 + return "fp"; 127 + case ORC_REG_PREV_SP: 128 + return "prevsp"; 129 + default: 130 + return "?"; 131 + } 132 + } 133 + 134 + static const char *orc_type_name(unsigned int type) 135 + { 136 + switch (type) { 137 + case UNWIND_HINT_TYPE_CALL: 138 + return "call"; 139 + case UNWIND_HINT_TYPE_REGS: 140 + return "regs"; 141 + case UNWIND_HINT_TYPE_REGS_PARTIAL: 142 + return "regs (partial)"; 143 + default: 144 + return "?"; 145 + } 146 + } 147 + 148 + static void print_reg(unsigned int reg, int offset) 149 + { 150 + if (reg == ORC_REG_UNDEFINED) 151 + printf(" (und) "); 152 + else 153 + printf("%s + %3d", reg_name(reg), offset); 154 + 155 + } 156 + 157 + void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i) 158 + { 159 + printf("type:%s", orc_type_name(orc[i].type)); 160 + 161 + printf(" sp:"); 162 + print_reg(orc[i].sp_reg, orc[i].sp_offset); 163 + 164 + printf(" fp:"); 165 + print_reg(orc[i].fp_reg, orc[i].fp_offset); 166 + 167 + printf(" ra:"); 168 + print_reg(orc[i].ra_reg, orc[i].ra_offset); 169 + 170 + printf(" signal:%d\n", orc[i].signal); 171 + }
+15
tools/objtool/arch/loongarch/special.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + #include <objtool/special.h> 3 + 4 + bool arch_support_alt_relocation(struct special_alt *special_alt, 5 + struct instruction *insn, 6 + struct reloc *reloc) 7 + { 8 + return false; 9 + } 10 + 11 + struct reloc *arch_find_switch_table(struct objtool_file *file, 12 + struct instruction *insn) 13 + { 14 + return NULL; 15 + }
+1
tools/objtool/arch/x86/Build
··· 1 1 objtool-y += special.o 2 2 objtool-y += decode.o 3 + objtool-y += orc.o 3 4 4 5 inat_tables_script = ../arch/x86/tools/gen-insn-attr-x86.awk 5 6 inat_tables_maps = ../arch/x86/lib/x86-opcode-map.txt
+188
tools/objtool/arch/x86/orc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + #include <linux/objtool_types.h> 3 + #include <asm/orc_types.h> 4 + 5 + #include <objtool/check.h> 6 + #include <objtool/orc.h> 7 + #include <objtool/warn.h> 8 + #include <objtool/endianness.h> 9 + 10 + int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn) 11 + { 12 + struct cfi_reg *bp = &cfi->regs[CFI_BP]; 13 + 14 + memset(orc, 0, sizeof(*orc)); 15 + 16 + if (!cfi) { 17 + /* 18 + * This is usually either unreachable nops/traps (which don't 19 + * trigger unreachable instruction warnings), or 20 + * STACK_FRAME_NON_STANDARD functions. 21 + */ 22 + orc->type = ORC_TYPE_UNDEFINED; 23 + return 0; 24 + } 25 + 26 + switch (cfi->type) { 27 + case UNWIND_HINT_TYPE_UNDEFINED: 28 + orc->type = ORC_TYPE_UNDEFINED; 29 + return 0; 30 + case UNWIND_HINT_TYPE_END_OF_STACK: 31 + orc->type = ORC_TYPE_END_OF_STACK; 32 + return 0; 33 + case UNWIND_HINT_TYPE_CALL: 34 + orc->type = ORC_TYPE_CALL; 35 + break; 36 + case UNWIND_HINT_TYPE_REGS: 37 + orc->type = ORC_TYPE_REGS; 38 + break; 39 + case UNWIND_HINT_TYPE_REGS_PARTIAL: 40 + orc->type = ORC_TYPE_REGS_PARTIAL; 41 + break; 42 + default: 43 + WARN_INSN(insn, "unknown unwind hint type %d", cfi->type); 44 + return -1; 45 + } 46 + 47 + orc->signal = cfi->signal; 48 + 49 + switch (cfi->cfa.base) { 50 + case CFI_SP: 51 + orc->sp_reg = ORC_REG_SP; 52 + break; 53 + case CFI_SP_INDIRECT: 54 + orc->sp_reg = ORC_REG_SP_INDIRECT; 55 + break; 56 + case CFI_BP: 57 + orc->sp_reg = ORC_REG_BP; 58 + break; 59 + case CFI_BP_INDIRECT: 60 + orc->sp_reg = ORC_REG_BP_INDIRECT; 61 + break; 62 + case CFI_R10: 63 + orc->sp_reg = ORC_REG_R10; 64 + break; 65 + case CFI_R13: 66 + orc->sp_reg = ORC_REG_R13; 67 + break; 68 + case CFI_DI: 69 + orc->sp_reg = ORC_REG_DI; 70 + break; 71 + case CFI_DX: 72 + orc->sp_reg = ORC_REG_DX; 73 + break; 74 + default: 75 + WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); 76 + return -1; 77 + } 78 + 79 + switch (bp->base) { 80 + case CFI_UNDEFINED: 81 + orc->bp_reg = ORC_REG_UNDEFINED; 82 + break; 83 + case CFI_CFA: 84 + orc->bp_reg = ORC_REG_PREV_SP; 85 + break; 86 + case CFI_BP: 87 + orc->bp_reg = ORC_REG_BP; 88 + break; 89 + default: 90 + WARN_INSN(insn, "unknown BP base reg %d", bp->base); 91 + return -1; 92 + } 93 + 94 + orc->sp_offset = cfi->cfa.offset; 95 + orc->bp_offset = bp->offset; 96 + 97 + return 0; 98 + } 99 + 100 + int write_orc_entry(struct elf *elf, struct section *orc_sec, 101 + struct section *ip_sec, unsigned int idx, 102 + struct section *insn_sec, unsigned long insn_off, 103 + struct orc_entry *o) 104 + { 105 + struct orc_entry *orc; 106 + 107 + /* populate ORC data */ 108 + orc = (struct orc_entry *)orc_sec->data->d_buf + idx; 109 + memcpy(orc, o, sizeof(*orc)); 110 + orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); 111 + orc->bp_offset = bswap_if_needed(elf, orc->bp_offset); 112 + 113 + /* populate reloc for ip */ 114 + if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, 115 + insn_sec, insn_off)) 116 + return -1; 117 + 118 + return 0; 119 + } 120 + 121 + static const char *reg_name(unsigned int reg) 122 + { 123 + switch (reg) { 124 + case ORC_REG_PREV_SP: 125 + return "prevsp"; 126 + case ORC_REG_DX: 127 + return "dx"; 128 + case ORC_REG_DI: 129 + return "di"; 130 + case ORC_REG_BP: 131 + return "bp"; 132 + case ORC_REG_SP: 133 + return "sp"; 134 + case ORC_REG_R10: 135 + return "r10"; 136 + case ORC_REG_R13: 137 + return "r13"; 138 + case ORC_REG_BP_INDIRECT: 139 + return "bp(ind)"; 140 + case ORC_REG_SP_INDIRECT: 141 + return "sp(ind)"; 142 + default: 143 + return "?"; 144 + } 145 + } 146 + 147 + static const char *orc_type_name(unsigned int type) 148 + { 149 + switch (type) { 150 + case ORC_TYPE_UNDEFINED: 151 + return "(und)"; 152 + case ORC_TYPE_END_OF_STACK: 153 + return "end"; 154 + case ORC_TYPE_CALL: 155 + return "call"; 156 + case ORC_TYPE_REGS: 157 + return "regs"; 158 + case ORC_TYPE_REGS_PARTIAL: 159 + return "regs (partial)"; 160 + default: 161 + return "?"; 162 + } 163 + } 164 + 165 + static void print_reg(unsigned int reg, int offset) 166 + { 167 + if (reg == ORC_REG_BP_INDIRECT) 168 + printf("(bp%+d)", offset); 169 + else if (reg == ORC_REG_SP_INDIRECT) 170 + printf("(sp)%+d", offset); 171 + else if (reg == ORC_REG_UNDEFINED) 172 + printf("(und)"); 173 + else 174 + printf("%s%+d", reg_name(reg), offset); 175 + } 176 + 177 + void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i) 178 + { 179 + printf("type:%s", orc_type_name(orc[i].type)); 180 + 181 + printf(" sp:"); 182 + print_reg(orc[i].sp_reg, bswap_if_needed(dummy_elf, orc[i].sp_offset)); 183 + 184 + printf(" bp:"); 185 + print_reg(orc[i].bp_reg, bswap_if_needed(dummy_elf, orc[i].bp_offset)); 186 + 187 + printf(" signal:%d\n", orc[i].signal); 188 + }
+34 -18
tools/objtool/check.c
··· 20 20 #include <linux/hashtable.h> 21 21 #include <linux/kernel.h> 22 22 #include <linux/static_call_types.h> 23 + #include <linux/string.h> 23 24 24 25 struct alternative { 25 26 struct alternative *next; ··· 585 584 struct section *rsec; 586 585 struct reloc *reloc; 587 586 struct instruction *insn; 588 - s64 addend; 587 + unsigned long offset; 589 588 590 589 /* 591 590 * Check for manually annotated dead ends. ··· 595 594 goto reachable; 596 595 597 596 for_each_reloc(rsec, reloc) { 598 - 599 - if (reloc->sym->type != STT_SECTION) { 597 + if (reloc->sym->type == STT_SECTION) { 598 + offset = reloc_addend(reloc); 599 + } else if (reloc->sym->local_label) { 600 + offset = reloc->sym->offset; 601 + } else { 600 602 WARN("unexpected relocation symbol type in %s", rsec->name); 601 603 return -1; 602 604 } 603 605 604 - addend = reloc_addend(reloc); 605 - 606 - insn = find_insn(file, reloc->sym->sec, addend); 606 + insn = find_insn(file, reloc->sym->sec, offset); 607 607 if (insn) 608 608 insn = prev_insn_same_sec(file, insn); 609 - else if (addend == reloc->sym->sec->sh.sh_size) { 609 + else if (offset == reloc->sym->sec->sh.sh_size) { 610 610 insn = find_last_insn(file, reloc->sym->sec); 611 611 if (!insn) { 612 612 WARN("can't find unreachable insn at %s+0x%" PRIx64, 613 - reloc->sym->sec->name, addend); 613 + reloc->sym->sec->name, offset); 614 614 return -1; 615 615 } 616 616 } else { 617 617 WARN("can't find unreachable insn at %s+0x%" PRIx64, 618 - reloc->sym->sec->name, addend); 618 + reloc->sym->sec->name, offset); 619 619 return -1; 620 620 } 621 621 ··· 635 633 return 0; 636 634 637 635 for_each_reloc(rsec, reloc) { 638 - 639 - if (reloc->sym->type != STT_SECTION) { 636 + if (reloc->sym->type == STT_SECTION) { 637 + offset = reloc_addend(reloc); 638 + } else if (reloc->sym->local_label) { 639 + offset = reloc->sym->offset; 640 + } else { 640 641 WARN("unexpected relocation symbol type in %s", rsec->name); 641 642 return -1; 642 643 } 643 644 644 - addend = reloc_addend(reloc); 645 - 646 - insn = find_insn(file, reloc->sym->sec, addend); 645 + insn = find_insn(file, reloc->sym->sec, offset); 647 646 if (insn) 648 647 insn = prev_insn_same_sec(file, insn); 649 - else if (addend == reloc->sym->sec->sh.sh_size) { 648 + else if (offset == reloc->sym->sec->sh.sh_size) { 650 649 insn = find_last_insn(file, reloc->sym->sec); 651 650 if (!insn) { 652 651 WARN("can't find reachable insn at %s+0x%" PRIx64, 653 - reloc->sym->sec->name, addend); 652 + reloc->sym->sec->name, offset); 654 653 return -1; 655 654 } 656 655 } else { 657 656 WARN("can't find reachable insn at %s+0x%" PRIx64, 658 - reloc->sym->sec->name, addend); 657 + reloc->sym->sec->name, offset); 659 658 return -1; 660 659 } 661 660 ··· 2227 2224 struct unwind_hint *hint; 2228 2225 struct instruction *insn; 2229 2226 struct reloc *reloc; 2227 + unsigned long offset; 2230 2228 int i; 2231 2229 2232 2230 sec = find_section_by_name(file->elf, ".discard.unwind_hints"); ··· 2255 2251 return -1; 2256 2252 } 2257 2253 2258 - insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); 2254 + if (reloc->sym->type == STT_SECTION) { 2255 + offset = reloc_addend(reloc); 2256 + } else if (reloc->sym->local_label) { 2257 + offset = reloc->sym->offset; 2258 + } else { 2259 + WARN("unexpected relocation symbol type in %s", sec->rsec->name); 2260 + return -1; 2261 + } 2262 + 2263 + insn = find_insn(file, reloc->sym->sec, offset); 2259 2264 if (!insn) { 2260 2265 WARN("can't find insn for unwind_hints[%d]", i); 2261 2266 return -1; ··· 2535 2522 struct symbol *func; 2536 2523 2537 2524 for_each_sym(file, func) { 2525 + if (func->type == STT_NOTYPE && strstarts(func->name, ".L")) 2526 + func->local_label = true; 2527 + 2538 2528 if (func->bind != STB_GLOBAL) 2539 2529 continue; 2540 2530
+1
tools/objtool/include/objtool/elf.h
··· 67 67 u8 profiling_func : 1; 68 68 u8 warned : 1; 69 69 u8 embedded_insn : 1; 70 + u8 local_label : 1; 70 71 struct list_head pv_target; 71 72 struct reloc *relocs; 72 73 };
+14
tools/objtool/include/objtool/orc.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _OBJTOOL_ORC_H 3 + #define _OBJTOOL_ORC_H 4 + 5 + #include <objtool/check.h> 6 + 7 + int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn); 8 + void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i); 9 + int write_orc_entry(struct elf *elf, struct section *orc_sec, 10 + struct section *ip_sec, unsigned int idx, 11 + struct section *insn_sec, unsigned long insn_off, 12 + struct orc_entry *o); 13 + 14 + #endif /* _OBJTOOL_ORC_H */
+2 -67
tools/objtool/orc_dump.c
··· 6 6 #include <unistd.h> 7 7 #include <asm/orc_types.h> 8 8 #include <objtool/objtool.h> 9 + #include <objtool/orc.h> 9 10 #include <objtool/warn.h> 10 11 #include <objtool/endianness.h> 11 - 12 - static const char *reg_name(unsigned int reg) 13 - { 14 - switch (reg) { 15 - case ORC_REG_PREV_SP: 16 - return "prevsp"; 17 - case ORC_REG_DX: 18 - return "dx"; 19 - case ORC_REG_DI: 20 - return "di"; 21 - case ORC_REG_BP: 22 - return "bp"; 23 - case ORC_REG_SP: 24 - return "sp"; 25 - case ORC_REG_R10: 26 - return "r10"; 27 - case ORC_REG_R13: 28 - return "r13"; 29 - case ORC_REG_BP_INDIRECT: 30 - return "bp(ind)"; 31 - case ORC_REG_SP_INDIRECT: 32 - return "sp(ind)"; 33 - default: 34 - return "?"; 35 - } 36 - } 37 - 38 - static const char *orc_type_name(unsigned int type) 39 - { 40 - switch (type) { 41 - case ORC_TYPE_UNDEFINED: 42 - return "(und)"; 43 - case ORC_TYPE_END_OF_STACK: 44 - return "end"; 45 - case ORC_TYPE_CALL: 46 - return "call"; 47 - case ORC_TYPE_REGS: 48 - return "regs"; 49 - case ORC_TYPE_REGS_PARTIAL: 50 - return "regs (partial)"; 51 - default: 52 - return "?"; 53 - } 54 - } 55 - 56 - static void print_reg(unsigned int reg, int offset) 57 - { 58 - if (reg == ORC_REG_BP_INDIRECT) 59 - printf("(bp%+d)", offset); 60 - else if (reg == ORC_REG_SP_INDIRECT) 61 - printf("(sp)%+d", offset); 62 - else if (reg == ORC_REG_UNDEFINED) 63 - printf("(und)"); 64 - else 65 - printf("%s%+d", reg_name(reg), offset); 66 - } 67 12 68 13 int orc_dump(const char *_objname) 69 14 { ··· 150 205 printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i])); 151 206 } 152 207 153 - printf("type:%s", orc_type_name(orc[i].type)); 154 - 155 - printf(" sp:"); 156 - 157 - print_reg(orc[i].sp_reg, bswap_if_needed(&dummy_elf, orc[i].sp_offset)); 158 - 159 - printf(" bp:"); 160 - 161 - print_reg(orc[i].bp_reg, bswap_if_needed(&dummy_elf, orc[i].bp_offset)); 162 - 163 - printf(" signal:%d\n", orc[i].signal); 208 + orc_print_dump(&dummy_elf, orc, i); 164 209 } 165 210 166 211 elf_end(elf);
+1 -112
tools/objtool/orc_gen.c
··· 10 10 #include <asm/orc_types.h> 11 11 12 12 #include <objtool/check.h> 13 + #include <objtool/orc.h> 13 14 #include <objtool/warn.h> 14 15 #include <objtool/endianness.h> 15 - 16 - static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, 17 - struct instruction *insn) 18 - { 19 - struct cfi_reg *bp = &cfi->regs[CFI_BP]; 20 - 21 - memset(orc, 0, sizeof(*orc)); 22 - 23 - if (!cfi) { 24 - /* 25 - * This is usually either unreachable nops/traps (which don't 26 - * trigger unreachable instruction warnings), or 27 - * STACK_FRAME_NON_STANDARD functions. 28 - */ 29 - orc->type = ORC_TYPE_UNDEFINED; 30 - return 0; 31 - } 32 - 33 - switch (cfi->type) { 34 - case UNWIND_HINT_TYPE_UNDEFINED: 35 - orc->type = ORC_TYPE_UNDEFINED; 36 - return 0; 37 - case UNWIND_HINT_TYPE_END_OF_STACK: 38 - orc->type = ORC_TYPE_END_OF_STACK; 39 - return 0; 40 - case UNWIND_HINT_TYPE_CALL: 41 - orc->type = ORC_TYPE_CALL; 42 - break; 43 - case UNWIND_HINT_TYPE_REGS: 44 - orc->type = ORC_TYPE_REGS; 45 - break; 46 - case UNWIND_HINT_TYPE_REGS_PARTIAL: 47 - orc->type = ORC_TYPE_REGS_PARTIAL; 48 - break; 49 - default: 50 - WARN_INSN(insn, "unknown unwind hint type %d", cfi->type); 51 - return -1; 52 - } 53 - 54 - orc->signal = cfi->signal; 55 - 56 - switch (cfi->cfa.base) { 57 - case CFI_SP: 58 - orc->sp_reg = ORC_REG_SP; 59 - break; 60 - case CFI_SP_INDIRECT: 61 - orc->sp_reg = ORC_REG_SP_INDIRECT; 62 - break; 63 - case CFI_BP: 64 - orc->sp_reg = ORC_REG_BP; 65 - break; 66 - case CFI_BP_INDIRECT: 67 - orc->sp_reg = ORC_REG_BP_INDIRECT; 68 - break; 69 - case CFI_R10: 70 - orc->sp_reg = ORC_REG_R10; 71 - break; 72 - case CFI_R13: 73 - orc->sp_reg = ORC_REG_R13; 74 - break; 75 - case CFI_DI: 76 - orc->sp_reg = ORC_REG_DI; 77 - break; 78 - case CFI_DX: 79 - orc->sp_reg = ORC_REG_DX; 80 - break; 81 - default: 82 - WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); 83 - return -1; 84 - } 85 - 86 - switch (bp->base) { 87 - case CFI_UNDEFINED: 88 - orc->bp_reg = ORC_REG_UNDEFINED; 89 - break; 90 - case CFI_CFA: 91 - orc->bp_reg = ORC_REG_PREV_SP; 92 - break; 93 - case CFI_BP: 94 - orc->bp_reg = ORC_REG_BP; 95 - break; 96 - default: 97 - WARN_INSN(insn, "unknown BP base reg %d", bp->base); 98 - return -1; 99 - } 100 - 101 - orc->sp_offset = cfi->cfa.offset; 102 - orc->bp_offset = bp->offset; 103 - 104 - return 0; 105 - } 106 - 107 - static int write_orc_entry(struct elf *elf, struct section *orc_sec, 108 - struct section *ip_sec, unsigned int idx, 109 - struct section *insn_sec, unsigned long insn_off, 110 - struct orc_entry *o) 111 - { 112 - struct orc_entry *orc; 113 - 114 - /* populate ORC data */ 115 - orc = (struct orc_entry *)orc_sec->data->d_buf + idx; 116 - memcpy(orc, o, sizeof(*orc)); 117 - orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); 118 - orc->bp_offset = bswap_if_needed(elf, orc->bp_offset); 119 - 120 - /* populate reloc for ip */ 121 - if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, 122 - insn_sec, insn_off)) 123 - return -1; 124 - 125 - return 0; 126 - } 127 16 128 17 struct orc_list_entry { 129 18 struct list_head list;