mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-13 20:15:20 -05:00
Merge tag 'loongarch-fixes-6.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
Pull LoongArch fixes from Huacai Chen: "Complete CPUCFG registers definition, set correct protection_map[] for VM_NONE/VM_SHARED, fix some bugs in the orc stack unwinder, ftrace and BPF JIT" * tag 'loongarch-fixes-6.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson: samples/ftrace: Adjust LoongArch register restore order in direct calls LoongArch: BPF: Enhance the bpf_arch_text_poke() function LoongArch: BPF: Enable trampoline-based tracing for module functions LoongArch: BPF: Adjust the jump offset of tail calls LoongArch: BPF: Save return address register ra to t0 before trampoline LoongArch: BPF: Zero-extend bpf_tail_call() index LoongArch: BPF: Sign extend kfunc call arguments LoongArch: Refactor register restoration in ftrace_common_return LoongArch: Enable exception fixup for specific ADE subcode LoongArch: Remove unnecessary checks for ORC unwinder LoongArch: Remove is_entry_func() and kernel_entry_end LoongArch: Use UNWIND_HINT_END_OF_STACK for entry points LoongArch: Set correct protection_map[] for VM_NONE/VM_SHARED LoongArch: Complete CPUCFG registers definition
This commit is contained in:
@@ -94,6 +94,12 @@
|
||||
#define CPUCFG2_LSPW BIT(21)
|
||||
#define CPUCFG2_LAM BIT(22)
|
||||
#define CPUCFG2_PTW BIT(24)
|
||||
#define CPUCFG2_FRECIPE BIT(25)
|
||||
#define CPUCFG2_DIV32 BIT(26)
|
||||
#define CPUCFG2_LAM_BH BIT(27)
|
||||
#define CPUCFG2_LAMCAS BIT(28)
|
||||
#define CPUCFG2_LLACQ_SCREL BIT(29)
|
||||
#define CPUCFG2_SCQ BIT(30)
|
||||
|
||||
#define LOONGARCH_CPUCFG3 0x3
|
||||
#define CPUCFG3_CCDMA BIT(0)
|
||||
@@ -108,6 +114,7 @@
|
||||
#define CPUCFG3_SPW_HG_HF BIT(11)
|
||||
#define CPUCFG3_RVA BIT(12)
|
||||
#define CPUCFG3_RVAMAX GENMASK(16, 13)
|
||||
#define CPUCFG3_DBAR_HINTS BIT(17)
|
||||
#define CPUCFG3_ALDORDER_CAP BIT(18) /* All address load ordered, capability */
|
||||
#define CPUCFG3_ASTORDER_CAP BIT(19) /* All address store ordered, capability */
|
||||
#define CPUCFG3_ALDORDER_STA BIT(20) /* All address load ordered, status */
|
||||
|
||||
@@ -42,6 +42,7 @@ SYM_DATA(kernel_fsize, .long _kernel_fsize);
|
||||
.align 12
|
||||
|
||||
SYM_CODE_START(kernel_entry) # kernel entry point
|
||||
UNWIND_HINT_END_OF_STACK
|
||||
|
||||
SETUP_TWINS
|
||||
SETUP_MODES t0
|
||||
@@ -113,6 +114,7 @@ SYM_CODE_END(kernel_entry)
|
||||
* function after setting up the stack and tp registers.
|
||||
*/
|
||||
SYM_CODE_START(smpboot_entry)
|
||||
UNWIND_HINT_END_OF_STACK
|
||||
|
||||
SETUP_TWINS
|
||||
SETUP_MODES t0
|
||||
@@ -142,5 +144,3 @@ SYM_CODE_START(smpboot_entry)
|
||||
SYM_CODE_END(smpboot_entry)
|
||||
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
SYM_ENTRY(kernel_entry_end, SYM_L_GLOBAL, SYM_A_NONE)
|
||||
|
||||
@@ -94,7 +94,6 @@ SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL)
|
||||
* at the callsite, so there is no need to restore the T series regs.
|
||||
*/
|
||||
ftrace_common_return:
|
||||
PTR_L ra, sp, PT_R1
|
||||
PTR_L a0, sp, PT_R4
|
||||
PTR_L a1, sp, PT_R5
|
||||
PTR_L a2, sp, PT_R6
|
||||
@@ -104,12 +103,17 @@ ftrace_common_return:
|
||||
PTR_L a6, sp, PT_R10
|
||||
PTR_L a7, sp, PT_R11
|
||||
PTR_L fp, sp, PT_R22
|
||||
PTR_L t0, sp, PT_ERA
|
||||
PTR_L t1, sp, PT_R13
|
||||
PTR_ADDI sp, sp, PT_SIZE
|
||||
bnez t1, .Ldirect
|
||||
|
||||
PTR_L ra, sp, PT_R1
|
||||
PTR_L t0, sp, PT_ERA
|
||||
PTR_ADDI sp, sp, PT_SIZE
|
||||
jr t0
|
||||
.Ldirect:
|
||||
PTR_L t0, sp, PT_R1
|
||||
PTR_L ra, sp, PT_ERA
|
||||
PTR_ADDI sp, sp, PT_SIZE
|
||||
jr t1
|
||||
SYM_CODE_END(ftrace_common)
|
||||
|
||||
@@ -161,6 +165,8 @@ SYM_CODE_END(return_to_handler)
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
|
||||
SYM_CODE_START(ftrace_stub_direct_tramp)
|
||||
UNWIND_HINT_UNDEFINED
|
||||
jr t0
|
||||
move t1, ra
|
||||
move ra, t0
|
||||
jr t1
|
||||
SYM_CODE_END(ftrace_stub_direct_tramp)
|
||||
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
|
||||
|
||||
@@ -535,10 +535,15 @@ asmlinkage void noinstr do_fpe(struct pt_regs *regs, unsigned long fcsr)
|
||||
asmlinkage void noinstr do_ade(struct pt_regs *regs)
|
||||
{
|
||||
irqentry_state_t state = irqentry_enter(regs);
|
||||
unsigned int esubcode = FIELD_GET(CSR_ESTAT_ESUBCODE, regs->csr_estat);
|
||||
|
||||
if ((esubcode == EXSUBCODE_ADEM) && fixup_exception(regs))
|
||||
goto out;
|
||||
|
||||
die_if_kernel("Kernel ade access", regs);
|
||||
force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)regs->csr_badvaddr);
|
||||
|
||||
out:
|
||||
irqentry_exit(regs, state);
|
||||
}
|
||||
|
||||
|
||||
@@ -348,24 +348,10 @@ void unwind_start(struct unwind_state *state, struct task_struct *task,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unwind_start);
|
||||
|
||||
static bool is_entry_func(unsigned long addr)
|
||||
{
|
||||
extern u32 kernel_entry;
|
||||
extern u32 kernel_entry_end;
|
||||
|
||||
return addr >= (unsigned long)&kernel_entry && addr < (unsigned long)&kernel_entry_end;
|
||||
}
|
||||
|
||||
static inline unsigned long bt_address(unsigned long ra)
|
||||
{
|
||||
extern unsigned long eentry;
|
||||
|
||||
if (__kernel_text_address(ra))
|
||||
return ra;
|
||||
|
||||
if (__module_text_address(ra))
|
||||
return ra;
|
||||
|
||||
if (ra >= eentry && ra < eentry + EXCCODE_INT_END * VECSIZE) {
|
||||
unsigned long func;
|
||||
unsigned long type = (ra - eentry) / VECSIZE;
|
||||
@@ -383,10 +369,13 @@ static inline unsigned long bt_address(unsigned long ra)
|
||||
break;
|
||||
}
|
||||
|
||||
return func + offset;
|
||||
ra = func + offset;
|
||||
}
|
||||
|
||||
return ra;
|
||||
if (__kernel_text_address(ra))
|
||||
return ra;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool unwind_next_frame(struct unwind_state *state)
|
||||
@@ -402,9 +391,6 @@ bool unwind_next_frame(struct unwind_state *state)
|
||||
/* Don't let modules unload while we're reading their ORC data. */
|
||||
guard(rcu)();
|
||||
|
||||
if (is_entry_func(state->pc))
|
||||
goto end;
|
||||
|
||||
orc = orc_find(state->pc);
|
||||
if (!orc) {
|
||||
/*
|
||||
@@ -512,9 +498,6 @@ bool unwind_next_frame(struct unwind_state *state)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!__kernel_text_address(state->pc))
|
||||
goto err;
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
|
||||
@@ -160,8 +160,8 @@ void cpu_cache_init(void)
|
||||
|
||||
static const pgprot_t protection_map[16] = {
|
||||
[VM_NONE] = __pgprot(_CACHE_CC | _PAGE_USER |
|
||||
_PAGE_PROTNONE | _PAGE_NO_EXEC |
|
||||
_PAGE_NO_READ),
|
||||
_PAGE_NO_EXEC | _PAGE_NO_READ |
|
||||
(_PAGE_PROTNONE ? : _PAGE_PRESENT)),
|
||||
[VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID |
|
||||
_PAGE_USER | _PAGE_PRESENT |
|
||||
_PAGE_NO_EXEC),
|
||||
@@ -180,8 +180,8 @@ static const pgprot_t protection_map[16] = {
|
||||
[VM_EXEC | VM_WRITE | VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID |
|
||||
_PAGE_USER | _PAGE_PRESENT),
|
||||
[VM_SHARED] = __pgprot(_CACHE_CC | _PAGE_USER |
|
||||
_PAGE_PROTNONE | _PAGE_NO_EXEC |
|
||||
_PAGE_NO_READ),
|
||||
_PAGE_NO_EXEC | _PAGE_NO_READ |
|
||||
(_PAGE_PROTNONE ? : _PAGE_PRESENT)),
|
||||
[VM_SHARED | VM_READ] = __pgprot(_CACHE_CC | _PAGE_VALID |
|
||||
_PAGE_USER | _PAGE_PRESENT |
|
||||
_PAGE_NO_EXEC),
|
||||
|
||||
@@ -139,6 +139,7 @@ static void build_prologue(struct jit_ctx *ctx)
|
||||
stack_adjust = round_up(stack_adjust, 16);
|
||||
stack_adjust += bpf_stack_adjust;
|
||||
|
||||
move_reg(ctx, LOONGARCH_GPR_T0, LOONGARCH_GPR_RA);
|
||||
/* Reserve space for the move_imm + jirl instruction */
|
||||
for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++)
|
||||
emit_insn(ctx, nop);
|
||||
@@ -238,7 +239,7 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call)
|
||||
* Call the next bpf prog and skip the first instruction
|
||||
* of TCC initialization.
|
||||
*/
|
||||
emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T3, 6);
|
||||
emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T3, 7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,6 +281,8 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)
|
||||
* goto out;
|
||||
*/
|
||||
tc_ninsn = insn ? ctx->offset[insn+1] - ctx->offset[insn] : ctx->offset[0];
|
||||
emit_zext_32(ctx, a2, true);
|
||||
|
||||
off = offsetof(struct bpf_array, map.max_entries);
|
||||
emit_insn(ctx, ldwu, t1, a1, off);
|
||||
/* bgeu $a2, $t1, jmp_offset */
|
||||
@@ -950,6 +953,22 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext
|
||||
emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off);
|
||||
}
|
||||
|
||||
if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
|
||||
const struct btf_func_model *m;
|
||||
int i;
|
||||
|
||||
m = bpf_jit_find_kfunc_model(ctx->prog, insn);
|
||||
if (!m)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < m->nr_args; i++) {
|
||||
u8 reg = regmap[BPF_REG_1 + i];
|
||||
bool sign = m->arg_flags[i] & BTF_FMODEL_SIGNED_ARG;
|
||||
|
||||
emit_abi_ext(ctx, reg, m->arg_size[i], sign);
|
||||
}
|
||||
}
|
||||
|
||||
move_addr(ctx, t1, func_addr);
|
||||
emit_insn(ctx, jirl, LOONGARCH_GPR_RA, t1, 0);
|
||||
|
||||
@@ -1265,7 +1284,7 @@ static int emit_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_T0 : LOONGARCH_GPR_ZERO, (u64)target);
|
||||
return emit_jump_and_link(&ctx, is_call ? LOONGARCH_GPR_RA : LOONGARCH_GPR_ZERO, (u64)target);
|
||||
}
|
||||
|
||||
static int emit_call(struct jit_ctx *ctx, u64 addr)
|
||||
@@ -1290,15 +1309,30 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
|
||||
{
|
||||
int ret;
|
||||
bool is_call;
|
||||
unsigned long size = 0;
|
||||
unsigned long offset = 0;
|
||||
void *image = NULL;
|
||||
char namebuf[KSYM_NAME_LEN];
|
||||
u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP};
|
||||
u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP};
|
||||
|
||||
/* Only poking bpf text is supported. Since kernel function entry
|
||||
* is set up by ftrace, we rely on ftrace to poke kernel functions.
|
||||
*/
|
||||
if (!is_bpf_text_address((unsigned long)ip))
|
||||
if (!__bpf_address_lookup((unsigned long)ip, &size, &offset, namebuf))
|
||||
return -ENOTSUPP;
|
||||
|
||||
image = ip - offset;
|
||||
|
||||
/* zero offset means we're poking bpf prog entry */
|
||||
if (offset == 0) {
|
||||
/* skip to the nop instruction in bpf prog entry:
|
||||
* move t0, ra
|
||||
* nop
|
||||
*/
|
||||
ip = image + LOONGARCH_INSN_SIZE;
|
||||
}
|
||||
|
||||
is_call = old_t == BPF_MOD_CALL;
|
||||
ret = emit_jump_or_nops(old_addr, ip, old_insns, is_call);
|
||||
if (ret)
|
||||
@@ -1622,14 +1656,12 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i
|
||||
|
||||
/* To traced function */
|
||||
/* Ftrace jump skips 2 NOP instructions */
|
||||
if (is_kernel_text((unsigned long)orig_call))
|
||||
if (is_kernel_text((unsigned long)orig_call) ||
|
||||
is_module_text_address((unsigned long)orig_call))
|
||||
orig_call += LOONGARCH_FENTRY_NBYTES;
|
||||
/* Direct jump skips 5 NOP instructions */
|
||||
else if (is_bpf_text_address((unsigned long)orig_call))
|
||||
orig_call += LOONGARCH_BPF_FENTRY_NBYTES;
|
||||
/* Module tracing not supported - cause kernel lockups */
|
||||
else if (is_module_text_address((unsigned long)orig_call))
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (flags & BPF_TRAMP_F_CALL_ORIG) {
|
||||
move_addr(ctx, LOONGARCH_GPR_A0, (const u64)im);
|
||||
@@ -1722,12 +1754,16 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i
|
||||
emit_insn(ctx, ldd, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, 0);
|
||||
emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, 16);
|
||||
|
||||
if (flags & BPF_TRAMP_F_SKIP_FRAME)
|
||||
if (flags & BPF_TRAMP_F_SKIP_FRAME) {
|
||||
/* return to parent function */
|
||||
emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_RA, 0);
|
||||
else
|
||||
/* return to traced function */
|
||||
move_reg(ctx, LOONGARCH_GPR_RA, LOONGARCH_GPR_T0);
|
||||
emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T0, 0);
|
||||
} else {
|
||||
/* return to traced function */
|
||||
move_reg(ctx, LOONGARCH_GPR_T1, LOONGARCH_GPR_RA);
|
||||
move_reg(ctx, LOONGARCH_GPR_RA, LOONGARCH_GPR_T0);
|
||||
emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ret = ctx->idx;
|
||||
|
||||
@@ -88,6 +88,32 @@ static inline void emit_sext_32(struct jit_ctx *ctx, enum loongarch_gpr reg, boo
|
||||
emit_insn(ctx, addiw, reg, reg, 0);
|
||||
}
|
||||
|
||||
/* Emit proper extension according to ABI requirements.
|
||||
* Note that it requires a value of size `size` already resides in register `reg`.
|
||||
*/
|
||||
static inline void emit_abi_ext(struct jit_ctx *ctx, int reg, u8 size, bool sign)
|
||||
{
|
||||
/* ABI requires unsigned char/short to be zero-extended */
|
||||
if (!sign && (size == 1 || size == 2))
|
||||
return;
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
emit_insn(ctx, extwb, reg, reg);
|
||||
break;
|
||||
case 2:
|
||||
emit_insn(ctx, extwh, reg, reg);
|
||||
break;
|
||||
case 4:
|
||||
emit_insn(ctx, addiw, reg, reg, 0);
|
||||
break;
|
||||
case 8:
|
||||
break;
|
||||
default:
|
||||
pr_warn("bpf_jit: invalid size %d for extension\n", size);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void move_addr(struct jit_ctx *ctx, enum loongarch_gpr rd, u64 addr)
|
||||
{
|
||||
u64 imm_11_0, imm_31_12, imm_51_32, imm_63_52;
|
||||
|
||||
@@ -176,8 +176,8 @@ asm (
|
||||
" st.d $t0, $sp, 0\n"
|
||||
" st.d $ra, $sp, 8\n"
|
||||
" bl my_direct_func1\n"
|
||||
" ld.d $t0, $sp, 0\n"
|
||||
" ld.d $ra, $sp, 8\n"
|
||||
" ld.d $ra, $sp, 0\n"
|
||||
" ld.d $t0, $sp, 8\n"
|
||||
" addi.d $sp, $sp, 16\n"
|
||||
" jr $t0\n"
|
||||
" .size my_tramp1, .-my_tramp1\n"
|
||||
@@ -189,8 +189,8 @@ asm (
|
||||
" st.d $t0, $sp, 0\n"
|
||||
" st.d $ra, $sp, 8\n"
|
||||
" bl my_direct_func2\n"
|
||||
" ld.d $t0, $sp, 0\n"
|
||||
" ld.d $ra, $sp, 8\n"
|
||||
" ld.d $ra, $sp, 0\n"
|
||||
" ld.d $t0, $sp, 8\n"
|
||||
" addi.d $sp, $sp, 16\n"
|
||||
" jr $t0\n"
|
||||
" .size my_tramp2, .-my_tramp2\n"
|
||||
|
||||
@@ -199,8 +199,8 @@ asm (
|
||||
" move $a0, $t0\n"
|
||||
" bl my_direct_func1\n"
|
||||
" ld.d $a0, $sp, 0\n"
|
||||
" ld.d $t0, $sp, 8\n"
|
||||
" ld.d $ra, $sp, 16\n"
|
||||
" ld.d $ra, $sp, 8\n"
|
||||
" ld.d $t0, $sp, 16\n"
|
||||
" addi.d $sp, $sp, 32\n"
|
||||
" jr $t0\n"
|
||||
" .size my_tramp1, .-my_tramp1\n"
|
||||
@@ -215,8 +215,8 @@ asm (
|
||||
" move $a0, $t0\n"
|
||||
" bl my_direct_func2\n"
|
||||
" ld.d $a0, $sp, 0\n"
|
||||
" ld.d $t0, $sp, 8\n"
|
||||
" ld.d $ra, $sp, 16\n"
|
||||
" ld.d $ra, $sp, 8\n"
|
||||
" ld.d $t0, $sp, 16\n"
|
||||
" addi.d $sp, $sp, 32\n"
|
||||
" jr $t0\n"
|
||||
" .size my_tramp2, .-my_tramp2\n"
|
||||
|
||||
@@ -131,8 +131,8 @@ asm (
|
||||
" move $a0, $t0\n"
|
||||
" bl my_direct_func\n"
|
||||
" ld.d $a0, $sp, 0\n"
|
||||
" ld.d $t0, $sp, 8\n"
|
||||
" ld.d $ra, $sp, 16\n"
|
||||
" ld.d $ra, $sp, 8\n"
|
||||
" ld.d $t0, $sp, 16\n"
|
||||
" addi.d $sp, $sp, 32\n"
|
||||
" jr $t0\n"
|
||||
" .size my_tramp, .-my_tramp\n"
|
||||
|
||||
@@ -143,8 +143,8 @@ asm (
|
||||
" ld.d $a0, $sp, 0\n"
|
||||
" ld.d $a1, $sp, 8\n"
|
||||
" ld.d $a2, $sp, 16\n"
|
||||
" ld.d $t0, $sp, 24\n"
|
||||
" ld.d $ra, $sp, 32\n"
|
||||
" ld.d $ra, $sp, 24\n"
|
||||
" ld.d $t0, $sp, 32\n"
|
||||
" addi.d $sp, $sp, 48\n"
|
||||
" jr $t0\n"
|
||||
" .size my_tramp, .-my_tramp\n"
|
||||
|
||||
@@ -124,8 +124,8 @@ asm (
|
||||
" st.d $ra, $sp, 16\n"
|
||||
" bl my_direct_func\n"
|
||||
" ld.d $a0, $sp, 0\n"
|
||||
" ld.d $t0, $sp, 8\n"
|
||||
" ld.d $ra, $sp, 16\n"
|
||||
" ld.d $ra, $sp, 8\n"
|
||||
" ld.d $t0, $sp, 16\n"
|
||||
" addi.d $sp, $sp, 32\n"
|
||||
" jr $t0\n"
|
||||
" .size my_tramp, .-my_tramp\n"
|
||||
|
||||
Reference in New Issue
Block a user