Skip to content

Commit

Permalink
Merge tag 'loongarch-fixes-6.2-1' of git://git.kernel.org/pub/scm/lin…
Browse files Browse the repository at this point in the history
…ux/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
  • Loading branch information
Linus Torvalds committed Jan 18, 2023
2 parents c1649ec + dc74a9e commit 84bd7e0
Show file tree
Hide file tree
Showing 14 changed files with 247 additions and 213 deletions.
2 changes: 0 additions & 2 deletions arch/loongarch/include/asm/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
9 changes: 1 addition & 8 deletions arch/loongarch/include/asm/inst.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand All @@ -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, \
Expand Down
41 changes: 40 additions & 1 deletion arch/loongarch/include/asm/unwind.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
Expand All @@ -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 */
2 changes: 1 addition & 1 deletion arch/loongarch/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions arch/loongarch/kernel/alternative.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion arch/loongarch/kernel/cpu-probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions arch/loongarch/kernel/genex.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
45 changes: 7 additions & 38 deletions arch/loongarch/kernel/inst.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,47 +58,29 @@ 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) {
pr_warn("The generated b instruction is out of range.\n");
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;
}

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) {
pr_warn("The generated bl instruction is out of range.\n");
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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
12 changes: 3 additions & 9 deletions arch/loongarch/kernel/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 0 additions & 3 deletions arch/loongarch/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
32 changes: 32 additions & 0 deletions arch/loongarch/kernel/unwind.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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;
}
Loading

0 comments on commit 84bd7e0

Please sign in to comment.