Commit 84bd7e08 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'loongarch-fixes-6.2-1' of...

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

Pull LoongArch fixes from Huacai Chen:
 "Fix a missing elf_hwcap, fix some stack unwinder bugs and two trivial
  cleanups"

* tag 'loongarch-fixes-6.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson:
  LoongArch: Add generic ex-handler unwind in prologue unwinder
  LoongArch: Strip guess unwinder out from prologue unwinder
  LoongArch: Use correct sp value to get graph addr in stack unwinders
  LoongArch: Get frame info in unwind_start() when regs is not available
  LoongArch: Adjust PC value when unwind next frame in unwinder
  LoongArch: Simplify larch_insn_gen_xxx implementation
  LoongArch: Use common function sign_extend64()
  LoongArch: Add HWCAP_LOONGARCH_CPUCFG to elf_hwcap
parents c1649ec5 dc74a9e8
......@@ -10,8 +10,6 @@
#define FTRACE_REGS_PLT_IDX 1
#define NR_FTRACE_PLTS 2
#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))
#ifdef CONFIG_FUNCTION_TRACER
#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
......
......@@ -377,14 +377,6 @@ static inline bool unsigned_imm_check(unsigned long val, unsigned int bit)
return val < (1UL << bit);
}
static inline unsigned long sign_extend(unsigned long val, unsigned int idx)
{
if (!is_imm_negative(val, idx + 1))
return ((1UL << idx) - 1) & val;
else
return ~((1UL << idx) - 1) | val;
}
#define DEF_EMIT_REG0I26_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
int offset) \
......@@ -401,6 +393,7 @@ static inline void emit_##NAME(union loongarch_instruction *insn, \
}
DEF_EMIT_REG0I26_FORMAT(b, b_op)
DEF_EMIT_REG0I26_FORMAT(bl, bl_op)
#define DEF_EMIT_REG1I20_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
......
......@@ -8,7 +8,9 @@
#define _ASM_UNWIND_H
#include <linux/sched.h>
#include <linux/ftrace.h>
#include <asm/ptrace.h>
#include <asm/stacktrace.h>
enum unwinder_type {
......@@ -20,11 +22,13 @@ struct unwind_state {
char type; /* UNWINDER_XXX */
struct stack_info stack_info;
struct task_struct *task;
bool first, error, is_ftrace;
bool first, error, reset;
int graph_idx;
unsigned long sp, pc, ra;
};
bool default_next_frame(struct unwind_state *state);
void unwind_start(struct unwind_state *state,
struct task_struct *task, struct pt_regs *regs);
bool unwind_next_frame(struct unwind_state *state);
......@@ -40,4 +44,39 @@ static inline bool unwind_error(struct unwind_state *state)
return state->error;
}
#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))
static inline unsigned long unwind_graph_addr(struct unwind_state *state,
unsigned long pc, unsigned long cfa)
{
return ftrace_graph_ret_addr(state->task, &state->graph_idx,
pc, (unsigned long *)(cfa - GRAPH_FAKE_OFFSET));
}
static __always_inline void __unwind_start(struct unwind_state *state,
struct task_struct *task, struct pt_regs *regs)
{
memset(state, 0, sizeof(*state));
if (regs) {
state->sp = regs->regs[3];
state->pc = regs->csr_era;
state->ra = regs->regs[1];
} else if (task && task != current) {
state->sp = thread_saved_fp(task);
state->pc = thread_saved_ra(task);
state->ra = 0;
} else {
state->sp = (unsigned long)__builtin_frame_address(0);
state->pc = (unsigned long)__builtin_return_address(0);
state->ra = 0;
}
state->task = task;
get_stack_info(state->sp, state->task, &state->stack_info);
state->pc = unwind_graph_addr(state, state->pc, state->sp);
}
static __always_inline unsigned long __unwind_get_return_address(struct unwind_state *state)
{
return unwind_done(state) ? 0 : state->pc;
}
#endif /* _ASM_UNWIND_H */
......@@ -8,7 +8,7 @@ extra-y := vmlinux.lds
obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \
traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \
elf.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o \
alternative.o unaligned.o
alternative.o unaligned.o unwind.o
obj-$(CONFIG_ACPI) += acpi.o
obj-$(CONFIG_EFI) += efi.o
......
......@@ -74,7 +74,7 @@ static void __init_or_module recompute_jump(union loongarch_instruction *buf,
switch (src->reg0i26_format.opcode) {
case b_op:
case bl_op:
jump_addr = cur_pc + sign_extend((si_h << 16 | si_l) << 2, 27);
jump_addr = cur_pc + sign_extend64((si_h << 16 | si_l) << 2, 27);
if (in_alt_jump(jump_addr, start, end))
return;
offset = jump_addr - pc;
......@@ -93,7 +93,7 @@ static void __init_or_module recompute_jump(union loongarch_instruction *buf,
fallthrough;
case beqz_op:
case bnez_op:
jump_addr = cur_pc + sign_extend((si_h << 16 | si_l) << 2, 22);
jump_addr = cur_pc + sign_extend64((si_h << 16 | si_l) << 2, 22);
if (in_alt_jump(jump_addr, start, end))
return;
offset = jump_addr - pc;
......@@ -112,7 +112,7 @@ static void __init_or_module recompute_jump(union loongarch_instruction *buf,
case bge_op:
case bltu_op:
case bgeu_op:
jump_addr = cur_pc + sign_extend(si << 2, 17);
jump_addr = cur_pc + sign_extend64(si << 2, 17);
if (in_alt_jump(jump_addr, start, end))
return;
offset = jump_addr - pc;
......
......@@ -94,7 +94,7 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)
c->options = LOONGARCH_CPU_CPUCFG | LOONGARCH_CPU_CSR |
LOONGARCH_CPU_TLB | LOONGARCH_CPU_VINT | LOONGARCH_CPU_WATCH;
elf_hwcap |= HWCAP_LOONGARCH_CRC32;
elf_hwcap = HWCAP_LOONGARCH_CPUCFG | HWCAP_LOONGARCH_CRC32;
config = read_cpucfg(LOONGARCH_CPUCFG1);
if (config & CPUCFG1_UAL) {
......
......@@ -67,14 +67,17 @@ SYM_FUNC_END(except_vec_cex)
.macro BUILD_HANDLER exception handler prep
.align 5
SYM_FUNC_START(handle_\exception)
666:
BACKUP_T0T1
SAVE_ALL
build_prep_\prep
move a0, sp
la.abs t0, do_\handler
jirl ra, t0, 0
668:
RESTORE_ALL_AND_RET
SYM_FUNC_END(handle_\exception)
SYM_DATA(unwind_hint_\exception, .word 668b - 666b)
.endm
BUILD_HANDLER ade ade badv
......
......@@ -58,7 +58,6 @@ u32 larch_insn_gen_nop(void)
u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
{
long offset = dest - pc;
unsigned int immediate_l, immediate_h;
union loongarch_instruction insn;
if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
......@@ -66,15 +65,7 @@ u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
return INSN_BREAK;
}
offset >>= 2;
immediate_l = offset & 0xffff;
offset >>= 16;
immediate_h = offset & 0x3ff;
insn.reg0i26_format.opcode = b_op;
insn.reg0i26_format.immediate_l = immediate_l;
insn.reg0i26_format.immediate_h = immediate_h;
emit_b(&insn, offset >> 2);
return insn.word;
}
......@@ -82,7 +73,6 @@ u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
{
long offset = dest - pc;
unsigned int immediate_l, immediate_h;
union loongarch_instruction insn;
if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
......@@ -90,15 +80,7 @@ u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
return INSN_BREAK;
}
offset >>= 2;
immediate_l = offset & 0xffff;
offset >>= 16;
immediate_h = offset & 0x3ff;
insn.reg0i26_format.opcode = bl_op;
insn.reg0i26_format.immediate_l = immediate_l;
insn.reg0i26_format.immediate_h = immediate_h;
emit_bl(&insn, offset >> 2);
return insn.word;
}
......@@ -107,10 +89,7 @@ u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongar
{
union loongarch_instruction insn;
insn.reg3_format.opcode = or_op;
insn.reg3_format.rd = rd;
insn.reg3_format.rj = rj;
insn.reg3_format.rk = rk;
emit_or(&insn, rd, rj, rk);
return insn.word;
}
......@@ -124,9 +103,7 @@ u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm)
{
union loongarch_instruction insn;
insn.reg1i20_format.opcode = lu12iw_op;
insn.reg1i20_format.rd = rd;
insn.reg1i20_format.immediate = imm;
emit_lu12iw(&insn, rd, imm);
return insn.word;
}
......@@ -135,9 +112,7 @@ u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
{
union loongarch_instruction insn;
insn.reg1i20_format.opcode = lu32id_op;
insn.reg1i20_format.rd = rd;
insn.reg1i20_format.immediate = imm;
emit_lu32id(&insn, rd, imm);
return insn.word;
}
......@@ -146,10 +121,7 @@ u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm)
{
union loongarch_instruction insn;
insn.reg2i12_format.opcode = lu52id_op;
insn.reg2i12_format.rd = rd;
insn.reg2i12_format.rj = rj;
insn.reg2i12_format.immediate = imm;
emit_lu52id(&insn, rd, rj, imm);
return insn.word;
}
......@@ -158,10 +130,7 @@ u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned l
{
union loongarch_instruction insn;
insn.reg2i16_format.opcode = jirl_op;
insn.reg2i16_format.rd = rd;
insn.reg2i16_format.rj = rj;
insn.reg2i16_format.immediate = (dest - pc) >> 2;
emit_jirl(&insn, rj, rd, (dest - pc) >> 2);
return insn.word;
}
......@@ -191,20 +191,14 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
unsigned long __get_wchan(struct task_struct *task)
{
unsigned long pc;
unsigned long pc = 0;
struct unwind_state state;
if (!try_get_task_stack(task))
return 0;
unwind_start(&state, task, NULL);
state.sp = thread_saved_fp(task);
get_stack_info(state.sp, state.task, &state.stack_info);
state.pc = thread_saved_ra(task);
#ifdef CONFIG_UNWINDER_PROLOGUE
state.type = UNWINDER_PROLOGUE;
#endif
for (; !unwind_done(&state); unwind_next_frame(&state)) {
for (unwind_start(&state, task, NULL);
!unwind_done(&state); unwind_next_frame(&state)) {
pc = unwind_get_return_address(&state);
if (!pc)
break;
......
......@@ -72,9 +72,6 @@ static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,
if (!task)
task = current;
if (user_mode(regs))
state.type = UNWINDER_GUESS;
printk("%sCall Trace:", loglvl);
for (unwind_start(&state, task, pregs);
!unwind_done(&state); unwind_next_frame(&state)) {
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022-2023 Loongson Technology Corporation Limited
*/
#include <linux/kernel.h>
#include <linux/ftrace.h>
#include <asm/unwind.h>
bool default_next_frame(struct unwind_state *state)
{
struct stack_info *info = &state->stack_info;
unsigned long addr;
if (unwind_done(state))
return false;
do {
for (state->sp += sizeof(unsigned long);
state->sp < info->end; state->sp += sizeof(unsigned long)) {
addr = *(unsigned long *)(state->sp);
state->pc = unwind_graph_addr(state, addr, state->sp + 8);
if (__kernel_text_address(state->pc))
return true;
}
state->sp = info->next_sp;
} while (!get_stack_info(state->sp, state->task, info));
return false;
}
......@@ -2,37 +2,18 @@
/*
* Copyright (C) 2022 Loongson Technology Corporation Limited
*/
#include <linux/kernel.h>
#include <linux/ftrace.h>
#include <asm/unwind.h>
unsigned long unwind_get_return_address(struct unwind_state *state)
{
if (unwind_done(state))
return 0;
else if (state->first)
return state->pc;
return *(unsigned long *)(state->sp);
return __unwind_get_return_address(state);
}
EXPORT_SYMBOL_GPL(unwind_get_return_address);
void unwind_start(struct unwind_state *state, struct task_struct *task,
struct pt_regs *regs)
{
memset(state, 0, sizeof(*state));
if (regs) {
state->sp = regs->regs[3];
state->pc = regs->csr_era;
}
state->task = task;
state->first = true;
get_stack_info(state->sp, state->task, &state->stack_info);
__unwind_start(state, task, regs);
if (!unwind_done(state) && !__kernel_text_address(state->pc))
unwind_next_frame(state);
}
......@@ -40,30 +21,6 @@ EXPORT_SYMBOL_GPL(unwind_start);
bool unwind_next_frame(struct unwind_state *state)
{
struct stack_info *info = &state->stack_info;
unsigned long addr;
if (unwind_done(state))
return false;
if (state->first)
state->first = false;
do {
for (state->sp += sizeof(unsigned long);
state->sp < info->end;
state->sp += sizeof(unsigned long)) {
addr = *(unsigned long *)(state->sp);
state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
if (__kernel_text_address(addr))
return true;
}
state->sp = info->next_sp;
} while (!get_stack_info(state->sp, state->task, info));
return false;
return default_next_frame(state);
}
EXPORT_SYMBOL_GPL(unwind_next_frame);
......@@ -2,61 +2,116 @@
/*
* Copyright (C) 2022 Loongson Technology Corporation Limited
*/
#include <linux/cpumask.h>
#include <linux/ftrace.h>
#include <linux/kallsyms.h>
#include <asm/inst.h>
#include <asm/loongson.h>
#include <asm/ptrace.h>
#include <asm/setup.h>
#include <asm/unwind.h>
static inline void unwind_state_fixup(struct unwind_state *state)
{
#ifdef CONFIG_DYNAMIC_FTRACE
static unsigned long ftrace = (unsigned long)ftrace_call + 4;
if (state->pc == ftrace)
state->is_ftrace = true;
extern const int unwind_hint_ade;
extern const int unwind_hint_ale;
extern const int unwind_hint_bp;
extern const int unwind_hint_fpe;
extern const int unwind_hint_fpu;
extern const int unwind_hint_lsx;
extern const int unwind_hint_lasx;
extern const int unwind_hint_lbt;
extern const int unwind_hint_ri;
extern const int unwind_hint_watch;
extern unsigned long eentry;
#ifdef CONFIG_NUMA
extern unsigned long pcpu_handlers[NR_CPUS];
#endif
}
unsigned long unwind_get_return_address(struct unwind_state *state)
static inline bool scan_handlers(unsigned long entry_offset)
{
int idx, offset;
if (unwind_done(state))
return 0;
else if (state->type)
return state->pc;
else if (state->first)
return state->pc;
return *(unsigned long *)(state->sp);
if (entry_offset >= EXCCODE_INT_START * VECSIZE)
return false;
idx = entry_offset / VECSIZE;
offset = entry_offset % VECSIZE;
switch (idx) {
case EXCCODE_ADE:
return offset == unwind_hint_ade;
case EXCCODE_ALE:
return offset == unwind_hint_ale;
case EXCCODE_BP:
return offset == unwind_hint_bp;
case EXCCODE_FPE:
return offset == unwind_hint_fpe;
case EXCCODE_FPDIS:
return offset == unwind_hint_fpu;
case EXCCODE_LSXDIS:
return offset == unwind_hint_lsx;
case EXCCODE_LASXDIS:
return offset == unwind_hint_lasx;
case EXCCODE_BTDIS:
return offset == unwind_hint_lbt;
case EXCCODE_INE:
return offset == unwind_hint_ri;
case EXCCODE_WATCH:
return offset == unwind_hint_watch;
default:
return false;
}
}
EXPORT_SYMBOL_GPL(unwind_get_return_address);
static bool unwind_by_guess(struct unwind_state *state)
static inline bool fix_exception(unsigned long pc)
{
struct stack_info *info = &state->stack_info;
unsigned long addr;
for (state->sp += sizeof(unsigned long);
state->sp < info->end;
state->sp += sizeof(unsigned long)) {
addr = *(unsigned long *)(state->sp);
state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
if (__kernel_text_address(addr))
#ifdef CONFIG_NUMA
int cpu;
for_each_possible_cpu(cpu) {
if (!pcpu_handlers[cpu])
continue;
if (scan_handlers(pc - pcpu_handlers[cpu]))
return true;
}
#endif
return scan_handlers(pc - eentry);
}
/*
* As we meet ftrace_regs_entry, reset first flag like first doing
* tracing. Prologue analysis will stop soon because PC is at entry.
*/
static inline bool fix_ftrace(unsigned long pc)
{
#ifdef CONFIG_DYNAMIC_FTRACE
return pc == (unsigned long)ftrace_call + LOONGARCH_INSN_SIZE;
#else
return false;
#endif
}
static inline bool unwind_state_fixup(struct unwind_state *state)
{
if (!fix_exception(state->pc) && !fix_ftrace(state->pc))
return false;
state->reset = true;
return true;
}
/*
* LoongArch function prologue is like follows,
* [instructions not use stack var]
* addi.d sp, sp, -imm
* st.d xx, sp, offset <- save callee saved regs and
* st.d yy, sp, offset save ra if function is nest.
* [others instructions]
*/
static bool unwind_by_prologue(struct unwind_state *state)
{
long frame_ra = -1;
unsigned long frame_size = 0;
unsigned long size, offset, pc = state->pc;
unsigned long size, offset, pc;
struct pt_regs *regs;
struct stack_info *info = &state->stack_info;
union loongarch_instruction *ip, *ip_end;
......@@ -64,20 +119,21 @@ static bool unwind_by_prologue(struct unwind_state *state)
if (state->sp >= info->end || state->sp < info->begin)
return false;
if (state->is_ftrace) {
/*
* As we meet ftrace_regs_entry, reset first flag like first doing
* tracing. Prologue analysis will stop soon because PC is at entry.
*/
if (state->reset) {
regs = (struct pt_regs *)state->sp;
state->first = true;
state->is_ftrace = false;
state->reset = false;
state->pc = regs->csr_era;
state->ra = regs->regs[1];
state->sp = regs->regs[3];
return true;
}
/*
* When first is not set, the PC is a return address in the previous frame.
* We need to adjust its value in case overflow to the next symbol.
*/
pc = state->pc - (state->first ? 0 : LOONGARCH_INSN_SIZE);
if (!kallsyms_lookup_size_offset(pc, &size, &offset))
return false;
......@@ -93,6 +149,10 @@ static bool unwind_by_prologue(struct unwind_state *state)
ip++;
}
/*
* Can't find stack alloc action, PC may be in a leaf function. Only the
* first being true is reasonable, otherwise indicate analysis is broken.
*/
if (!frame_size) {
if (state->first)
goto first;
......@@ -110,6 +170,7 @@ static bool unwind_by_prologue(struct unwind_state *state)
ip++;
}
/* Can't find save $ra action, PC may be in a leaf function, too. */
if (frame_ra < 0) {
if (state->first) {
state->sp = state->sp + frame_size;
......@@ -118,88 +179,47 @@ static bool unwind_by_prologue(struct unwind_state *state)
return false;
}
if (state->first)
state->first = false;
state->pc = *(unsigned long *)(state->sp + frame_ra);
state->sp = state->sp + frame_size;
goto out;
first:
state->first = false;
if (state->pc == state->ra)
return false;
state->pc = state->ra;
out:
unwind_state_fixup(state);
return !!__kernel_text_address(state->pc);
}
void unwind_start(struct unwind_state *state, struct task_struct *task,
struct pt_regs *regs)
{
memset(state, 0, sizeof(*state));
if (regs && __kernel_text_address(regs->csr_era)) {
state->pc = regs->csr_era;
state->sp = regs->regs[3];
state->ra = regs->regs[1];
state->type = UNWINDER_PROLOGUE;
}
state->task = task;
state->first = true;
get_stack_info(state->sp, state->task, &state->stack_info);
if (!unwind_done(state) && !__kernel_text_address(state->pc))
unwind_next_frame(state);
state->first = false;
return unwind_state_fixup(state) || __kernel_text_address(state->pc);
}
EXPORT_SYMBOL_GPL(unwind_start);
bool unwind_next_frame(struct unwind_state *state)
static bool next_frame(struct unwind_state *state)
{
struct stack_info *info = &state->stack_info;
struct pt_regs *regs;
unsigned long pc;
struct pt_regs *regs;
struct stack_info *info = &state->stack_info;
if (unwind_done(state))
return false;
do {
switch (state->type) {
case UNWINDER_GUESS:
state->first = false;
if (unwind_by_guess(state))
return true;
break;
if (unwind_by_prologue(state)) {
state->pc = unwind_graph_addr(state, state->pc, state->sp);
return true;
}
if (info->type == STACK_TYPE_IRQ && info->end == state->sp) {
regs = (struct pt_regs *)info->next_sp;
pc = regs->csr_era;
if (user_mode(regs) || !__kernel_text_address(pc))
return false;
state->first = true;
state->pc = pc;
state->ra = regs->regs[1];
state->sp = regs->regs[3];
get_stack_info(state->sp, state->task, info);
case UNWINDER_PROLOGUE:
if (unwind_by_prologue(state)) {
state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
state->pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
return true;
}
if (info->type == STACK_TYPE_IRQ &&
info->end == state->sp) {
regs = (struct pt_regs *)info->next_sp;
pc = regs->csr_era;
if (user_mode(regs) || !__kernel_text_address(pc))
return false;
state->first = true;
state->ra = regs->regs[1];
state->sp = regs->regs[3];
state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
get_stack_info(state->sp, state->task, info);
return true;
}
return true;
}
state->sp = info->next_sp;
......@@ -208,4 +228,36 @@ bool unwind_next_frame(struct unwind_state *state)
return false;
}
unsigned long unwind_get_return_address(struct unwind_state *state)
{
return __unwind_get_return_address(state);
}
EXPORT_SYMBOL_GPL(unwind_get_return_address);
void unwind_start(struct unwind_state *state, struct task_struct *task,
struct pt_regs *regs)
{
__unwind_start(state, task, regs);
state->type = UNWINDER_PROLOGUE;
state->first = true;
/*
* The current PC is not kernel text address, we cannot find its
* relative symbol. Thus, prologue analysis will be broken. Luckily,
* we can use the default_next_frame().
*/
if (!__kernel_text_address(state->pc)) {
state->type = UNWINDER_GUESS;
if (!unwind_done(state))
unwind_next_frame(state);
}
}
EXPORT_SYMBOL_GPL(unwind_start);
bool unwind_next_frame(struct unwind_state *state)
{
return state->type == UNWINDER_PROLOGUE ?
next_frame(state) : default_next_frame(state);
}
EXPORT_SYMBOL_GPL(unwind_next_frame);
......@@ -251,7 +251,7 @@ static void output_pgtable_bits_defines(void)
}
#ifdef CONFIG_NUMA
static unsigned long pcpu_handlers[NR_CPUS];
unsigned long pcpu_handlers[NR_CPUS];
#endif
extern long exception_handlers[VECSIZE * 128 / sizeof(long)];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment