From eca6a5479129b02c356c71a4f9230613be0ccb62 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 11 Mar 2024 22:23:46 +0800 Subject: [PATCH 01/23] objtool/LoongArch: Enable objtool to be built commit e8aff71ca93026209dd0eab9b285e6808cd87d05 upstream. Add the minimal changes to enable objtool build on LoongArch, most of the functions are stubs to only fix the build errors when make -C tools/objtool. This is similar with commit e52ec98c5ab1 ("objtool/powerpc: Enable objtool to be built on ppc"). Co-developed-by: Jinyang He Signed-off-by: Jinyang He Co-developed-by: Youling Tang Signed-off-by: Youling Tang Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- tools/objtool/arch/loongarch/Build | 2 + tools/objtool/arch/loongarch/decode.c | 71 +++++++++++++++++++ .../arch/loongarch/include/arch/cfi_regs.h | 22 ++++++ .../objtool/arch/loongarch/include/arch/elf.h | 30 ++++++++ .../arch/loongarch/include/arch/special.h | 33 +++++++++ tools/objtool/arch/loongarch/special.c | 15 ++++ 6 files changed, 173 insertions(+) create mode 100644 tools/objtool/arch/loongarch/Build create mode 100644 tools/objtool/arch/loongarch/decode.c create mode 100644 tools/objtool/arch/loongarch/include/arch/cfi_regs.h create mode 100644 tools/objtool/arch/loongarch/include/arch/elf.h create mode 100644 tools/objtool/arch/loongarch/include/arch/special.h create mode 100644 tools/objtool/arch/loongarch/special.c diff --git a/tools/objtool/arch/loongarch/Build b/tools/objtool/arch/loongarch/Build new file mode 100644 index 000000000000..d24d5636a5b8 --- /dev/null +++ b/tools/objtool/arch/loongarch/Build @@ -0,0 +1,2 @@ +objtool-y += decode.o +objtool-y += special.o diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c new file mode 100644 index 000000000000..cc74ba4e0f54 --- /dev/null +++ b/tools/objtool/arch/loongarch/decode.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + +int arch_ftrace_match(char *name) +{ + return !strcmp(name, "_mcount"); +} + +unsigned long arch_jump_destination(struct instruction *insn) +{ + return insn->offset + (insn->immediate << 2); +} + +unsigned long arch_dest_reloc_offset(int addend) +{ + return addend; +} + +bool arch_pc_relative_reloc(struct reloc *reloc) +{ + return false; +} + +bool arch_callee_saved_reg(unsigned char reg) +{ + switch (reg) { + case CFI_RA: + case CFI_FP: + case CFI_S0 ... CFI_S8: + return true; + default: + return false; + } +} + +int arch_decode_hint_reg(u8 sp_reg, int *base) +{ + return 0; +} + +int arch_decode_instruction(struct objtool_file *file, const struct section *sec, + unsigned long offset, unsigned int maxlen, + struct instruction *insn) +{ + return 0; +} + +const char *arch_nop_insn(int len) +{ + return NULL; +} + +const char *arch_ret_insn(int len) +{ + return NULL; +} + +void arch_initial_func_cfi_state(struct cfi_init_state *state) +{ + int i; + + for (i = 0; i < CFI_NUM_REGS; i++) { + state->regs[i].base = CFI_UNDEFINED; + state->regs[i].offset = 0; + } + + /* initial CFA (call frame address) */ + state->cfa.base = CFI_SP; + state->cfa.offset = 0; +} diff --git a/tools/objtool/arch/loongarch/include/arch/cfi_regs.h b/tools/objtool/arch/loongarch/include/arch/cfi_regs.h new file mode 100644 index 000000000000..d183cc8f43bf --- /dev/null +++ b/tools/objtool/arch/loongarch/include/arch/cfi_regs.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _OBJTOOL_ARCH_CFI_REGS_H +#define _OBJTOOL_ARCH_CFI_REGS_H + +#define CFI_RA 1 +#define CFI_SP 3 +#define CFI_A0 4 +#define CFI_FP 22 +#define CFI_S0 23 +#define CFI_S1 24 +#define CFI_S2 25 +#define CFI_S3 26 +#define CFI_S4 27 +#define CFI_S5 28 +#define CFI_S6 29 +#define CFI_S7 30 +#define CFI_S8 31 +#define CFI_NUM_REGS 32 + +#define CFI_BP CFI_FP + +#endif /* _OBJTOOL_ARCH_CFI_REGS_H */ diff --git a/tools/objtool/arch/loongarch/include/arch/elf.h b/tools/objtool/arch/loongarch/include/arch/elf.h new file mode 100644 index 000000000000..9623d663220e --- /dev/null +++ b/tools/objtool/arch/loongarch/include/arch/elf.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _OBJTOOL_ARCH_ELF_H +#define _OBJTOOL_ARCH_ELF_H + +/* + * See the following link for more info about ELF Relocation types: + * https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html#_relocations + */ +#ifndef R_LARCH_NONE +#define R_LARCH_NONE 0 +#endif +#ifndef R_LARCH_32 +#define R_LARCH_32 1 +#endif +#ifndef R_LARCH_64 +#define R_LARCH_64 2 +#endif +#ifndef R_LARCH_32_PCREL +#define R_LARCH_32_PCREL 99 +#endif + +#define R_NONE R_LARCH_NONE +#define R_ABS32 R_LARCH_32 +#define R_ABS64 R_LARCH_64 +#define R_DATA32 R_LARCH_32_PCREL +#define R_DATA64 R_LARCH_32_PCREL +#define R_TEXT32 R_LARCH_32_PCREL +#define R_TEXT64 R_LARCH_32_PCREL + +#endif /* _OBJTOOL_ARCH_ELF_H */ diff --git a/tools/objtool/arch/loongarch/include/arch/special.h b/tools/objtool/arch/loongarch/include/arch/special.h new file mode 100644 index 000000000000..35fc979b550a --- /dev/null +++ b/tools/objtool/arch/loongarch/include/arch/special.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _OBJTOOL_ARCH_SPECIAL_H +#define _OBJTOOL_ARCH_SPECIAL_H + +/* + * See more info about struct exception_table_entry + * in arch/loongarch/include/asm/extable.h + */ +#define EX_ENTRY_SIZE 12 +#define EX_ORIG_OFFSET 0 +#define EX_NEW_OFFSET 4 + +/* + * See more info about struct jump_entry + * in include/linux/jump_label.h + */ +#define JUMP_ENTRY_SIZE 16 +#define JUMP_ORIG_OFFSET 0 +#define JUMP_NEW_OFFSET 4 +#define JUMP_KEY_OFFSET 8 + +/* + * See more info about struct alt_instr + * in arch/loongarch/include/asm/alternative.h + */ +#define ALT_ENTRY_SIZE 12 +#define ALT_ORIG_OFFSET 0 +#define ALT_NEW_OFFSET 4 +#define ALT_FEATURE_OFFSET 8 +#define ALT_ORIG_LEN_OFFSET 10 +#define ALT_NEW_LEN_OFFSET 11 + +#endif /* _OBJTOOL_ARCH_SPECIAL_H */ diff --git a/tools/objtool/arch/loongarch/special.c b/tools/objtool/arch/loongarch/special.c new file mode 100644 index 000000000000..9bba1e9318e0 --- /dev/null +++ b/tools/objtool/arch/loongarch/special.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include + +bool arch_support_alt_relocation(struct special_alt *special_alt, + struct instruction *insn, + struct reloc *reloc) +{ + return false; +} + +struct reloc *arch_find_switch_table(struct objtool_file *file, + struct instruction *insn) +{ + return NULL; +} From fd40028d8bd5b6de16030150e7e76ebc5ba37373 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 11 Mar 2024 22:23:47 +0800 Subject: [PATCH 02/23] objtool/LoongArch: Implement instruction decoder commit b2d23158e6c881326321c2351b92568be4e57030 upstream. Only copy the minimal definitions of instruction opcodes and formats in inst.h from arch/loongarch to tools/arch/loongarch, and also copy the definition of sign_extend64() to tools/include/linux/bitops.h to decode the following kinds of instructions: (1) stack pointer related instructions addi.d, ld.d, st.d, ldptr.d and stptr.d (2) branch and jump related instructions beq, bne, blt, bge, bltu, bgeu, beqz, bnez, bceqz, bcnez, b, bl and jirl (3) other instructions break, nop and ertn See more info about instructions in LoongArch Reference Manual: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html Co-developed-by: Jinyang He Signed-off-by: Jinyang He Co-developed-by: Youling Tang Signed-off-by: Youling Tang Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- tools/arch/loongarch/include/asm/inst.h | 161 ++++++++++++++ tools/include/linux/bitops.h | 11 + tools/objtool/arch/loongarch/decode.c | 273 +++++++++++++++++++++++- 3 files changed, 443 insertions(+), 2 deletions(-) create mode 100644 tools/arch/loongarch/include/asm/inst.h diff --git a/tools/arch/loongarch/include/asm/inst.h b/tools/arch/loongarch/include/asm/inst.h new file mode 100644 index 000000000000..c25b5853181d --- /dev/null +++ b/tools/arch/loongarch/include/asm/inst.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ +#ifndef _ASM_INST_H +#define _ASM_INST_H + +#include + +#define LOONGARCH_INSN_NOP 0x03400000 + +enum reg0i15_op { + break_op = 0x54, +}; + +enum reg0i26_op { + b_op = 0x14, + bl_op = 0x15, +}; + +enum reg1i21_op { + beqz_op = 0x10, + bnez_op = 0x11, + bceqz_op = 0x12, /* bits[9:8] = 0x00 */ + bcnez_op = 0x12, /* bits[9:8] = 0x01 */ +}; + +enum reg2_op { + ertn_op = 0x1920e, +}; + +enum reg2i12_op { + addid_op = 0x0b, + andi_op = 0x0d, + ldd_op = 0xa3, + std_op = 0xa7, +}; + +enum reg2i14_op { + ldptrd_op = 0x26, + stptrd_op = 0x27, +}; + +enum reg2i16_op { + jirl_op = 0x13, + beq_op = 0x16, + bne_op = 0x17, + blt_op = 0x18, + bge_op = 0x19, + bltu_op = 0x1a, + bgeu_op = 0x1b, +}; + +struct reg0i15_format { + unsigned int immediate : 15; + unsigned int opcode : 17; +}; + +struct reg0i26_format { + unsigned int immediate_h : 10; + unsigned int immediate_l : 16; + unsigned int opcode : 6; +}; + +struct reg1i21_format { + unsigned int immediate_h : 5; + unsigned int rj : 5; + unsigned int immediate_l : 16; + unsigned int opcode : 6; +}; + +struct reg2_format { + unsigned int rd : 5; + unsigned int rj : 5; + unsigned int opcode : 22; +}; + +struct reg2i12_format { + unsigned int rd : 5; + unsigned int rj : 5; + unsigned int immediate : 12; + unsigned int opcode : 10; +}; + +struct reg2i14_format { + unsigned int rd : 5; + unsigned int rj : 5; + unsigned int immediate : 14; + unsigned int opcode : 8; +}; + +struct reg2i16_format { + unsigned int rd : 5; + unsigned int rj : 5; + unsigned int immediate : 16; + unsigned int opcode : 6; +}; + +union loongarch_instruction { + unsigned int word; + struct reg0i15_format reg0i15_format; + struct reg0i26_format reg0i26_format; + struct reg1i21_format reg1i21_format; + struct reg2_format reg2_format; + struct reg2i12_format reg2i12_format; + struct reg2i14_format reg2i14_format; + struct reg2i16_format reg2i16_format; +}; + +#define LOONGARCH_INSN_SIZE sizeof(union loongarch_instruction) + +enum loongarch_gpr { + LOONGARCH_GPR_ZERO = 0, + LOONGARCH_GPR_RA = 1, + LOONGARCH_GPR_TP = 2, + LOONGARCH_GPR_SP = 3, + LOONGARCH_GPR_A0 = 4, /* Reused as V0 for return value */ + LOONGARCH_GPR_A1, /* Reused as V1 for return value */ + LOONGARCH_GPR_A2, + LOONGARCH_GPR_A3, + LOONGARCH_GPR_A4, + LOONGARCH_GPR_A5, + LOONGARCH_GPR_A6, + LOONGARCH_GPR_A7, + LOONGARCH_GPR_T0 = 12, + LOONGARCH_GPR_T1, + LOONGARCH_GPR_T2, + LOONGARCH_GPR_T3, + LOONGARCH_GPR_T4, + LOONGARCH_GPR_T5, + LOONGARCH_GPR_T6, + LOONGARCH_GPR_T7, + LOONGARCH_GPR_T8, + LOONGARCH_GPR_FP = 22, + LOONGARCH_GPR_S0 = 23, + LOONGARCH_GPR_S1, + LOONGARCH_GPR_S2, + LOONGARCH_GPR_S3, + LOONGARCH_GPR_S4, + LOONGARCH_GPR_S5, + LOONGARCH_GPR_S6, + LOONGARCH_GPR_S7, + LOONGARCH_GPR_S8, + LOONGARCH_GPR_MAX +}; + +#define DEF_EMIT_REG2I16_FORMAT(NAME, OP) \ +static inline void emit_##NAME(union loongarch_instruction *insn, \ + enum loongarch_gpr rj, \ + enum loongarch_gpr rd, \ + int offset) \ +{ \ + insn->reg2i16_format.opcode = OP; \ + insn->reg2i16_format.immediate = offset; \ + insn->reg2i16_format.rj = rj; \ + insn->reg2i16_format.rd = rd; \ +} + +DEF_EMIT_REG2I16_FORMAT(jirl, jirl_op) + +#endif /* _ASM_INST_H */ diff --git a/tools/include/linux/bitops.h b/tools/include/linux/bitops.h index f18683b95ea6..7319f6ced108 100644 --- a/tools/include/linux/bitops.h +++ b/tools/include/linux/bitops.h @@ -87,4 +87,15 @@ static inline __u32 rol32(__u32 word, unsigned int shift) return (word << shift) | (word >> ((-shift) & 31)); } +/** + * sign_extend64 - sign extend a 64-bit value using specified bit as sign-bit + * @value: value to sign extend + * @index: 0 based bit index (0<=index<64) to sign bit + */ +static __always_inline __s64 sign_extend64(__u64 value, int index) +{ + __u8 shift = 63 - index; + return (__s64)(value << shift) >> shift; +} + #endif diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c index cc74ba4e0f54..ff0b53144d12 100644 --- a/tools/objtool/arch/loongarch/decode.c +++ b/tools/objtool/arch/loongarch/decode.c @@ -1,6 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include #include +#include +#include + +#ifndef EM_LOONGARCH +#define EM_LOONGARCH 258 +#endif int arch_ftrace_match(char *name) { @@ -39,21 +45,284 @@ int arch_decode_hint_reg(u8 sp_reg, int *base) return 0; } +static bool is_loongarch(const struct elf *elf) +{ + if (elf->ehdr.e_machine == EM_LOONGARCH) + return true; + + WARN("unexpected ELF machine type %d", elf->ehdr.e_machine); + return false; +} + +#define ADD_OP(op) \ + if (!(op = calloc(1, sizeof(*op)))) \ + return -1; \ + else for (*ops_list = op, ops_list = &op->next; op; op = NULL) + +static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst, + struct instruction *insn) +{ + switch (inst.reg0i26_format.opcode) { + case b_op: + insn->type = INSN_JUMP_UNCONDITIONAL; + insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 | + inst.reg0i26_format.immediate_l, 25); + break; + case bl_op: + insn->type = INSN_CALL; + insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 | + inst.reg0i26_format.immediate_l, 25); + break; + default: + return false; + } + + return true; +} + +static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst, + struct instruction *insn) +{ + switch (inst.reg1i21_format.opcode) { + case beqz_op: + case bnez_op: + case bceqz_op: + insn->type = INSN_JUMP_CONDITIONAL; + insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 | + inst.reg1i21_format.immediate_l, 20); + break; + default: + return false; + } + + return true; +} + +static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst, + struct instruction *insn, + struct stack_op **ops_list, + struct stack_op *op) +{ + switch (inst.reg2i12_format.opcode) { + case addid_op: + if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) { + /* addi.d sp,sp,si12 or addi.d fp,sp,si12 */ + insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); + ADD_OP(op) { + op->src.type = OP_SRC_ADD; + op->src.reg = inst.reg2i12_format.rj; + op->src.offset = insn->immediate; + op->dest.type = OP_DEST_REG; + op->dest.reg = inst.reg2i12_format.rd; + } + } + break; + case ldd_op: + if (inst.reg2i12_format.rj == CFI_SP) { + /* ld.d rd,sp,si12 */ + insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); + ADD_OP(op) { + op->src.type = OP_SRC_REG_INDIRECT; + op->src.reg = CFI_SP; + op->src.offset = insn->immediate; + op->dest.type = OP_DEST_REG; + op->dest.reg = inst.reg2i12_format.rd; + } + } + break; + case std_op: + if (inst.reg2i12_format.rj == CFI_SP) { + /* st.d rd,sp,si12 */ + insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); + ADD_OP(op) { + op->src.type = OP_SRC_REG; + op->src.reg = inst.reg2i12_format.rd; + op->dest.type = OP_DEST_REG_INDIRECT; + op->dest.reg = CFI_SP; + op->dest.offset = insn->immediate; + } + } + break; + case andi_op: + if (inst.reg2i12_format.rd == 0 && + inst.reg2i12_format.rj == 0 && + inst.reg2i12_format.immediate == 0) + /* andi r0,r0,0 */ + insn->type = INSN_NOP; + break; + default: + return false; + } + + return true; +} + +static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst, + struct instruction *insn, + struct stack_op **ops_list, + struct stack_op *op) +{ + switch (inst.reg2i14_format.opcode) { + case ldptrd_op: + if (inst.reg2i14_format.rj == CFI_SP) { + /* ldptr.d rd,sp,si14 */ + insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13); + ADD_OP(op) { + op->src.type = OP_SRC_REG_INDIRECT; + op->src.reg = CFI_SP; + op->src.offset = insn->immediate; + op->dest.type = OP_DEST_REG; + op->dest.reg = inst.reg2i14_format.rd; + } + } + break; + case stptrd_op: + if (inst.reg2i14_format.rj == CFI_SP) { + /* stptr.d ra,sp,0 */ + if (inst.reg2i14_format.rd == LOONGARCH_GPR_RA && + inst.reg2i14_format.immediate == 0) + break; + + /* stptr.d rd,sp,si14 */ + insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13); + ADD_OP(op) { + op->src.type = OP_SRC_REG; + op->src.reg = inst.reg2i14_format.rd; + op->dest.type = OP_DEST_REG_INDIRECT; + op->dest.reg = CFI_SP; + op->dest.offset = insn->immediate; + } + } + break; + default: + return false; + } + + return true; +} + +static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst, + struct instruction *insn) +{ + switch (inst.reg2i16_format.opcode) { + case jirl_op: + if (inst.reg2i16_format.rd == 0 && + inst.reg2i16_format.rj == CFI_RA && + inst.reg2i16_format.immediate == 0) { + /* jirl r0,ra,0 */ + insn->type = INSN_RETURN; + } else if (inst.reg2i16_format.rd == CFI_RA) { + /* jirl ra,rj,offs16 */ + insn->type = INSN_CALL_DYNAMIC; + } else if (inst.reg2i16_format.rd == CFI_A0 && + inst.reg2i16_format.immediate == 0) { + /* + * jirl a0,t0,0 + * this is a special case in loongarch_suspend_enter, + * just treat it as a call instruction. + */ + insn->type = INSN_CALL_DYNAMIC; + } else if (inst.reg2i16_format.rd == 0 && + inst.reg2i16_format.immediate == 0) { + /* jirl r0,rj,0 */ + insn->type = INSN_JUMP_DYNAMIC; + } else if (inst.reg2i16_format.rd == 0 && + inst.reg2i16_format.immediate != 0) { + /* + * jirl r0,t0,12 + * this is a rare case in JUMP_VIRT_ADDR, + * just ignore it due to it is harmless for tracing. + */ + break; + } else { + /* jirl rd,rj,offs16 */ + insn->type = INSN_JUMP_UNCONDITIONAL; + insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15); + } + break; + case beq_op: + case bne_op: + case blt_op: + case bge_op: + case bltu_op: + case bgeu_op: + insn->type = INSN_JUMP_CONDITIONAL; + insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15); + break; + default: + return false; + } + + return true; +} + int arch_decode_instruction(struct objtool_file *file, const struct section *sec, unsigned long offset, unsigned int maxlen, struct instruction *insn) { + struct stack_op **ops_list = &insn->stack_ops; + const struct elf *elf = file->elf; + struct stack_op *op = NULL; + union loongarch_instruction inst; + + if (!is_loongarch(elf)) + return -1; + + if (maxlen < LOONGARCH_INSN_SIZE) + return 0; + + insn->len = LOONGARCH_INSN_SIZE; + insn->type = INSN_OTHER; + insn->immediate = 0; + + inst = *(union loongarch_instruction *)(sec->data->d_buf + offset); + + if (decode_insn_reg0i26_fomat(inst, insn)) + return 0; + if (decode_insn_reg1i21_fomat(inst, insn)) + return 0; + if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op)) + return 0; + if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op)) + return 0; + if (decode_insn_reg2i16_fomat(inst, insn)) + return 0; + + if (inst.word == 0) + insn->type = INSN_NOP; + else if (inst.reg0i15_format.opcode == break_op) { + /* break */ + insn->type = INSN_BUG; + } else if (inst.reg2_format.opcode == ertn_op) { + /* ertn */ + insn->type = INSN_RETURN; + } + return 0; } const char *arch_nop_insn(int len) { - return NULL; + static u32 nop; + + if (len != LOONGARCH_INSN_SIZE) + WARN("invalid NOP size: %d\n", len); + + nop = LOONGARCH_INSN_NOP; + + return (const char *)&nop; } const char *arch_ret_insn(int len) { - return NULL; + static u32 ret; + + if (len != LOONGARCH_INSN_SIZE) + WARN("invalid RET size: %d\n", len); + + emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0); + + return (const char *)&ret; } void arch_initial_func_cfi_state(struct cfi_init_state *state) From 70146607e5663f748f6d1febe5b0d951e0151746 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 11 Mar 2024 22:23:47 +0800 Subject: [PATCH 03/23] objtool/x86: Separate arch-specific and generic parts commit b8e85e6f3a09fc56b0ff574887798962ef8a8f80 upstream. Move init_orc_entry(), write_orc_entry(), reg_name(), orc_type_name() and print_reg() from generic orc_gen.c and orc_dump.c to arch-specific orc.c, then introduce a new function orc_print_dump() to print info. This is preparation for later patch, no functionality change. Co-developed-by: Jinyang He Signed-off-by: Jinyang He Co-developed-by: Youling Tang Signed-off-by: Youling Tang Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- tools/objtool/arch/x86/Build | 1 + tools/objtool/arch/x86/orc.c | 188 ++++++++++++++++++++++++++++ tools/objtool/include/objtool/orc.h | 14 +++ tools/objtool/orc_dump.c | 69 +--------- tools/objtool/orc_gen.c | 113 +---------------- 5 files changed, 206 insertions(+), 179 deletions(-) create mode 100644 tools/objtool/arch/x86/orc.c create mode 100644 tools/objtool/include/objtool/orc.h diff --git a/tools/objtool/arch/x86/Build b/tools/objtool/arch/x86/Build index 9f7869b5c5e0..3dedb2fd8f3a 100644 --- a/tools/objtool/arch/x86/Build +++ b/tools/objtool/arch/x86/Build @@ -1,5 +1,6 @@ objtool-y += special.o objtool-y += decode.o +objtool-y += orc.o inat_tables_script = ../arch/x86/tools/gen-insn-attr-x86.awk inat_tables_maps = ../arch/x86/lib/x86-opcode-map.txt diff --git a/tools/objtool/arch/x86/orc.c b/tools/objtool/arch/x86/orc.c new file mode 100644 index 000000000000..b6cd943e87f9 --- /dev/null +++ b/tools/objtool/arch/x86/orc.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + +#include +#include +#include +#include + +int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn) +{ + struct cfi_reg *bp = &cfi->regs[CFI_BP]; + + memset(orc, 0, sizeof(*orc)); + + if (!cfi) { + /* + * This is usually either unreachable nops/traps (which don't + * trigger unreachable instruction warnings), or + * STACK_FRAME_NON_STANDARD functions. + */ + orc->type = ORC_TYPE_UNDEFINED; + return 0; + } + + switch (cfi->type) { + case UNWIND_HINT_TYPE_UNDEFINED: + orc->type = ORC_TYPE_UNDEFINED; + return 0; + case UNWIND_HINT_TYPE_END_OF_STACK: + orc->type = ORC_TYPE_END_OF_STACK; + return 0; + case UNWIND_HINT_TYPE_CALL: + orc->type = ORC_TYPE_CALL; + break; + case UNWIND_HINT_TYPE_REGS: + orc->type = ORC_TYPE_REGS; + break; + case UNWIND_HINT_TYPE_REGS_PARTIAL: + orc->type = ORC_TYPE_REGS_PARTIAL; + break; + default: + WARN_INSN(insn, "unknown unwind hint type %d", cfi->type); + return -1; + } + + orc->signal = cfi->signal; + + switch (cfi->cfa.base) { + case CFI_SP: + orc->sp_reg = ORC_REG_SP; + break; + case CFI_SP_INDIRECT: + orc->sp_reg = ORC_REG_SP_INDIRECT; + break; + case CFI_BP: + orc->sp_reg = ORC_REG_BP; + break; + case CFI_BP_INDIRECT: + orc->sp_reg = ORC_REG_BP_INDIRECT; + break; + case CFI_R10: + orc->sp_reg = ORC_REG_R10; + break; + case CFI_R13: + orc->sp_reg = ORC_REG_R13; + break; + case CFI_DI: + orc->sp_reg = ORC_REG_DI; + break; + case CFI_DX: + orc->sp_reg = ORC_REG_DX; + break; + default: + WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); + return -1; + } + + switch (bp->base) { + case CFI_UNDEFINED: + orc->bp_reg = ORC_REG_UNDEFINED; + break; + case CFI_CFA: + orc->bp_reg = ORC_REG_PREV_SP; + break; + case CFI_BP: + orc->bp_reg = ORC_REG_BP; + break; + default: + WARN_INSN(insn, "unknown BP base reg %d", bp->base); + return -1; + } + + orc->sp_offset = cfi->cfa.offset; + orc->bp_offset = bp->offset; + + return 0; +} + +int write_orc_entry(struct elf *elf, struct section *orc_sec, + struct section *ip_sec, unsigned int idx, + struct section *insn_sec, unsigned long insn_off, + struct orc_entry *o) +{ + struct orc_entry *orc; + + /* populate ORC data */ + orc = (struct orc_entry *)orc_sec->data->d_buf + idx; + memcpy(orc, o, sizeof(*orc)); + orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); + orc->bp_offset = bswap_if_needed(elf, orc->bp_offset); + + /* populate reloc for ip */ + if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, + insn_sec, insn_off)) + return -1; + + return 0; +} + +static const char *reg_name(unsigned int reg) +{ + switch (reg) { + case ORC_REG_PREV_SP: + return "prevsp"; + case ORC_REG_DX: + return "dx"; + case ORC_REG_DI: + return "di"; + case ORC_REG_BP: + return "bp"; + case ORC_REG_SP: + return "sp"; + case ORC_REG_R10: + return "r10"; + case ORC_REG_R13: + return "r13"; + case ORC_REG_BP_INDIRECT: + return "bp(ind)"; + case ORC_REG_SP_INDIRECT: + return "sp(ind)"; + default: + return "?"; + } +} + +static const char *orc_type_name(unsigned int type) +{ + switch (type) { + case ORC_TYPE_UNDEFINED: + return "(und)"; + case ORC_TYPE_END_OF_STACK: + return "end"; + case ORC_TYPE_CALL: + return "call"; + case ORC_TYPE_REGS: + return "regs"; + case ORC_TYPE_REGS_PARTIAL: + return "regs (partial)"; + default: + return "?"; + } +} + +static void print_reg(unsigned int reg, int offset) +{ + if (reg == ORC_REG_BP_INDIRECT) + printf("(bp%+d)", offset); + else if (reg == ORC_REG_SP_INDIRECT) + printf("(sp)%+d", offset); + else if (reg == ORC_REG_UNDEFINED) + printf("(und)"); + else + printf("%s%+d", reg_name(reg), offset); +} + +void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i) +{ + printf("type:%s", orc_type_name(orc[i].type)); + + printf(" sp:"); + print_reg(orc[i].sp_reg, bswap_if_needed(dummy_elf, orc[i].sp_offset)); + + printf(" bp:"); + print_reg(orc[i].bp_reg, bswap_if_needed(dummy_elf, orc[i].bp_offset)); + + printf(" signal:%d\n", orc[i].signal); +} diff --git a/tools/objtool/include/objtool/orc.h b/tools/objtool/include/objtool/orc.h new file mode 100644 index 000000000000..15a32def1071 --- /dev/null +++ b/tools/objtool/include/objtool/orc.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _OBJTOOL_ORC_H +#define _OBJTOOL_ORC_H + +#include + +int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn); +void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i); +int write_orc_entry(struct elf *elf, struct section *orc_sec, + struct section *ip_sec, unsigned int idx, + struct section *insn_sec, unsigned long insn_off, + struct orc_entry *o); + +#endif /* _OBJTOOL_ORC_H */ diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c index 0e183bb1c720..a62247efb64f 100644 --- a/tools/objtool/orc_dump.c +++ b/tools/objtool/orc_dump.c @@ -6,65 +6,10 @@ #include #include #include +#include #include #include -static const char *reg_name(unsigned int reg) -{ - switch (reg) { - case ORC_REG_PREV_SP: - return "prevsp"; - case ORC_REG_DX: - return "dx"; - case ORC_REG_DI: - return "di"; - case ORC_REG_BP: - return "bp"; - case ORC_REG_SP: - return "sp"; - case ORC_REG_R10: - return "r10"; - case ORC_REG_R13: - return "r13"; - case ORC_REG_BP_INDIRECT: - return "bp(ind)"; - case ORC_REG_SP_INDIRECT: - return "sp(ind)"; - default: - return "?"; - } -} - -static const char *orc_type_name(unsigned int type) -{ - switch (type) { - case ORC_TYPE_UNDEFINED: - return "(und)"; - case ORC_TYPE_END_OF_STACK: - return "end"; - case ORC_TYPE_CALL: - return "call"; - case ORC_TYPE_REGS: - return "regs"; - case ORC_TYPE_REGS_PARTIAL: - return "regs (partial)"; - default: - return "?"; - } -} - -static void print_reg(unsigned int reg, int offset) -{ - if (reg == ORC_REG_BP_INDIRECT) - printf("(bp%+d)", offset); - else if (reg == ORC_REG_SP_INDIRECT) - printf("(sp)%+d", offset); - else if (reg == ORC_REG_UNDEFINED) - printf("(und)"); - else - printf("%s%+d", reg_name(reg), offset); -} - int orc_dump(const char *_objname) { int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0; @@ -205,17 +150,7 @@ int orc_dump(const char *_objname) printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i])); } - printf("type:%s", orc_type_name(orc[i].type)); - - printf(" sp:"); - - print_reg(orc[i].sp_reg, bswap_if_needed(&dummy_elf, orc[i].sp_offset)); - - printf(" bp:"); - - print_reg(orc[i].bp_reg, bswap_if_needed(&dummy_elf, orc[i].bp_offset)); - - printf(" signal:%d\n", orc[i].signal); + orc_print_dump(&dummy_elf, orc, i); } elf_end(elf); diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c index bae343908867..922e6aac7cea 100644 --- a/tools/objtool/orc_gen.c +++ b/tools/objtool/orc_gen.c @@ -10,121 +10,10 @@ #include #include +#include #include #include -static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, - struct instruction *insn) -{ - struct cfi_reg *bp = &cfi->regs[CFI_BP]; - - memset(orc, 0, sizeof(*orc)); - - if (!cfi) { - /* - * This is usually either unreachable nops/traps (which don't - * trigger unreachable instruction warnings), or - * STACK_FRAME_NON_STANDARD functions. - */ - orc->type = ORC_TYPE_UNDEFINED; - return 0; - } - - switch (cfi->type) { - case UNWIND_HINT_TYPE_UNDEFINED: - orc->type = ORC_TYPE_UNDEFINED; - return 0; - case UNWIND_HINT_TYPE_END_OF_STACK: - orc->type = ORC_TYPE_END_OF_STACK; - return 0; - case UNWIND_HINT_TYPE_CALL: - orc->type = ORC_TYPE_CALL; - break; - case UNWIND_HINT_TYPE_REGS: - orc->type = ORC_TYPE_REGS; - break; - case UNWIND_HINT_TYPE_REGS_PARTIAL: - orc->type = ORC_TYPE_REGS_PARTIAL; - break; - default: - WARN_INSN(insn, "unknown unwind hint type %d", cfi->type); - return -1; - } - - orc->signal = cfi->signal; - - switch (cfi->cfa.base) { - case CFI_SP: - orc->sp_reg = ORC_REG_SP; - break; - case CFI_SP_INDIRECT: - orc->sp_reg = ORC_REG_SP_INDIRECT; - break; - case CFI_BP: - orc->sp_reg = ORC_REG_BP; - break; - case CFI_BP_INDIRECT: - orc->sp_reg = ORC_REG_BP_INDIRECT; - break; - case CFI_R10: - orc->sp_reg = ORC_REG_R10; - break; - case CFI_R13: - orc->sp_reg = ORC_REG_R13; - break; - case CFI_DI: - orc->sp_reg = ORC_REG_DI; - break; - case CFI_DX: - orc->sp_reg = ORC_REG_DX; - break; - default: - WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); - return -1; - } - - switch (bp->base) { - case CFI_UNDEFINED: - orc->bp_reg = ORC_REG_UNDEFINED; - break; - case CFI_CFA: - orc->bp_reg = ORC_REG_PREV_SP; - break; - case CFI_BP: - orc->bp_reg = ORC_REG_BP; - break; - default: - WARN_INSN(insn, "unknown BP base reg %d", bp->base); - return -1; - } - - orc->sp_offset = cfi->cfa.offset; - orc->bp_offset = bp->offset; - - return 0; -} - -static int write_orc_entry(struct elf *elf, struct section *orc_sec, - struct section *ip_sec, unsigned int idx, - struct section *insn_sec, unsigned long insn_off, - struct orc_entry *o) -{ - struct orc_entry *orc; - - /* populate ORC data */ - orc = (struct orc_entry *)orc_sec->data->d_buf + idx; - memcpy(orc, o, sizeof(*orc)); - orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); - orc->bp_offset = bswap_if_needed(elf, orc->bp_offset); - - /* populate reloc for ip */ - if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, - insn_sec, insn_off)) - return -1; - - return 0; -} - struct orc_list_entry { struct list_head list; struct orc_entry orc; From a2265ff39861cddb2ea0b188665b444277db6958 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 11 Mar 2024 22:23:47 +0800 Subject: [PATCH 04/23] objtool/LoongArch: Enable orc to be built commit 3c7266cd7bc5e7843b631fea73cb0e82111e3158 upstream. Implement arch-specific init_orc_entry(), write_orc_entry(), reg_name(), orc_type_name(), print_reg() and orc_print_dump(), then set BUILD_ORC as y to build the orc related files. Co-developed-by: Jinyang He Signed-off-by: Jinyang He Co-developed-by: Youling Tang Signed-off-by: Youling Tang Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- tools/arch/loongarch/include/asm/orc_types.h | 58 +++++++ tools/objtool/Makefile | 4 + tools/objtool/arch/loongarch/Build | 1 + tools/objtool/arch/loongarch/decode.c | 16 ++ tools/objtool/arch/loongarch/orc.c | 171 +++++++++++++++++++ 5 files changed, 250 insertions(+) create mode 100644 tools/arch/loongarch/include/asm/orc_types.h create mode 100644 tools/objtool/arch/loongarch/orc.c diff --git a/tools/arch/loongarch/include/asm/orc_types.h b/tools/arch/loongarch/include/asm/orc_types.h new file mode 100644 index 000000000000..caf1f71a1057 --- /dev/null +++ b/tools/arch/loongarch/include/asm/orc_types.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ORC_TYPES_H +#define _ORC_TYPES_H + +#include + +/* + * The ORC_REG_* registers are base registers which are used to find other + * registers on the stack. + * + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the + * address of the previous frame: the caller's SP before it called the current + * function. + * + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in + * the current frame. + * + * The most commonly used base registers are SP and FP -- which the previous SP + * is usually based on -- and PREV_SP and UNDEFINED -- which the previous FP is + * usually based on. + * + * The rest of the base registers are needed for special cases like entry code + * and GCC realigned stacks. + */ +#define ORC_REG_UNDEFINED 0 +#define ORC_REG_PREV_SP 1 +#define ORC_REG_SP 2 +#define ORC_REG_FP 3 +#define ORC_REG_MAX 4 + +#define ORC_TYPE_UNDEFINED 0 +#define ORC_TYPE_END_OF_STACK 1 +#define ORC_TYPE_CALL 2 +#define ORC_TYPE_REGS 3 +#define ORC_TYPE_REGS_PARTIAL 4 + +#ifndef __ASSEMBLY__ +/* + * This struct is more or less a vastly simplified version of the DWARF Call + * Frame Information standard. It contains only the necessary parts of DWARF + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the + * unwinder how to find the previous SP and FP (and sometimes entry regs) on + * the stack for a given code address. Each instance of the struct corresponds + * to one or more code locations. + */ +struct orc_entry { + s16 sp_offset; + s16 fp_offset; + s16 ra_offset; + unsigned int sp_reg:4; + unsigned int fp_reg:4; + unsigned int ra_reg:4; + unsigned int type:3; + unsigned int signal:1; +}; +#endif /* __ASSEMBLY__ */ + +#endif /* _ORC_TYPES_H */ diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index 83b100c1e7f6..bf7f7f84ac62 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -57,6 +57,10 @@ ifeq ($(SRCARCH),x86) BUILD_ORC := y endif +ifeq ($(SRCARCH),loongarch) + BUILD_ORC := y +endif + export BUILD_ORC export srctree OUTPUT CFLAGS SRCARCH AWK include $(srctree)/tools/build/Makefile.include diff --git a/tools/objtool/arch/loongarch/Build b/tools/objtool/arch/loongarch/Build index d24d5636a5b8..1d4b784b6887 100644 --- a/tools/objtool/arch/loongarch/Build +++ b/tools/objtool/arch/loongarch/Build @@ -1,2 +1,3 @@ objtool-y += decode.o objtool-y += special.o +objtool-y += orc.o diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c index ff0b53144d12..aee479d2191c 100644 --- a/tools/objtool/arch/loongarch/decode.c +++ b/tools/objtool/arch/loongarch/decode.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #ifndef EM_LOONGARCH #define EM_LOONGARCH 258 @@ -42,6 +44,20 @@ bool arch_callee_saved_reg(unsigned char reg) int arch_decode_hint_reg(u8 sp_reg, int *base) { + switch (sp_reg) { + case ORC_REG_UNDEFINED: + *base = CFI_UNDEFINED; + break; + case ORC_REG_SP: + *base = CFI_SP; + break; + case ORC_REG_FP: + *base = CFI_FP; + break; + default: + return -1; + } + return 0; } diff --git a/tools/objtool/arch/loongarch/orc.c b/tools/objtool/arch/loongarch/orc.c new file mode 100644 index 000000000000..873536d009d9 --- /dev/null +++ b/tools/objtool/arch/loongarch/orc.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + +#include +#include +#include +#include + +int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn) +{ + struct cfi_reg *fp = &cfi->regs[CFI_FP]; + struct cfi_reg *ra = &cfi->regs[CFI_RA]; + + memset(orc, 0, sizeof(*orc)); + + if (!cfi) { + /* + * This is usually either unreachable nops/traps (which don't + * trigger unreachable instruction warnings), or + * STACK_FRAME_NON_STANDARD functions. + */ + orc->type = ORC_TYPE_UNDEFINED; + return 0; + } + + switch (cfi->type) { + case UNWIND_HINT_TYPE_UNDEFINED: + orc->type = ORC_TYPE_UNDEFINED; + return 0; + case UNWIND_HINT_TYPE_END_OF_STACK: + orc->type = ORC_TYPE_END_OF_STACK; + return 0; + case UNWIND_HINT_TYPE_CALL: + orc->type = ORC_TYPE_CALL; + break; + case UNWIND_HINT_TYPE_REGS: + orc->type = ORC_TYPE_REGS; + break; + case UNWIND_HINT_TYPE_REGS_PARTIAL: + orc->type = ORC_TYPE_REGS_PARTIAL; + break; + default: + WARN_INSN(insn, "unknown unwind hint type %d", cfi->type); + return -1; + } + + orc->signal = cfi->signal; + + switch (cfi->cfa.base) { + case CFI_SP: + orc->sp_reg = ORC_REG_SP; + break; + case CFI_FP: + orc->sp_reg = ORC_REG_FP; + break; + default: + WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); + return -1; + } + + switch (fp->base) { + case CFI_UNDEFINED: + orc->fp_reg = ORC_REG_UNDEFINED; + orc->fp_offset = 0; + break; + case CFI_CFA: + orc->fp_reg = ORC_REG_PREV_SP; + orc->fp_offset = fp->offset; + break; + case CFI_FP: + orc->fp_reg = ORC_REG_FP; + break; + default: + WARN_INSN(insn, "unknown FP base reg %d", fp->base); + return -1; + } + + switch (ra->base) { + case CFI_UNDEFINED: + orc->ra_reg = ORC_REG_UNDEFINED; + orc->ra_offset = 0; + break; + case CFI_CFA: + orc->ra_reg = ORC_REG_PREV_SP; + orc->ra_offset = ra->offset; + break; + case CFI_FP: + orc->ra_reg = ORC_REG_FP; + break; + default: + WARN_INSN(insn, "unknown RA base reg %d", ra->base); + return -1; + } + + orc->sp_offset = cfi->cfa.offset; + + return 0; +} + +int write_orc_entry(struct elf *elf, struct section *orc_sec, + struct section *ip_sec, unsigned int idx, + struct section *insn_sec, unsigned long insn_off, + struct orc_entry *o) +{ + struct orc_entry *orc; + + /* populate ORC data */ + orc = (struct orc_entry *)orc_sec->data->d_buf + idx; + memcpy(orc, o, sizeof(*orc)); + + /* populate reloc for ip */ + if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, + insn_sec, insn_off)) + return -1; + + return 0; +} + +static const char *reg_name(unsigned int reg) +{ + switch (reg) { + case ORC_REG_SP: + return "sp"; + case ORC_REG_FP: + return "fp"; + case ORC_REG_PREV_SP: + return "prevsp"; + default: + return "?"; + } +} + +static const char *orc_type_name(unsigned int type) +{ + switch (type) { + case UNWIND_HINT_TYPE_CALL: + return "call"; + case UNWIND_HINT_TYPE_REGS: + return "regs"; + case UNWIND_HINT_TYPE_REGS_PARTIAL: + return "regs (partial)"; + default: + return "?"; + } +} + +static void print_reg(unsigned int reg, int offset) +{ + if (reg == ORC_REG_UNDEFINED) + printf(" (und) "); + else + printf("%s + %3d", reg_name(reg), offset); + +} + +void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i) +{ + printf("type:%s", orc_type_name(orc[i].type)); + + printf(" sp:"); + print_reg(orc[i].sp_reg, orc[i].sp_offset); + + printf(" fp:"); + print_reg(orc[i].fp_reg, orc[i].fp_offset); + + printf(" ra:"); + print_reg(orc[i].ra_reg, orc[i].ra_offset); + + printf(" signal:%d\n", orc[i].signal); +} From 1218e5151e2a06acdcb9e71f4bf78b71d92b20f1 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 11 Mar 2024 22:23:47 +0800 Subject: [PATCH 05/23] objtool: Check local label in add_dead_ends() commit d5ab2bc36c6b0ce2f3409f934ff9cdf6d6768fa2 upstream. When update the latest upstream gcc and binutils, it generates more objtool warnings on LoongArch, like this: init/main.o: warning: objtool: unexpected relocation symbol type in .rela.discard.unreachable We can see that the reloc sym name is local label instead of section in relocation section '.rela.discard.unreachable', in this case, the reloc sym type is STT_NOTYPE instead of STT_SECTION. As suggested by Peter Zijlstra, we add a "local_label" member in struct symbol, then set it as true if symbol type is STT_NOTYPE and symbol name starts with ".L" string in classify_symbols(). Let's check reloc->sym->local_label to not return -1 in add_dead_ends(), and also use reloc->sym->offset instead of reloc addend which is 0 to find the corresponding instruction. At the same time, let's replace the variable "addend" with "offset" to reflect the reality. Here are some detailed info: [fedora@linux 6.8.test]$ gcc --version gcc (GCC) 14.0.1 20240129 (experimental) [fedora@linux 6.8.test]$ as --version GNU assembler (GNU Binutils) 2.42.50.20240129 [fedora@linux 6.8.test]$ readelf -r init/main.o | grep -A 2 "rela.discard.unreachable" Relocation section '.rela.discard.unreachable' at offset 0x6028 contains 1 entry: Offset Info Type Sym. Value Sym. Name + Addend 000000000000 00d900000063 R_LARCH_32_PCREL 00000000000002c4 .L500^B1 + 0 Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- tools/objtool/check.c | 40 +++++++++++++++++------------ tools/objtool/include/objtool/elf.h | 1 + 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index e3fc263b1b20..a85d58d6e03f 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -20,6 +20,7 @@ #include #include #include +#include struct alternative { struct alternative *next; @@ -584,7 +585,7 @@ static int add_dead_ends(struct objtool_file *file) struct section *rsec; struct reloc *reloc; struct instruction *insn; - s64 addend; + unsigned long offset; /* * Check for manually annotated dead ends. @@ -594,27 +595,28 @@ static int add_dead_ends(struct objtool_file *file) goto reachable; for_each_reloc(rsec, reloc) { - - if (reloc->sym->type != STT_SECTION) { + if (reloc->sym->type == STT_SECTION) { + offset = reloc_addend(reloc); + } else if (reloc->sym->local_label) { + offset = reloc->sym->offset; + } else { WARN("unexpected relocation symbol type in %s", rsec->name); return -1; } - addend = reloc_addend(reloc); - - insn = find_insn(file, reloc->sym->sec, addend); + insn = find_insn(file, reloc->sym->sec, offset); if (insn) insn = prev_insn_same_sec(file, insn); - else if (addend == reloc->sym->sec->sh.sh_size) { + else if (offset == reloc->sym->sec->sh.sh_size) { insn = find_last_insn(file, reloc->sym->sec); if (!insn) { WARN("can't find unreachable insn at %s+0x%" PRIx64, - reloc->sym->sec->name, addend); + reloc->sym->sec->name, offset); return -1; } } else { WARN("can't find unreachable insn at %s+0x%" PRIx64, - reloc->sym->sec->name, addend); + reloc->sym->sec->name, offset); return -1; } @@ -633,27 +635,28 @@ reachable: return 0; for_each_reloc(rsec, reloc) { - - if (reloc->sym->type != STT_SECTION) { + if (reloc->sym->type == STT_SECTION) { + offset = reloc_addend(reloc); + } else if (reloc->sym->local_label) { + offset = reloc->sym->offset; + } else { WARN("unexpected relocation symbol type in %s", rsec->name); return -1; } - addend = reloc_addend(reloc); - - insn = find_insn(file, reloc->sym->sec, addend); + insn = find_insn(file, reloc->sym->sec, offset); if (insn) insn = prev_insn_same_sec(file, insn); - else if (addend == reloc->sym->sec->sh.sh_size) { + else if (offset == reloc->sym->sec->sh.sh_size) { insn = find_last_insn(file, reloc->sym->sec); if (!insn) { WARN("can't find reachable insn at %s+0x%" PRIx64, - reloc->sym->sec->name, addend); + reloc->sym->sec->name, offset); return -1; } } else { WARN("can't find reachable insn at %s+0x%" PRIx64, - reloc->sym->sec->name, addend); + reloc->sym->sec->name, offset); return -1; } @@ -2506,6 +2509,9 @@ static int classify_symbols(struct objtool_file *file) struct symbol *func; for_each_sym(file, func) { + if (func->type == STT_NOTYPE && strstarts(func->name, ".L")) + func->local_label = true; + if (func->bind != STB_GLOBAL) continue; diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index 9f71e988eca4..2b8a69de4db8 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -67,6 +67,7 @@ struct symbol { u8 profiling_func : 1; u8 warned : 1; u8 embedded_insn : 1; + u8 local_label : 1; struct list_head pv_target; struct reloc *relocs; }; From 69b3f5cd6ed76ff5058efcbb45389782a8a6ce5c Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 11 Mar 2024 22:23:47 +0800 Subject: [PATCH 06/23] objtool: Check local label in read_unwind_hints() commit e91c5e4c21b0339376ee124cda5c9b27d41f2cbc upstream. When update the latest upstream gcc and binutils, it generates some objtool warnings on LoongArch, like this: arch/loongarch/kernel/entry.o: warning: objtool: ret_from_fork+0x0: unreachable instruction We can see that the reloc sym name is local label instead of section in relocation section '.rela.discard.unwind_hints', in this case, the reloc sym type is STT_NOTYPE instead of STT_SECTION. Let us check it to not return -1, then use reloc->sym->offset instead of reloc addend which is 0 to find the corresponding instruction. Here are some detailed info: [fedora@linux 6.8.test]$ gcc --version gcc (GCC) 14.0.1 20240129 (experimental) [fedora@linux 6.8.test]$ as --version GNU assembler (GNU Binutils) 2.42.50.20240129 [fedora@linux 6.8.test]$ readelf -r arch/loongarch/kernel/entry.o | grep -A 3 "rela.discard.unwind_hints" Relocation section '.rela.discard.unwind_hints' at offset 0x3a8 contains 7 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000000 000a00000063 R_LARCH_32_PCREL 0000000000000000 .Lhere_1 + 0 00000000000c 000b00000063 R_LARCH_32_PCREL 00000000000000a8 .Lhere_50 + 0 Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- tools/objtool/check.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index a85d58d6e03f..05a6335116c9 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -2211,6 +2211,7 @@ static int read_unwind_hints(struct objtool_file *file) struct unwind_hint *hint; struct instruction *insn; struct reloc *reloc; + unsigned long offset; int i; sec = find_section_by_name(file->elf, ".discard.unwind_hints"); @@ -2238,7 +2239,16 @@ static int read_unwind_hints(struct objtool_file *file) return -1; } - insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); + if (reloc->sym->type == STT_SECTION) { + offset = reloc_addend(reloc); + } else if (reloc->sym->local_label) { + offset = reloc->sym->offset; + } else { + WARN("unexpected relocation symbol type in %s", sec->rsec->name); + return -1; + } + + insn = find_insn(file, reloc->sym->sec, offset); if (!insn) { WARN("can't find insn for unwind_hints[%d]", i); return -1; From 559ae71cef99cc8e7ae4991e56703637e08738c6 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 11 Mar 2024 22:23:47 +0800 Subject: [PATCH 07/23] LoongArch: Add ORC stack unwinder support commit cb8a2ef0848ca80d67d6d56e2df757cfdf6b3355 upstream. The kernel CONFIG_UNWINDER_ORC option enables the ORC unwinder, which is similar in concept to a DWARF unwinder. The difference is that the format of the ORC data is much simpler than DWARF, which in turn allows the ORC unwinder to be much simpler and faster. The ORC data consists of unwind tables which are generated by objtool. After analyzing all the code paths of a .o file, it determines information about the stack state at each instruction address in the file and outputs that information to the .orc_unwind and .orc_unwind_ip sections. The per-object ORC sections are combined at link time and are sorted and post-processed at boot time. The unwinder uses the resulting data to correlate instruction addresses with their stack states at run time. Most of the logic are similar with x86, in order to get ra info before ra is saved into stack, add ra_reg and ra_offset into orc_entry. At the same time, modify some arch-specific code to silence the objtool warnings. Co-developed-by: Jinyang He Signed-off-by: Jinyang He Co-developed-by: Youling Tang Signed-off-by: Youling Tang Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- arch/loongarch/Kconfig | 2 + arch/loongarch/Kconfig.debug | 11 + arch/loongarch/Makefile | 23 +- arch/loongarch/include/asm/Kbuild | 2 + arch/loongarch/include/asm/bug.h | 1 + arch/loongarch/include/asm/exception.h | 2 + arch/loongarch/include/asm/module.h | 7 + arch/loongarch/include/asm/orc_header.h | 18 + arch/loongarch/include/asm/orc_lookup.h | 31 ++ arch/loongarch/include/asm/orc_types.h | 58 +++ arch/loongarch/include/asm/stackframe.h | 3 + arch/loongarch/include/asm/unwind.h | 20 +- arch/loongarch/include/asm/unwind_hints.h | 28 ++ arch/loongarch/kernel/Makefile | 4 + arch/loongarch/kernel/entry.S | 5 + arch/loongarch/kernel/fpu.S | 7 + arch/loongarch/kernel/genex.S | 6 + arch/loongarch/kernel/lbt.S | 3 + arch/loongarch/kernel/mcount_dyn.S | 6 + arch/loongarch/kernel/module.c | 22 +- arch/loongarch/kernel/relocate_kernel.S | 7 +- arch/loongarch/kernel/rethook_trampoline.S | 1 + arch/loongarch/kernel/setup.c | 2 + arch/loongarch/kernel/stacktrace.c | 1 + arch/loongarch/kernel/traps.c | 42 +- arch/loongarch/kernel/unwind_orc.c | 528 +++++++++++++++++++++ arch/loongarch/kernel/vmlinux.lds.S | 3 + arch/loongarch/kvm/switch.S | 9 +- arch/loongarch/lib/clear_user.S | 3 + arch/loongarch/lib/copy_user.S | 3 + arch/loongarch/lib/memcpy.S | 3 + arch/loongarch/lib/memset.S | 3 + arch/loongarch/mm/tlb.c | 27 +- arch/loongarch/mm/tlbex.S | 9 + arch/loongarch/vdso/Makefile | 1 + include/linux/compiler.h | 9 + scripts/Makefile | 7 +- 37 files changed, 875 insertions(+), 42 deletions(-) create mode 100644 arch/loongarch/include/asm/orc_header.h create mode 100644 arch/loongarch/include/asm/orc_lookup.h create mode 100644 arch/loongarch/include/asm/orc_types.h create mode 100644 arch/loongarch/include/asm/unwind_hints.h create mode 100644 arch/loongarch/kernel/unwind_orc.c diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 8c1854ef9561..2a25bf82b769 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -135,6 +135,7 @@ config LOONGARCH select HAVE_KVM select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI + select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS select HAVE_PCI select HAVE_PERF_EVENTS select HAVE_PERF_REGS @@ -145,6 +146,7 @@ config LOONGARCH select HAVE_SAMPLE_FTRACE_DIRECT select HAVE_SAMPLE_FTRACE_DIRECT_MULTI select HAVE_SETUP_PER_CPU_AREA if NUMA + select HAVE_STACK_VALIDATION if HAVE_OBJTOOL select HAVE_STACKPROTECTOR select ARCH_HAS_PHYS_TO_DMA select HAVE_SYSCALL_TRACEPOINTS diff --git a/arch/loongarch/Kconfig.debug b/arch/loongarch/Kconfig.debug index 8d36aab53008..98d60630c3d4 100644 --- a/arch/loongarch/Kconfig.debug +++ b/arch/loongarch/Kconfig.debug @@ -26,4 +26,15 @@ config UNWINDER_PROLOGUE Some of the addresses it reports may be incorrect (but better than the Guess unwinder). +config UNWINDER_ORC + bool "ORC unwinder" + select OBJTOOL + help + This option enables the ORC (Oops Rewind Capability) unwinder for + unwinding kernel stack traces. It uses a custom data format which is + a simplified version of the DWARF Call Frame Information standard. + + Enabling this option will increase the kernel's runtime memory usage + by roughly 2-4MB, depending on your kernel config. + endchoice diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index 81e8089c9c4f..e05c10b04456 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -25,6 +25,18 @@ endif 32bit-emul = elf32loongarch 64bit-emul = elf64loongarch +ifdef CONFIG_UNWINDER_ORC +orc_hash_h := arch/$(SRCARCH)/include/generated/asm/orc_hash.h +orc_hash_sh := $(srctree)/scripts/orc_hash.sh +targets += $(orc_hash_h) +quiet_cmd_orc_hash = GEN $@ + cmd_orc_hash = mkdir -p $(dir $@); \ + $(CONFIG_SHELL) $(orc_hash_sh) < $< > $@ +$(orc_hash_h): $(srctree)/arch/loongarch/include/asm/orc_types.h $(orc_hash_sh) FORCE + $(call if_changed,orc_hash) +archprepare: $(orc_hash_h) +endif + ifdef CONFIG_DYNAMIC_FTRACE KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY CC_FLAGS_FTRACE := -fpatchable-function-entry=2 @@ -68,8 +80,6 @@ LDFLAGS_vmlinux += -static -n -nostdlib ifdef CONFIG_AS_HAS_EXPLICIT_RELOCS cflags-y += $(call cc-option,-mexplicit-relocs) KBUILD_CFLAGS_KERNEL += $(call cc-option,-mdirect-extern-access) -KBUILD_AFLAGS_MODULE += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) -KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) else cflags-y += $(call cc-option,-mno-explicit-relocs) KBUILD_AFLAGS_KERNEL += -Wa,-mla-global-with-pcrel @@ -78,6 +88,15 @@ KBUILD_AFLAGS_MODULE += -Wa,-mla-global-with-abs KBUILD_CFLAGS_MODULE += -fplt -Wa,-mla-global-with-abs,-mla-local-with-abs endif +KBUILD_AFLAGS += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) +KBUILD_CFLAGS += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) +KBUILD_AFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub) +KBUILD_CFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub) + +ifdef CONFIG_OBJTOOL +KBUILD_CFLAGS += -fno-jump-tables +endif + ifeq ($(CONFIG_RELOCATABLE),y) KBUILD_CFLAGS_KERNEL += -fPIE LDFLAGS_vmlinux += -static -pie --no-dynamic-linker -z notext $(call ld-option, --apply-dynamic-relocs) diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild index 22991a6f0e2b..aa4ab6ccc0c0 100644 --- a/arch/loongarch/include/asm/Kbuild +++ b/arch/loongarch/include/asm/Kbuild @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +generated-y += orc_hash.h + generic-y += dma-contiguous.h generic-y += mcs_spinlock.h generic-y += parport.h diff --git a/arch/loongarch/include/asm/bug.h b/arch/loongarch/include/asm/bug.h index d4ca3ba25418..08388876ade4 100644 --- a/arch/loongarch/include/asm/bug.h +++ b/arch/loongarch/include/asm/bug.h @@ -44,6 +44,7 @@ do { \ instrumentation_begin(); \ __BUG_FLAGS(BUGFLAG_WARNING|(flags)); \ + annotate_reachable(); \ instrumentation_end(); \ } while (0) diff --git a/arch/loongarch/include/asm/exception.h b/arch/loongarch/include/asm/exception.h index af74a3fdcad1..c6d20736fd92 100644 --- a/arch/loongarch/include/asm/exception.h +++ b/arch/loongarch/include/asm/exception.h @@ -6,6 +6,8 @@ #include #include +extern void *exception_table[]; + void show_registers(struct pt_regs *regs); asmlinkage void cache_parity_error(void); diff --git a/arch/loongarch/include/asm/module.h b/arch/loongarch/include/asm/module.h index 2ecd82bb64e1..f33f3fd32ecc 100644 --- a/arch/loongarch/include/asm/module.h +++ b/arch/loongarch/include/asm/module.h @@ -6,6 +6,7 @@ #define _ASM_MODULE_H #include +#include #include #define RELA_STACK_DEPTH 16 @@ -21,6 +22,12 @@ struct mod_arch_specific { struct mod_section plt; struct mod_section plt_idx; +#ifdef CONFIG_UNWINDER_ORC + unsigned int num_orcs; + int *orc_unwind_ip; + struct orc_entry *orc_unwind; +#endif + /* For CONFIG_DYNAMIC_FTRACE */ struct plt_entry *ftrace_trampolines; }; diff --git a/arch/loongarch/include/asm/orc_header.h b/arch/loongarch/include/asm/orc_header.h new file mode 100644 index 000000000000..f9d509c3fd70 --- /dev/null +++ b/arch/loongarch/include/asm/orc_header.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _ORC_HEADER_H +#define _ORC_HEADER_H + +#include +#include +#include + +/* + * The header is currently a 20-byte hash of the ORC entry definition; see + * scripts/orc_hash.sh. + */ +#define ORC_HEADER \ + __used __section(".orc_header") __aligned(4) \ + static const u8 orc_header[] = { ORC_HASH } + +#endif /* _ORC_HEADER_H */ diff --git a/arch/loongarch/include/asm/orc_lookup.h b/arch/loongarch/include/asm/orc_lookup.h new file mode 100644 index 000000000000..b02e6357def4 --- /dev/null +++ b/arch/loongarch/include/asm/orc_lookup.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ORC_LOOKUP_H +#define _ORC_LOOKUP_H + +/* + * This is a lookup table for speeding up access to the .orc_unwind table. + * Given an input address offset, the corresponding lookup table entry + * specifies a subset of the .orc_unwind table to search. + * + * Each block represents the end of the previous range and the start of the + * next range. An extra block is added to give the last range an end. + * + * The block size should be a power of 2 to avoid a costly 'div' instruction. + * + * A block size of 256 was chosen because it roughly doubles unwinder + * performance while only adding ~5% to the ORC data footprint. + */ +#define LOOKUP_BLOCK_ORDER 8 +#define LOOKUP_BLOCK_SIZE (1 << LOOKUP_BLOCK_ORDER) + +#ifndef LINKER_SCRIPT + +extern unsigned int orc_lookup[]; +extern unsigned int orc_lookup_end[]; + +#define LOOKUP_START_IP (unsigned long)_stext +#define LOOKUP_STOP_IP (unsigned long)_etext + +#endif /* LINKER_SCRIPT */ + +#endif /* _ORC_LOOKUP_H */ diff --git a/arch/loongarch/include/asm/orc_types.h b/arch/loongarch/include/asm/orc_types.h new file mode 100644 index 000000000000..caf1f71a1057 --- /dev/null +++ b/arch/loongarch/include/asm/orc_types.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _ORC_TYPES_H +#define _ORC_TYPES_H + +#include + +/* + * The ORC_REG_* registers are base registers which are used to find other + * registers on the stack. + * + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the + * address of the previous frame: the caller's SP before it called the current + * function. + * + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in + * the current frame. + * + * The most commonly used base registers are SP and FP -- which the previous SP + * is usually based on -- and PREV_SP and UNDEFINED -- which the previous FP is + * usually based on. + * + * The rest of the base registers are needed for special cases like entry code + * and GCC realigned stacks. + */ +#define ORC_REG_UNDEFINED 0 +#define ORC_REG_PREV_SP 1 +#define ORC_REG_SP 2 +#define ORC_REG_FP 3 +#define ORC_REG_MAX 4 + +#define ORC_TYPE_UNDEFINED 0 +#define ORC_TYPE_END_OF_STACK 1 +#define ORC_TYPE_CALL 2 +#define ORC_TYPE_REGS 3 +#define ORC_TYPE_REGS_PARTIAL 4 + +#ifndef __ASSEMBLY__ +/* + * This struct is more or less a vastly simplified version of the DWARF Call + * Frame Information standard. It contains only the necessary parts of DWARF + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the + * unwinder how to find the previous SP and FP (and sometimes entry regs) on + * the stack for a given code address. Each instance of the struct corresponds + * to one or more code locations. + */ +struct orc_entry { + s16 sp_offset; + s16 fp_offset; + s16 ra_offset; + unsigned int sp_reg:4; + unsigned int fp_reg:4; + unsigned int ra_reg:4; + unsigned int type:3; + unsigned int signal:1; +}; +#endif /* __ASSEMBLY__ */ + +#endif /* _ORC_TYPES_H */ diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h index 35ba862f2025..66736837085b 100644 --- a/arch/loongarch/include/asm/stackframe.h +++ b/arch/loongarch/include/asm/stackframe.h @@ -13,6 +13,7 @@ #include #include #include +#include /* Make the addition of cfi info a little easier. */ .macro cfi_rel_offset reg offset=0 docfi=0 @@ -173,6 +174,7 @@ li.w t0, CSR_CRMD_WE csrxchg t0, t0, LOONGARCH_CSR_CRMD #endif + UNWIND_HINT_REGS .endm .macro SAVE_ALL docfi=0 @@ -230,6 +232,7 @@ .macro RESTORE_SP_AND_RET docfi=0 cfi_ld sp, PT_R3, \docfi + UNWIND_HINT_FUNC ertn .endm diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h index b9dce87afd2e..40a6763c5aec 100644 --- a/arch/loongarch/include/asm/unwind.h +++ b/arch/loongarch/include/asm/unwind.h @@ -16,6 +16,7 @@ enum unwinder_type { UNWINDER_GUESS, UNWINDER_PROLOGUE, + UNWINDER_ORC, }; struct unwind_state { @@ -24,7 +25,7 @@ struct unwind_state { struct task_struct *task; bool first, error, reset; int graph_idx; - unsigned long sp, pc, ra; + unsigned long sp, fp, pc, ra; }; bool default_next_frame(struct unwind_state *state); @@ -61,14 +62,17 @@ static __always_inline void __unwind_start(struct unwind_state *state, state->sp = regs->regs[3]; state->pc = regs->csr_era; state->ra = regs->regs[1]; + state->fp = regs->regs[22]; } else if (task && task != current) { state->sp = thread_saved_fp(task); state->pc = thread_saved_ra(task); state->ra = 0; + state->fp = 0; } else { state->sp = (unsigned long)__builtin_frame_address(0); state->pc = (unsigned long)__builtin_return_address(0); state->ra = 0; + state->fp = 0; } state->task = task; get_stack_info(state->sp, state->task, &state->stack_info); @@ -77,6 +81,18 @@ static __always_inline void __unwind_start(struct unwind_state *state, static __always_inline unsigned long __unwind_get_return_address(struct unwind_state *state) { - return unwind_done(state) ? 0 : state->pc; + if (unwind_done(state)) + return 0; + + return __kernel_text_address(state->pc) ? state->pc : 0; } + +#ifdef CONFIG_UNWINDER_ORC +void unwind_init(void); +void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, void *orc, size_t orc_size); +#else +static inline void unwind_init(void) {} +static inline void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size, void *orc, size_t orc_size) {} +#endif + #endif /* _ASM_UNWIND_H */ diff --git a/arch/loongarch/include/asm/unwind_hints.h b/arch/loongarch/include/asm/unwind_hints.h new file mode 100644 index 000000000000..a01086ad9dde --- /dev/null +++ b/arch/loongarch/include/asm/unwind_hints.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_LOONGARCH_UNWIND_HINTS_H +#define _ASM_LOONGARCH_UNWIND_HINTS_H + +#include +#include + +#ifdef __ASSEMBLY__ + +.macro UNWIND_HINT_UNDEFINED + UNWIND_HINT type=UNWIND_HINT_TYPE_UNDEFINED +.endm + +.macro UNWIND_HINT_END_OF_STACK + UNWIND_HINT type=UNWIND_HINT_TYPE_END_OF_STACK +.endm + +.macro UNWIND_HINT_REGS + UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_REGS +.endm + +.macro UNWIND_HINT_FUNC + UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_CALL +.endm + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_LOONGARCH_UNWIND_HINTS_H */ diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 6c148ccea674..caf9a0b5e62d 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -3,6 +3,8 @@ # Makefile for the Linux/LoongArch kernel. # +OBJECT_FILES_NON_STANDARD_head.o := y + extra-y := vmlinux.lds obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \ @@ -22,6 +24,7 @@ obj-$(CONFIG_ARCH_STRICT_ALIGN) += unaligned.o CFLAGS_module.o += $(call cc-option,-Wno-override-init,) CFLAGS_syscall.o += $(call cc-option,-Wno-override-init,) +CFLAGS_traps.o += $(call cc-option,-Wno-override-init,) CFLAGS_perf_event.o += $(call cc-option,-Wno-override-init,) ifdef CONFIG_FUNCTION_TRACER @@ -64,6 +67,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o +obj-$(CONFIG_UNWINDER_ORC) += unwind_orc.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_regs.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o diff --git a/arch/loongarch/kernel/entry.S b/arch/loongarch/kernel/entry.S index 1ec8e4c4cc2b..48e7e34e355e 100644 --- a/arch/loongarch/kernel/entry.S +++ b/arch/loongarch/kernel/entry.S @@ -14,11 +14,13 @@ #include #include #include +#include .text .cfi_sections .debug_frame .align 5 SYM_CODE_START(handle_syscall) + UNWIND_HINT_UNDEFINED csrrd t0, PERCPU_BASE_KS la.pcrel t1, kernelsp add.d t1, t1, t0 @@ -57,6 +59,7 @@ SYM_CODE_START(handle_syscall) cfi_st fp, PT_R22 SAVE_STATIC + UNWIND_HINT_REGS #ifdef CONFIG_KGDB li.w t1, CSR_CRMD_WE @@ -75,6 +78,7 @@ SYM_CODE_END(handle_syscall) _ASM_NOKPROBE(handle_syscall) SYM_CODE_START(ret_from_fork) + UNWIND_HINT_REGS bl schedule_tail # a0 = struct task_struct *prev move a0, sp bl syscall_exit_to_user_mode @@ -84,6 +88,7 @@ SYM_CODE_START(ret_from_fork) SYM_CODE_END(ret_from_fork) SYM_CODE_START(ret_from_kernel_thread) + UNWIND_HINT_REGS bl schedule_tail # a0 = struct task_struct *prev move a0, s1 jirl ra, s0, 0 diff --git a/arch/loongarch/kernel/fpu.S b/arch/loongarch/kernel/fpu.S index 4382e36ae3d4..69a85f2479fb 100644 --- a/arch/loongarch/kernel/fpu.S +++ b/arch/loongarch/kernel/fpu.S @@ -15,6 +15,7 @@ #include #include #include +#include #define FPU_REG_WIDTH 8 #define LSX_REG_WIDTH 16 @@ -526,3 +527,9 @@ SYM_FUNC_END(_restore_lasx_context) .L_fpu_fault: li.w a0, -EFAULT # failure jr ra + +#ifdef CONFIG_CPU_HAS_LBT +STACK_FRAME_NON_STANDARD _restore_fp +STACK_FRAME_NON_STANDARD _restore_lsx +STACK_FRAME_NON_STANDARD _restore_lasx +#endif diff --git a/arch/loongarch/kernel/genex.S b/arch/loongarch/kernel/genex.S index 2bb3aa2dcfcb..86d5d90ebefe 100644 --- a/arch/loongarch/kernel/genex.S +++ b/arch/loongarch/kernel/genex.S @@ -32,6 +32,7 @@ SYM_FUNC_START(__arch_cpu_idle) SYM_FUNC_END(__arch_cpu_idle) SYM_CODE_START(handle_vint) + UNWIND_HINT_UNDEFINED BACKUP_T0T1 SAVE_ALL la_abs t1, __arch_cpu_idle @@ -49,6 +50,7 @@ SYM_CODE_START(handle_vint) SYM_CODE_END(handle_vint) SYM_CODE_START(except_vec_cex) + UNWIND_HINT_UNDEFINED b cache_parity_error SYM_CODE_END(except_vec_cex) @@ -67,6 +69,7 @@ SYM_CODE_END(except_vec_cex) .macro BUILD_HANDLER exception handler prep .align 5 SYM_CODE_START(handle_\exception) + UNWIND_HINT_UNDEFINED 666: BACKUP_T0T1 SAVE_ALL @@ -77,7 +80,9 @@ SYM_CODE_END(except_vec_cex) 668: RESTORE_ALL_AND_RET SYM_CODE_END(handle_\exception) + .pushsection ".data", "aw", %progbits SYM_DATA(unwind_hint_\exception, .word 668b - 666b) + .popsection .endm BUILD_HANDLER ade ade badv @@ -94,6 +99,7 @@ SYM_CODE_END(except_vec_cex) BUILD_HANDLER reserved reserved none /* others */ SYM_CODE_START(handle_sys) + UNWIND_HINT_UNDEFINED la_abs t0, handle_syscall jr t0 SYM_CODE_END(handle_sys) diff --git a/arch/loongarch/kernel/lbt.S b/arch/loongarch/kernel/lbt.S index 9c75120a26d8..001f061d226a 100644 --- a/arch/loongarch/kernel/lbt.S +++ b/arch/loongarch/kernel/lbt.S @@ -11,6 +11,7 @@ #include #include #include +#include #define SCR_REG_WIDTH 8 @@ -153,3 +154,5 @@ SYM_FUNC_END(_restore_ftop_context) .L_lbt_fault: li.w a0, -EFAULT # failure jr ra + +STACK_FRAME_NON_STANDARD _restore_ftop_context diff --git a/arch/loongarch/kernel/mcount_dyn.S b/arch/loongarch/kernel/mcount_dyn.S index 482aa553aa2d..0c65cf09110c 100644 --- a/arch/loongarch/kernel/mcount_dyn.S +++ b/arch/loongarch/kernel/mcount_dyn.S @@ -73,6 +73,7 @@ SYM_FUNC_START(ftrace_stub) SYM_FUNC_END(ftrace_stub) SYM_CODE_START(ftrace_common) + UNWIND_HINT_UNDEFINED PTR_ADDI a0, ra, -8 /* arg0: ip */ move a1, t0 /* arg1: parent_ip */ la.pcrel t1, function_trace_op @@ -113,12 +114,14 @@ ftrace_common_return: SYM_CODE_END(ftrace_common) SYM_CODE_START(ftrace_caller) + UNWIND_HINT_UNDEFINED ftrace_regs_entry allregs=0 b ftrace_common SYM_CODE_END(ftrace_caller) #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS SYM_CODE_START(ftrace_regs_caller) + UNWIND_HINT_UNDEFINED ftrace_regs_entry allregs=1 b ftrace_common SYM_CODE_END(ftrace_regs_caller) @@ -126,6 +129,7 @@ SYM_CODE_END(ftrace_regs_caller) #ifdef CONFIG_FUNCTION_GRAPH_TRACER SYM_CODE_START(ftrace_graph_caller) + UNWIND_HINT_UNDEFINED PTR_L a0, sp, PT_ERA PTR_ADDI a0, a0, -8 /* arg0: self_addr */ PTR_ADDI a1, sp, PT_R1 /* arg1: parent */ @@ -134,6 +138,7 @@ SYM_CODE_START(ftrace_graph_caller) SYM_CODE_END(ftrace_graph_caller) SYM_CODE_START(return_to_handler) + UNWIND_HINT_UNDEFINED /* Save return value regs */ PTR_ADDI sp, sp, -FGRET_REGS_SIZE PTR_S a0, sp, FGRET_REGS_A0 @@ -155,6 +160,7 @@ 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 SYM_CODE_END(ftrace_stub_direct_tramp) #endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */ diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c index b13b2858fe39..c7d0338d12c1 100644 --- a/arch/loongarch/kernel/module.c +++ b/arch/loongarch/kernel/module.c @@ -20,6 +20,7 @@ #include #include #include +#include static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top) { @@ -515,15 +516,28 @@ static void module_init_ftrace_plt(const Elf_Ehdr *hdr, int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod) { - const Elf_Shdr *s, *se; const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + const Elf_Shdr *s, *alt = NULL, *orc = NULL, *orc_ip = NULL, *ftrace = NULL; - for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) { + for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { if (!strcmp(".altinstructions", secstrs + s->sh_name)) - apply_alternatives((void *)s->sh_addr, (void *)s->sh_addr + s->sh_size); + alt = s; + if (!strcmp(".orc_unwind", secstrs + s->sh_name)) + orc = s; + if (!strcmp(".orc_unwind_ip", secstrs + s->sh_name)) + orc_ip = s; if (!strcmp(".ftrace_trampoline", secstrs + s->sh_name)) - module_init_ftrace_plt(hdr, s, mod); + ftrace = s; } + if (alt) + apply_alternatives((void *)alt->sh_addr, (void *)alt->sh_addr + alt->sh_size); + + if (orc && orc_ip) + unwind_module_init(mod, (void *)orc_ip->sh_addr, orc_ip->sh_size, (void *)orc->sh_addr, orc->sh_size); + + if (ftrace) + module_init_ftrace_plt(hdr, ftrace, mod); + return 0; } diff --git a/arch/loongarch/kernel/relocate_kernel.S b/arch/loongarch/kernel/relocate_kernel.S index f49f6b053763..84e6de2fd973 100644 --- a/arch/loongarch/kernel/relocate_kernel.S +++ b/arch/loongarch/kernel/relocate_kernel.S @@ -15,6 +15,7 @@ #include SYM_CODE_START(relocate_new_kernel) + UNWIND_HINT_UNDEFINED /* * a0: EFI boot flag for the new kernel * a1: Command line pointer for the new kernel @@ -90,6 +91,7 @@ SYM_CODE_END(relocate_new_kernel) * then start at the entry point from LOONGARCH_IOCSR_MBUF0. */ SYM_CODE_START(kexec_smp_wait) + UNWIND_HINT_UNDEFINED 1: li.w t0, 0x100 /* wait for init loop */ 2: addi.w t0, t0, -1 /* limit mailbox access */ bnez t0, 2b @@ -106,6 +108,5 @@ SYM_CODE_END(kexec_smp_wait) relocate_new_kernel_end: -SYM_DATA_START(relocate_new_kernel_size) - PTR relocate_new_kernel_end - relocate_new_kernel -SYM_DATA_END(relocate_new_kernel_size) + .section ".data" +SYM_DATA(relocate_new_kernel_size, .long relocate_new_kernel_end - relocate_new_kernel) diff --git a/arch/loongarch/kernel/rethook_trampoline.S b/arch/loongarch/kernel/rethook_trampoline.S index bd5772c96338..d4ceb2fa2a5c 100644 --- a/arch/loongarch/kernel/rethook_trampoline.S +++ b/arch/loongarch/kernel/rethook_trampoline.S @@ -76,6 +76,7 @@ .endm SYM_CODE_START(arch_rethook_trampoline) + UNWIND_HINT_UNDEFINED addi.d sp, sp, -PT_SIZE save_all_base_regs diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 6ccffb2ab7c8..7c08b032a7d6 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -48,6 +48,7 @@ #include #include #include +#include #include "legacy_boot.h" #define SMBIOS_BIOSSIZE_OFFSET 0x09 @@ -649,6 +650,7 @@ static void __init prefill_possible_map(void) void __init setup_arch(char **cmdline_p) { cpu_probe(); + unwind_init(); init_environ(); efi_init(); diff --git a/arch/loongarch/kernel/stacktrace.c b/arch/loongarch/kernel/stacktrace.c index f623feb2129f..eaec82e02c92 100644 --- a/arch/loongarch/kernel/stacktrace.c +++ b/arch/loongarch/kernel/stacktrace.c @@ -29,6 +29,7 @@ void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, regs->csr_era = thread_saved_ra(task); } regs->regs[1] = 0; + regs->regs[22] = 0; } for (unwind_start(&state, task, regs); diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c index aebfc3733a76..f9f4eb00c92e 100644 --- a/arch/loongarch/kernel/traps.c +++ b/arch/loongarch/kernel/traps.c @@ -53,6 +53,32 @@ #include "access-helper.h" +void *exception_table[EXCCODE_INT_START] = { + [0 ... EXCCODE_INT_START - 1] = handle_reserved, + + [EXCCODE_TLBI] = handle_tlb_load, + [EXCCODE_TLBL] = handle_tlb_load, + [EXCCODE_TLBS] = handle_tlb_store, + [EXCCODE_TLBM] = handle_tlb_modify, + [EXCCODE_TLBNR] = handle_tlb_protect, + [EXCCODE_TLBNX] = handle_tlb_protect, + [EXCCODE_TLBPE] = handle_tlb_protect, + [EXCCODE_ADE] = handle_ade, + [EXCCODE_ALE] = handle_ale, + [EXCCODE_BCE] = handle_bce, + [EXCCODE_SYS] = handle_sys, + [EXCCODE_BP] = handle_bp, + [EXCCODE_INE] = handle_ri, + [EXCCODE_IPE] = handle_ri, + [EXCCODE_FPDIS] = handle_fpu, + [EXCCODE_LSXDIS] = handle_lsx, + [EXCCODE_LASXDIS] = handle_lasx, + [EXCCODE_FPE] = handle_fpe, + [EXCCODE_WATCH] = handle_watch, + [EXCCODE_BTDIS] = handle_lbt, +}; +EXPORT_SYMBOL_GPL(exception_table); + static void show_backtrace(struct task_struct *task, const struct pt_regs *regs, const char *loglvl, bool user) { @@ -1150,19 +1176,9 @@ void __init trap_init(void) for (i = EXCCODE_INT_START; i <= EXCCODE_INT_END; i++) set_handler(i * VECSIZE, handle_vint, VECSIZE); - set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE); - set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE); - set_handler(EXCCODE_BCE * VECSIZE, handle_bce, VECSIZE); - set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE); - set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE); - set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE); - set_handler(EXCCODE_IPE * VECSIZE, handle_ri, VECSIZE); - set_handler(EXCCODE_FPDIS * VECSIZE, handle_fpu, VECSIZE); - set_handler(EXCCODE_LSXDIS * VECSIZE, handle_lsx, VECSIZE); - set_handler(EXCCODE_LASXDIS * VECSIZE, handle_lasx, VECSIZE); - set_handler(EXCCODE_FPE * VECSIZE, handle_fpe, VECSIZE); - set_handler(EXCCODE_BTDIS * VECSIZE, handle_lbt, VECSIZE); - set_handler(EXCCODE_WATCH * VECSIZE, handle_watch, VECSIZE); + /* Set exception vector handler */ + for (i = EXCCODE_ADE; i <= EXCCODE_BTDIS; i++) + set_handler(i * VECSIZE, exception_table[i], VECSIZE); cache_error_setup(); diff --git a/arch/loongarch/kernel/unwind_orc.c b/arch/loongarch/kernel/unwind_orc.c new file mode 100644 index 000000000000..b25722876331 --- /dev/null +++ b/arch/loongarch/kernel/unwind_orc.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ORC_HEADER; + +#define orc_warn(fmt, ...) \ + printk_deferred_once(KERN_WARNING "WARNING: " fmt, ##__VA_ARGS__) + +extern int __start_orc_unwind_ip[]; +extern int __stop_orc_unwind_ip[]; +extern struct orc_entry __start_orc_unwind[]; +extern struct orc_entry __stop_orc_unwind[]; + +static bool orc_init __ro_after_init; +static unsigned int lookup_num_blocks __ro_after_init; + +/* Fake frame pointer entry -- used as a fallback for generated code */ +static struct orc_entry orc_fp_entry = { + .sp_reg = ORC_REG_FP, + .sp_offset = 16, + .fp_reg = ORC_REG_PREV_SP, + .fp_offset = -16, + .ra_reg = ORC_REG_PREV_SP, + .ra_offset = -8, + .type = ORC_TYPE_CALL +}; + +/* + * If we crash with IP==0, the last successfully executed instruction + * was probably an indirect function call with a NULL function pointer, + * and we don't have unwind information for NULL. + * This hardcoded ORC entry for IP==0 allows us to unwind from a NULL function + * pointer into its parent and then continue normally from there. + */ +static struct orc_entry orc_null_entry = { + .sp_reg = ORC_REG_SP, + .sp_offset = sizeof(long), + .fp_reg = ORC_REG_UNDEFINED, + .type = ORC_TYPE_CALL +}; + +static inline unsigned long orc_ip(const int *ip) +{ + return (unsigned long)ip + *ip; +} + +static struct orc_entry *__orc_find(int *ip_table, struct orc_entry *u_table, + unsigned int num_entries, unsigned long ip) +{ + int *first = ip_table; + int *mid = first, *found = first; + int *last = ip_table + num_entries - 1; + + if (!num_entries) + return NULL; + + /* + * Do a binary range search to find the rightmost duplicate of a given + * starting address. Some entries are section terminators which are + * "weak" entries for ensuring there are no gaps. They should be + * ignored when they conflict with a real entry. + */ + while (first <= last) { + mid = first + ((last - first) / 2); + + if (orc_ip(mid) <= ip) { + found = mid; + first = mid + 1; + } else + last = mid - 1; + } + + return u_table + (found - ip_table); +} + +#ifdef CONFIG_MODULES +static struct orc_entry *orc_module_find(unsigned long ip) +{ + struct module *mod; + + mod = __module_address(ip); + if (!mod || !mod->arch.orc_unwind || !mod->arch.orc_unwind_ip) + return NULL; + + return __orc_find(mod->arch.orc_unwind_ip, mod->arch.orc_unwind, mod->arch.num_orcs, ip); +} +#else +static struct orc_entry *orc_module_find(unsigned long ip) +{ + return NULL; +} +#endif + +#ifdef CONFIG_DYNAMIC_FTRACE +static struct orc_entry *orc_find(unsigned long ip); + +/* + * Ftrace dynamic trampolines do not have orc entries of their own. + * But they are copies of the ftrace entries that are static and + * defined in ftrace_*.S, which do have orc entries. + * + * If the unwinder comes across a ftrace trampoline, then find the + * ftrace function that was used to create it, and use that ftrace + * function's orc entry, as the placement of the return code in + * the stack will be identical. + */ +static struct orc_entry *orc_ftrace_find(unsigned long ip) +{ + struct ftrace_ops *ops; + unsigned long tramp_addr, offset; + + ops = ftrace_ops_trampoline(ip); + if (!ops) + return NULL; + + /* Set tramp_addr to the start of the code copied by the trampoline */ + if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) + tramp_addr = (unsigned long)ftrace_regs_caller; + else + tramp_addr = (unsigned long)ftrace_caller; + + /* Now place tramp_addr to the location within the trampoline ip is at */ + offset = ip - ops->trampoline; + tramp_addr += offset; + + /* Prevent unlikely recursion */ + if (ip == tramp_addr) + return NULL; + + return orc_find(tramp_addr); +} +#else +static struct orc_entry *orc_ftrace_find(unsigned long ip) +{ + return NULL; +} +#endif + +static struct orc_entry *orc_find(unsigned long ip) +{ + static struct orc_entry *orc; + + if (ip == 0) + return &orc_null_entry; + + /* For non-init vmlinux addresses, use the fast lookup table: */ + if (ip >= LOOKUP_START_IP && ip < LOOKUP_STOP_IP) { + unsigned int idx, start, stop; + + idx = (ip - LOOKUP_START_IP) / LOOKUP_BLOCK_SIZE; + + if (unlikely((idx >= lookup_num_blocks-1))) { + orc_warn("WARNING: bad lookup idx: idx=%u num=%u ip=%pB\n", + idx, lookup_num_blocks, (void *)ip); + return NULL; + } + + start = orc_lookup[idx]; + stop = orc_lookup[idx + 1] + 1; + + if (unlikely((__start_orc_unwind + start >= __stop_orc_unwind) || + (__start_orc_unwind + stop > __stop_orc_unwind))) { + orc_warn("WARNING: bad lookup value: idx=%u num=%u start=%u stop=%u ip=%pB\n", + idx, lookup_num_blocks, start, stop, (void *)ip); + return NULL; + } + + return __orc_find(__start_orc_unwind_ip + start, + __start_orc_unwind + start, stop - start, ip); + } + + /* vmlinux .init slow lookup: */ + if (is_kernel_inittext(ip)) + return __orc_find(__start_orc_unwind_ip, __start_orc_unwind, + __stop_orc_unwind_ip - __start_orc_unwind_ip, ip); + + /* Module lookup: */ + orc = orc_module_find(ip); + if (orc) + return orc; + + return orc_ftrace_find(ip); +} + +#ifdef CONFIG_MODULES + +static DEFINE_MUTEX(sort_mutex); +static int *cur_orc_ip_table = __start_orc_unwind_ip; +static struct orc_entry *cur_orc_table = __start_orc_unwind; + +static void orc_sort_swap(void *_a, void *_b, int size) +{ + int delta = _b - _a; + int *a = _a, *b = _b, tmp; + struct orc_entry *orc_a, *orc_b; + + /* Swap the .orc_unwind_ip entries: */ + tmp = *a; + *a = *b + delta; + *b = tmp - delta; + + /* Swap the corresponding .orc_unwind entries: */ + orc_a = cur_orc_table + (a - cur_orc_ip_table); + orc_b = cur_orc_table + (b - cur_orc_ip_table); + swap(*orc_a, *orc_b); +} + +static int orc_sort_cmp(const void *_a, const void *_b) +{ + const int *a = _a, *b = _b; + unsigned long a_val = orc_ip(a); + unsigned long b_val = orc_ip(b); + struct orc_entry *orc_a; + + if (a_val > b_val) + return 1; + if (a_val < b_val) + return -1; + + /* + * The "weak" section terminator entries need to always be first + * to ensure the lookup code skips them in favor of real entries. + * These terminator entries exist to handle any gaps created by + * whitelisted .o files which didn't get objtool generation. + */ + orc_a = cur_orc_table + (a - cur_orc_ip_table); + + return orc_a->type == ORC_TYPE_UNDEFINED ? -1 : 1; +} + +void unwind_module_init(struct module *mod, void *_orc_ip, size_t orc_ip_size, + void *_orc, size_t orc_size) +{ + int *orc_ip = _orc_ip; + struct orc_entry *orc = _orc; + unsigned int num_entries = orc_ip_size / sizeof(int); + + WARN_ON_ONCE(orc_ip_size % sizeof(int) != 0 || + orc_size % sizeof(*orc) != 0 || + num_entries != orc_size / sizeof(*orc)); + + /* + * The 'cur_orc_*' globals allow the orc_sort_swap() callback to + * associate an .orc_unwind_ip table entry with its corresponding + * .orc_unwind entry so they can both be swapped. + */ + mutex_lock(&sort_mutex); + cur_orc_ip_table = orc_ip; + cur_orc_table = orc; + sort(orc_ip, num_entries, sizeof(int), orc_sort_cmp, orc_sort_swap); + mutex_unlock(&sort_mutex); + + mod->arch.orc_unwind_ip = orc_ip; + mod->arch.orc_unwind = orc; + mod->arch.num_orcs = num_entries; +} +#endif + +void __init unwind_init(void) +{ + int i; + size_t orc_size = (void *)__stop_orc_unwind - (void *)__start_orc_unwind; + size_t orc_ip_size = (void *)__stop_orc_unwind_ip - (void *)__start_orc_unwind_ip; + size_t num_entries = orc_ip_size / sizeof(int); + struct orc_entry *orc; + + if (!num_entries || orc_ip_size % sizeof(int) != 0 || + orc_size % sizeof(struct orc_entry) != 0 || + num_entries != orc_size / sizeof(struct orc_entry)) { + orc_warn("WARNING: Bad or missing .orc_unwind table. Disabling unwinder.\n"); + return; + } + + /* + * Note, the orc_unwind and orc_unwind_ip tables were already + * sorted at build time via the 'sorttable' tool. + * It's ready for binary search straight away, no need to sort it. + */ + + /* Initialize the fast lookup table: */ + lookup_num_blocks = orc_lookup_end - orc_lookup; + for (i = 0; i < lookup_num_blocks-1; i++) { + orc = __orc_find(__start_orc_unwind_ip, __start_orc_unwind, + num_entries, LOOKUP_START_IP + (LOOKUP_BLOCK_SIZE * i)); + if (!orc) { + orc_warn("WARNING: Corrupt .orc_unwind table. Disabling unwinder.\n"); + return; + } + + orc_lookup[i] = orc - __start_orc_unwind; + } + + /* Initialize the ending block: */ + orc = __orc_find(__start_orc_unwind_ip, __start_orc_unwind, num_entries, LOOKUP_STOP_IP); + if (!orc) { + orc_warn("WARNING: Corrupt .orc_unwind table. Disabling unwinder.\n"); + return; + } + orc_lookup[lookup_num_blocks-1] = orc - __start_orc_unwind; + + orc_init = true; +} + +static inline bool on_stack(struct stack_info *info, unsigned long addr, size_t len) +{ + unsigned long begin = info->begin; + unsigned long end = info->end; + + return (info->type != STACK_TYPE_UNKNOWN && + addr >= begin && addr < end && addr + len > begin && addr + len <= end); +} + +static bool stack_access_ok(struct unwind_state *state, unsigned long addr, size_t len) +{ + struct stack_info *info = &state->stack_info; + + if (on_stack(info, addr, len)) + return true; + + return !get_stack_info(addr, state->task, info) && on_stack(info, addr, len); +} + +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_ORC; + if (!unwind_done(state) && !__kernel_text_address(state->pc)) + unwind_next_frame(state); +} +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; + unsigned long offset = (ra - eentry) % VECSIZE; + + switch (type) { + case 0 ... EXCCODE_INT_START - 1: + func = (unsigned long)exception_table[type]; + break; + case EXCCODE_INT_START ... EXCCODE_INT_END: + func = (unsigned long)handle_vint; + break; + default: + func = (unsigned long)handle_reserved; + break; + } + + return func + offset; + } + + return ra; +} + +bool unwind_next_frame(struct unwind_state *state) +{ + unsigned long *p, pc; + struct pt_regs *regs; + struct orc_entry *orc; + struct stack_info *info = &state->stack_info; + + if (unwind_done(state)) + return false; + + /* Don't let modules unload while we're reading their ORC data. */ + preempt_disable(); + + if (is_entry_func(state->pc)) + goto end; + + orc = orc_find(state->pc); + if (!orc) { + /* + * As a fallback, try to assume this code uses a frame pointer. + * This is useful for generated code, like BPF, which ORC + * doesn't know about. This is just a guess, so the rest of + * the unwind is no longer considered reliable. + */ + orc = &orc_fp_entry; + state->error = true; + } else { + if (orc->type == ORC_TYPE_UNDEFINED) + goto err; + + if (orc->type == ORC_TYPE_END_OF_STACK) + goto end; + } + + switch (orc->sp_reg) { + case ORC_REG_SP: + if (info->type == STACK_TYPE_IRQ && state->sp == info->end) + orc->type = ORC_TYPE_REGS; + else + state->sp = state->sp + orc->sp_offset; + break; + case ORC_REG_FP: + state->sp = state->fp; + break; + default: + orc_warn("unknown SP base reg %d at %pB\n", orc->sp_reg, (void *)state->pc); + goto err; + } + + switch (orc->fp_reg) { + case ORC_REG_PREV_SP: + p = (unsigned long *)(state->sp + orc->fp_offset); + if (!stack_access_ok(state, (unsigned long)p, sizeof(unsigned long))) + goto err; + + state->fp = *p; + break; + case ORC_REG_UNDEFINED: + /* Nothing. */ + break; + default: + orc_warn("unknown FP base reg %d at %pB\n", orc->fp_reg, (void *)state->pc); + goto err; + } + + switch (orc->type) { + case ORC_TYPE_CALL: + if (orc->ra_reg == ORC_REG_PREV_SP) { + p = (unsigned long *)(state->sp + orc->ra_offset); + if (!stack_access_ok(state, (unsigned long)p, sizeof(unsigned long))) + goto err; + + pc = unwind_graph_addr(state, *p, state->sp); + pc -= LOONGARCH_INSN_SIZE; + } else if (orc->ra_reg == ORC_REG_UNDEFINED) { + if (!state->ra || state->ra == state->pc) + goto err; + + pc = unwind_graph_addr(state, state->ra, state->sp); + pc -= LOONGARCH_INSN_SIZE; + state->ra = 0; + } else { + orc_warn("unknown ra base reg %d at %pB\n", orc->ra_reg, (void *)state->pc); + goto err; + } + break; + case ORC_TYPE_REGS: + if (info->type == STACK_TYPE_IRQ && state->sp == info->end) + regs = (struct pt_regs *)info->next_sp; + else + regs = (struct pt_regs *)state->sp; + + if (!stack_access_ok(state, (unsigned long)regs, sizeof(*regs))) + goto err; + + if ((info->end == (unsigned long)regs + sizeof(*regs)) && + !regs->regs[3] && !regs->regs[1]) + goto end; + + if (user_mode(regs)) + goto end; + + pc = regs->csr_era; + if (!__kernel_text_address(pc)) + goto err; + + state->sp = regs->regs[3]; + state->ra = regs->regs[1]; + state->fp = regs->regs[22]; + get_stack_info(state->sp, state->task, info); + + break; + default: + orc_warn("unknown .orc_unwind entry type %d at %pB\n", orc->type, (void *)state->pc); + goto err; + } + + state->pc = bt_address(pc); + if (!state->pc) { + pr_err("cannot find unwind pc at %pK\n", (void *)pc); + goto err; + } + + if (!__kernel_text_address(state->pc)) + goto err; + + preempt_enable(); + return true; + +err: + state->error = true; + +end: + preempt_enable(); + state->stack_info.type = STACK_TYPE_UNKNOWN; + return false; +} +EXPORT_SYMBOL_GPL(unwind_next_frame); diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S index d5afd0c80a49..3c7595342730 100644 --- a/arch/loongarch/kernel/vmlinux.lds.S +++ b/arch/loongarch/kernel/vmlinux.lds.S @@ -2,6 +2,7 @@ #include #include #include +#include #define PAGE_SIZE _PAGE_SIZE #define RO_EXCEPTION_TABLE_ALIGN 4 @@ -123,6 +124,8 @@ SECTIONS } #endif + ORC_UNWIND_TABLE + .sdata : { *(.sdata) } diff --git a/arch/loongarch/kvm/switch.S b/arch/loongarch/kvm/switch.S index 3634431db18a..80e988985a6a 100644 --- a/arch/loongarch/kvm/switch.S +++ b/arch/loongarch/kvm/switch.S @@ -8,7 +8,7 @@ #include #include #include -#include +#include #define HGPR_OFFSET(x) (PT_R0 + 8*x) #define GGPR_OFFSET(x) (KVM_ARCH_GGPR + 8*x) @@ -112,6 +112,7 @@ .text .cfi_sections .debug_frame SYM_CODE_START(kvm_exc_entry) + UNWIND_HINT_UNDEFINED csrwr a2, KVM_TEMP_KS csrrd a2, KVM_VCPU_KS addi.d a2, a2, KVM_VCPU_ARCH @@ -273,3 +274,9 @@ SYM_FUNC_END(kvm_restore_lasx) .section ".rodata" SYM_DATA(kvm_exception_size, .quad kvm_exc_entry_end - kvm_exc_entry) SYM_DATA(kvm_enter_guest_size, .quad kvm_enter_guest_end - kvm_enter_guest) + +#ifdef CONFIG_CPU_HAS_LBT +STACK_FRAME_NON_STANDARD kvm_restore_fpu +STACK_FRAME_NON_STANDARD kvm_restore_lsx +STACK_FRAME_NON_STANDARD kvm_restore_lasx +#endif diff --git a/arch/loongarch/lib/clear_user.S b/arch/loongarch/lib/clear_user.S index be741544e62b..7a0db643b286 100644 --- a/arch/loongarch/lib/clear_user.S +++ b/arch/loongarch/lib/clear_user.S @@ -10,6 +10,7 @@ #include #include #include +#include SYM_FUNC_START(__clear_user) /* @@ -204,3 +205,5 @@ SYM_FUNC_START(__clear_user_fast) _asm_extable 28b, .Lsmall_fixup _asm_extable 29b, .Lexit SYM_FUNC_END(__clear_user_fast) + +STACK_FRAME_NON_STANDARD __clear_user_fast diff --git a/arch/loongarch/lib/copy_user.S b/arch/loongarch/lib/copy_user.S index feec3d362803..095ce9181c6c 100644 --- a/arch/loongarch/lib/copy_user.S +++ b/arch/loongarch/lib/copy_user.S @@ -10,6 +10,7 @@ #include #include #include +#include SYM_FUNC_START(__copy_user) /* @@ -278,3 +279,5 @@ SYM_FUNC_START(__copy_user_fast) _asm_extable 58b, .Lexit _asm_extable 59b, .Lexit SYM_FUNC_END(__copy_user_fast) + +STACK_FRAME_NON_STANDARD __copy_user_fast diff --git a/arch/loongarch/lib/memcpy.S b/arch/loongarch/lib/memcpy.S index fa1148878d2b..9517a2f961af 100644 --- a/arch/loongarch/lib/memcpy.S +++ b/arch/loongarch/lib/memcpy.S @@ -9,6 +9,7 @@ #include #include #include +#include .section .noinstr.text, "ax" @@ -197,3 +198,5 @@ SYM_FUNC_START(__memcpy_fast) jr ra SYM_FUNC_END(__memcpy_fast) _ASM_NOKPROBE(__memcpy_fast) + +STACK_FRAME_NON_STANDARD __memcpy_small diff --git a/arch/loongarch/lib/memset.S b/arch/loongarch/lib/memset.S index 06d3ca54cbfe..df3846620553 100644 --- a/arch/loongarch/lib/memset.S +++ b/arch/loongarch/lib/memset.S @@ -9,6 +9,7 @@ #include #include #include +#include .macro fill_to_64 r0 bstrins.d \r0, \r0, 15, 8 @@ -166,3 +167,5 @@ SYM_FUNC_START(__memset_fast) jr ra SYM_FUNC_END(__memset_fast) _ASM_NOKPROBE(__memset_fast) + +STACK_FRAME_NON_STANDARD __memset_fast diff --git a/arch/loongarch/mm/tlb.c b/arch/loongarch/mm/tlb.c index 56bf1dd5358a..5fcda8adf90d 100644 --- a/arch/loongarch/mm/tlb.c +++ b/arch/loongarch/mm/tlb.c @@ -9,8 +9,9 @@ #include #include -#include #include +#include +#include #include #include #include @@ -266,24 +267,20 @@ static void setup_tlb_handler(int cpu) setup_ptwalker(); local_flush_tlb_all(); + if (cpu_has_ptw) { + exception_table[EXCCODE_TLBI] = handle_tlb_load_ptw; + exception_table[EXCCODE_TLBL] = handle_tlb_load_ptw; + exception_table[EXCCODE_TLBS] = handle_tlb_store_ptw; + exception_table[EXCCODE_TLBM] = handle_tlb_modify_ptw; + } + /* The tlb handlers are generated only once */ if (cpu == 0) { memcpy((void *)tlbrentry, handle_tlb_refill, 0x80); local_flush_icache_range(tlbrentry, tlbrentry + 0x80); - if (!cpu_has_ptw) { - set_handler(EXCCODE_TLBI * VECSIZE, handle_tlb_load, VECSIZE); - set_handler(EXCCODE_TLBL * VECSIZE, handle_tlb_load, VECSIZE); - set_handler(EXCCODE_TLBS * VECSIZE, handle_tlb_store, VECSIZE); - set_handler(EXCCODE_TLBM * VECSIZE, handle_tlb_modify, VECSIZE); - } else { - set_handler(EXCCODE_TLBI * VECSIZE, handle_tlb_load_ptw, VECSIZE); - set_handler(EXCCODE_TLBL * VECSIZE, handle_tlb_load_ptw, VECSIZE); - set_handler(EXCCODE_TLBS * VECSIZE, handle_tlb_store_ptw, VECSIZE); - set_handler(EXCCODE_TLBM * VECSIZE, handle_tlb_modify_ptw, VECSIZE); - } - set_handler(EXCCODE_TLBNR * VECSIZE, handle_tlb_protect, VECSIZE); - set_handler(EXCCODE_TLBNX * VECSIZE, handle_tlb_protect, VECSIZE); - set_handler(EXCCODE_TLBPE * VECSIZE, handle_tlb_protect, VECSIZE); + + for (int i = EXCCODE_TLBL; i <= EXCCODE_TLBPE; i++) + set_handler(i * VECSIZE, exception_table[i], VECSIZE); } else { int vec_sz __maybe_unused; void *addr __maybe_unused; diff --git a/arch/loongarch/mm/tlbex.S b/arch/loongarch/mm/tlbex.S index d5d682f3d29f..a44387b838af 100644 --- a/arch/loongarch/mm/tlbex.S +++ b/arch/loongarch/mm/tlbex.S @@ -18,6 +18,7 @@ .macro tlb_do_page_fault, write SYM_CODE_START(tlb_do_page_fault_\write) + UNWIND_HINT_UNDEFINED SAVE_ALL csrrd a2, LOONGARCH_CSR_BADV move a0, sp @@ -32,6 +33,7 @@ tlb_do_page_fault 1 SYM_CODE_START(handle_tlb_protect) + UNWIND_HINT_UNDEFINED BACKUP_T0T1 SAVE_ALL move a0, sp @@ -44,6 +46,7 @@ SYM_CODE_START(handle_tlb_protect) SYM_CODE_END(handle_tlb_protect) SYM_CODE_START(handle_tlb_load) + UNWIND_HINT_UNDEFINED csrwr t0, EXCEPTION_KS0 csrwr t1, EXCEPTION_KS1 csrwr ra, EXCEPTION_KS2 @@ -190,6 +193,7 @@ nopage_tlb_load: SYM_CODE_END(handle_tlb_load) SYM_CODE_START(handle_tlb_load_ptw) + UNWIND_HINT_UNDEFINED csrwr t0, LOONGARCH_CSR_KS0 csrwr t1, LOONGARCH_CSR_KS1 la_abs t0, tlb_do_page_fault_0 @@ -197,6 +201,7 @@ SYM_CODE_START(handle_tlb_load_ptw) SYM_CODE_END(handle_tlb_load_ptw) SYM_CODE_START(handle_tlb_store) + UNWIND_HINT_UNDEFINED csrwr t0, EXCEPTION_KS0 csrwr t1, EXCEPTION_KS1 csrwr ra, EXCEPTION_KS2 @@ -346,6 +351,7 @@ nopage_tlb_store: SYM_CODE_END(handle_tlb_store) SYM_CODE_START(handle_tlb_store_ptw) + UNWIND_HINT_UNDEFINED csrwr t0, LOONGARCH_CSR_KS0 csrwr t1, LOONGARCH_CSR_KS1 la_abs t0, tlb_do_page_fault_1 @@ -353,6 +359,7 @@ SYM_CODE_START(handle_tlb_store_ptw) SYM_CODE_END(handle_tlb_store_ptw) SYM_CODE_START(handle_tlb_modify) + UNWIND_HINT_UNDEFINED csrwr t0, EXCEPTION_KS0 csrwr t1, EXCEPTION_KS1 csrwr ra, EXCEPTION_KS2 @@ -500,6 +507,7 @@ nopage_tlb_modify: SYM_CODE_END(handle_tlb_modify) SYM_CODE_START(handle_tlb_modify_ptw) + UNWIND_HINT_UNDEFINED csrwr t0, LOONGARCH_CSR_KS0 csrwr t1, LOONGARCH_CSR_KS1 la_abs t0, tlb_do_page_fault_1 @@ -507,6 +515,7 @@ SYM_CODE_START(handle_tlb_modify_ptw) SYM_CODE_END(handle_tlb_modify_ptw) SYM_CODE_START(handle_tlb_refill) + UNWIND_HINT_UNDEFINED csrwr t0, LOONGARCH_CSR_TLBRSAVE csrrd t0, LOONGARCH_CSR_PGD lddir t0, t0, 3 diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile index f597cd08a96b..75c6726382c3 100644 --- a/arch/loongarch/vdso/Makefile +++ b/arch/loongarch/vdso/Makefile @@ -4,6 +4,7 @@ KASAN_SANITIZE := n UBSAN_SANITIZE := n KCOV_INSTRUMENT := n +OBJECT_FILES_NON_STANDARD := y # Include the generic Makefile to check the built vdso. include $(srctree)/lib/vdso/Makefile diff --git a/include/linux/compiler.h b/include/linux/compiler.h index d7779a18b24f..df29ddb3c862 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -116,6 +116,14 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, */ #define __stringify_label(n) #n +#define __annotate_reachable(c) ({ \ + asm volatile(__stringify_label(c) ":\n\t" \ + ".pushsection .discard.reachable\n\t" \ + ".long " __stringify_label(c) "b - .\n\t" \ + ".popsection\n\t"); \ +}) +#define annotate_reachable() __annotate_reachable(__COUNTER__) + #define __annotate_unreachable(c) ({ \ asm volatile(__stringify_label(c) ":\n\t" \ ".pushsection .discard.unreachable\n\t" \ @@ -128,6 +136,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, #define __annotate_jump_table __section(".rodata..c_jump_table") #else /* !CONFIG_OBJTOOL */ +#define annotate_reachable() #define annotate_unreachable() #define __annotate_jump_table #endif /* CONFIG_OBJTOOL */ diff --git a/scripts/Makefile b/scripts/Makefile index 576cf64be667..e4cca53d2285 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -31,9 +31,12 @@ HOSTLDLIBS_sign-file = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null | ifdef CONFIG_UNWINDER_ORC ifeq ($(ARCH),x86_64) -ARCH := x86 +SRCARCH := x86 endif -HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/x86/include +ifeq ($(ARCH),loongarch) +SRCARCH := loongarch +endif +HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/$(SRCARCH)/include HOSTCFLAGS_sorttable.o += -DUNWINDER_ORC_ENABLED endif From 2d905626cad3580b9e26f91185a3c6449eafb17d Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Wed, 17 Jan 2024 12:43:08 +0800 Subject: [PATCH 08/23] LoongArch: Fix definition of ftrace_regs_set_instruction_pointer() commit 91af17cd7d03db8836554c91ba7c38b0817aa980 upstream. The current definition of ftrace_regs_set_instruction_pointer() is not correct. Obviously, this function is used to set instruction pointer but not return value, so it should call instruction_pointer_set() instead of regs_set_return_value(). There is no side effect by now because it is only used for kernel live- patching which is not supported, so fix it to avoid failure when testing livepatch in the future. Fixes: 6fbff14a6382 ("LoongArch: ftrace: Abstract DYNAMIC_FTRACE_WITH_ARGS accesses") Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- arch/loongarch/include/asm/ftrace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h index a11996eb5892..de891c2c83d4 100644 --- a/arch/loongarch/include/asm/ftrace.h +++ b/arch/loongarch/include/asm/ftrace.h @@ -63,7 +63,7 @@ ftrace_regs_get_instruction_pointer(struct ftrace_regs *fregs) static __always_inline void ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip) { - regs_set_return_value(&fregs->regs, ip); + instruction_pointer_set(&fregs->regs, ip); } #define ftrace_regs_get_argument(fregs, n) \ From 53d2b63952341430bd483340117c65cbbf390fd8 Mon Sep 17 00:00:00 2001 From: Jinyang He Date: Mon, 11 Mar 2024 22:23:47 +0800 Subject: [PATCH 09/23] LoongArch: Add kernel livepatching support commit 199cc14cb4f1cb8668be45f67af41755ed5f0175 upstream. The arch-specified function ftrace_regs_set_instruction_pointer() has been implemented in arch/loongarch/include/asm/ftrace.h, so here only implement arch_stack_walk_reliable() function. Here are the test logs: [root@linux fedora]# cat /proc/cmdline BOOT_IMAGE=/vmlinuz-6.8.0-rc2 root=/dev/sda3 [root@linux fedora]# modprobe livepatch-sample [root@linux fedora]# cat /proc/cmdline this has been live patched [root@linux fedora]# echo 0 > /sys/kernel/livepatch/livepatch_sample/enabled [root@linux fedora]# rmmod livepatch_sample [root@linux fedora]# cat /proc/cmdline BOOT_IMAGE=/vmlinuz-6.8.0-rc2 root=/dev/sda3 [root@linux fedora]# dmesg -t | tail -5 livepatch: enabling patch 'livepatch_sample' livepatch: 'livepatch_sample': starting patching transition livepatch: 'livepatch_sample': patching complete livepatch: 'livepatch_sample': starting unpatching transition livepatch: 'livepatch_sample': unpatching complete Signed-off-by: Jinyang He Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- arch/loongarch/Kconfig | 4 +++ arch/loongarch/include/asm/thread_info.h | 2 ++ arch/loongarch/kernel/stacktrace.c | 40 ++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 2a25bf82b769..0dab7540a633 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -133,6 +133,7 @@ config LOONGARCH select HAVE_KPROBES_ON_FTRACE select HAVE_KRETPROBES select HAVE_KVM + select HAVE_LIVEPATCH select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS @@ -141,6 +142,7 @@ config LOONGARCH select HAVE_PERF_REGS select HAVE_PERF_USER_STACK_DUMP select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_RELIABLE_STACKTRACE if UNWINDER_ORC select HAVE_RETHOOK select HAVE_RSEQ select HAVE_SAMPLE_FTRACE_DIRECT @@ -641,6 +643,8 @@ config PARAVIRT_TIME_ACCOUNTING If in doubt, say N here. +source "kernel/livepatch/Kconfig" + endmenu config ARCH_SELECT_MEMORY_MODEL diff --git a/arch/loongarch/include/asm/thread_info.h b/arch/loongarch/include/asm/thread_info.h index 8cb653d49a54..8bf0e6f51546 100644 --- a/arch/loongarch/include/asm/thread_info.h +++ b/arch/loongarch/include/asm/thread_info.h @@ -86,6 +86,7 @@ register unsigned long current_stack_pointer __asm__("$sp"); #define TIF_LASX_CTX_LIVE 18 /* LASX context must be preserved */ #define TIF_USEDLBT 19 /* LBT was used by this task this quantum (SMP) */ #define TIF_LBT_CTX_LIVE 20 /* LBT context must be preserved */ +#define TIF_PATCH_PENDING 21 /* pending live patching update */ #define _TIF_SIGPENDING (1<regs[3] = (unsigned long)__builtin_frame_address(0); + regs->csr_era = (unsigned long)__builtin_return_address(0); + } else { + regs->regs[3] = thread_saved_fp(task); + regs->csr_era = thread_saved_ra(task); + } + regs->regs[1] = 0; + regs->regs[22] = 0; + + for (unwind_start(&state, task, regs); + !unwind_done(&state) && !unwind_error(&state); unwind_next_frame(&state)) { + addr = unwind_get_return_address(&state); + + /* + * A NULL or invalid return address probably means there's some + * generated code which __kernel_text_address() doesn't know about. + */ + if (!addr) + return -EINVAL; + + if (!consume_entry(cookie, addr)) + return -EINVAL; + } + + /* Check for stack corruption */ + if (unwind_error(&state)) + return -EINVAL; + + return 0; +} + static int copy_stack_frame(unsigned long fp, struct stack_frame *frame) { From c681137e0e8c76edeebcde31c5001105504eefeb Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Fri, 21 Jun 2024 10:18:40 +0800 Subject: [PATCH 10/23] LoongArch: Only allow OBJTOOL & ORC unwinder if toolchain supports -mthin-add-sub commit 120dd4118e58dbda2ddb1dcf55f3c56cdfe8cee0 upstream. GAS <= 2.41 does not support generating R_LARCH_{32,64}_PCREL for "label - ." and it generates R_LARCH_{ADD,SUB}{32,64} pairs instead. Objtool cannot handle R_LARCH_{ADD,SUB}{32,64} pair in __jump_table (static key implementation) and etc. so it will produce some warnings. This is causing the kernel CI systems to complain everywhere. For GAS we can check if -mthin-add-sub option is available to know if R_LARCH_{32,64}_PCREL are supported. For Clang, we require Clang >= 18 and Clang >= 17 already supports R_LARCH_{32,64}_PCREL. But unfortunately Clang has some other issues, so we disable objtool for Clang at present. Note that __jump_table here is not generated by the compiler, so -fno-jump-table is completely irrelevant for this issue. Fixes: cb8a2ef0848c ("LoongArch: Add ORC stack unwinder support") Closes: https://lore.kernel.org/loongarch/Zl5m1ZlVmGKitAof@yujie-X299/ Closes: https://lore.kernel.org/loongarch/ZlY1gDDPi_mNrwJ1@slm.duckdns.org/ Closes: https://lore.kernel.org/loongarch/1717478006.038663-1-hengqi@linux.alibaba.com/ Link: https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=816029e06768 Link: https://github.com/llvm/llvm-project/commit/42cb3c6346fc Signed-off-by: Xi Ruoyao Signed-off-by: Huacai Chen Signed-off-by: Tiezhu Yang --- arch/loongarch/Kconfig | 5 ++++- arch/loongarch/Kconfig.debug | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 0dab7540a633..cf36ce211d2b 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -136,7 +136,7 @@ config LOONGARCH select HAVE_LIVEPATCH select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI - select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS + select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS && AS_HAS_THIN_ADD_SUB && !CC_IS_CLANG select HAVE_PCI select HAVE_PERF_EVENTS select HAVE_PERF_REGS @@ -263,6 +263,9 @@ config AS_HAS_EXPLICIT_RELOCS config AS_HAS_FCSR_CLASS def_bool $(as-instr,movfcsr2gr \$t0$(comma)\$fcsr0) +config AS_HAS_THIN_ADD_SUB + def_bool $(cc-option,-Wa$(comma)-mthin-add-sub) + config AS_HAS_LSX_EXTENSION def_bool $(as-instr,vld \$vr0$(comma)\$a0$(comma)0) diff --git a/arch/loongarch/Kconfig.debug b/arch/loongarch/Kconfig.debug index 98d60630c3d4..8b2ce5b5d43e 100644 --- a/arch/loongarch/Kconfig.debug +++ b/arch/loongarch/Kconfig.debug @@ -28,6 +28,7 @@ config UNWINDER_PROLOGUE config UNWINDER_ORC bool "ORC unwinder" + depends on HAVE_OBJTOOL select OBJTOOL help This option enables the ORC (Oops Rewind Capability) unwinder for From 65294a2d3eb4755a48178b5cbafbf98f336ac24b Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 26 Aug 2024 23:11:32 +0800 Subject: [PATCH 11/23] LoongArch: Add ifdefs to fix LSX and LASX related warnings commit 80376323e2b6a4559f86b2b4d864848ac25cb054 upstream. There exist some warnings when building kernel if CONFIG_CPU_HAS_LBT is set but CONFIG_CPU_HAS_LSX and CONFIG_CPU_HAS_LASX are not set. In this case, there are no definitions of _restore_lsx & _restore_lasx and there are also no definitions of kvm_restore_lsx & kvm_restore_lasx in fpu.S and switch.S respectively, just add some ifdefs to fix these warnings. AS arch/loongarch/kernel/fpu.o arch/loongarch/kernel/fpu.o: warning: objtool: unexpected relocation symbol type in .rela.discard.func_stack_frame_non_standard: 0 arch/loongarch/kernel/fpu.o: warning: objtool: unexpected relocation symbol type in .rela.discard.func_stack_frame_non_standard: 0 AS [M] arch/loongarch/kvm/switch.o arch/loongarch/kvm/switch.o: warning: objtool: unexpected relocation symbol type in .rela.discard.func_stack_frame_non_standard: 0 arch/loongarch/kvm/switch.o: warning: objtool: unexpected relocation symbol type in .rela.discard.func_stack_frame_non_standard: 0 MODPOST Module.symvers ERROR: modpost: "kvm_restore_lsx" [arch/loongarch/kvm/kvm.ko] undefined! ERROR: modpost: "kvm_restore_lasx" [arch/loongarch/kvm/kvm.ko] undefined! Cc: stable@vger.kernel.org # 6.9+ Fixes: cb8a2ef0848c ("LoongArch: Add ORC stack unwinder support") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202408120955.qls5oNQY-lkp@intel.com/ Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- arch/loongarch/kernel/fpu.S | 4 ++++ arch/loongarch/kvm/switch.S | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/arch/loongarch/kernel/fpu.S b/arch/loongarch/kernel/fpu.S index 69a85f2479fb..6ab640101457 100644 --- a/arch/loongarch/kernel/fpu.S +++ b/arch/loongarch/kernel/fpu.S @@ -530,6 +530,10 @@ SYM_FUNC_END(_restore_lasx_context) #ifdef CONFIG_CPU_HAS_LBT STACK_FRAME_NON_STANDARD _restore_fp +#ifdef CONFIG_CPU_HAS_LSX STACK_FRAME_NON_STANDARD _restore_lsx +#endif +#ifdef CONFIG_CPU_HAS_LASX STACK_FRAME_NON_STANDARD _restore_lasx #endif +#endif diff --git a/arch/loongarch/kvm/switch.S b/arch/loongarch/kvm/switch.S index 80e988985a6a..0c292f818492 100644 --- a/arch/loongarch/kvm/switch.S +++ b/arch/loongarch/kvm/switch.S @@ -277,6 +277,10 @@ SYM_DATA(kvm_enter_guest_size, .quad kvm_enter_guest_end - kvm_enter_guest) #ifdef CONFIG_CPU_HAS_LBT STACK_FRAME_NON_STANDARD kvm_restore_fpu +#ifdef CONFIG_CPU_HAS_LSX STACK_FRAME_NON_STANDARD kvm_restore_lsx +#endif +#ifdef CONFIG_CPU_HAS_LASX STACK_FRAME_NON_STANDARD kvm_restore_lasx #endif +#endif From 83c79d19b0ab2be2472e94b852ed2989b9bd29e2 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 17 Sep 2024 22:23:09 +0800 Subject: [PATCH 12/23] objtool: Handle frame pointer related instructions commit da5b2ad1c2f18834cb1ce429e2e5a5cf5cbdf21b upstream. After commit a0f7085f6a63 ("LoongArch: Add RANDOMIZE_KSTACK_OFFSET support"), there are three new instructions "addi.d $fp, $sp, 32", "sub.d $sp, $sp, $t0" and "addi.d $sp, $fp, -32" for the secondary stack in do_syscall(), then there is a objtool warning "return with modified stack frame" and no handle_syscall() which is the previous frame of do_syscall() in the call trace when executing the command "echo l > /proc/sysrq-trigger". objdump shows something like this: 0000000000000000 : 0: 02ff8063 addi.d $sp, $sp, -32 4: 29c04076 st.d $fp, $sp, 16 8: 29c02077 st.d $s0, $sp, 8 c: 29c06061 st.d $ra, $sp, 24 10: 02c08076 addi.d $fp, $sp, 32 ... 74: 0011b063 sub.d $sp, $sp, $t0 ... a8: 4c000181 jirl $ra, $t0, 0 ... dc: 02ff82c3 addi.d $sp, $fp, -32 e0: 28c06061 ld.d $ra, $sp, 24 e4: 28c04076 ld.d $fp, $sp, 16 e8: 28c02077 ld.d $s0, $sp, 8 ec: 02c08063 addi.d $sp, $sp, 32 f0: 4c000020 jirl $zero, $ra, 0 The instruction "sub.d $sp, $sp, $t0" changes the stack bottom and the new stack size is a random value, in order to find the return address of do_syscall() which is stored in the original stack frame after executing "jirl $ra, $t0, 0", it should use fp which points to the original stack top. At the beginning, the thought is tended to decode the secondary stack instruction "sub.d $sp, $sp, $t0" and set it as a label, then check this label for the two frame pointer instructions to change the cfa base and cfa offset during the period of secondary stack in update_cfi_state(). This is valid for GCC but invalid for Clang due to there are different secondary stack instructions for ClangBuiltLinux on LoongArch, something like this: 0000000000000000 : ... 88: 00119064 sub.d $a0, $sp, $a0 8c: 00150083 or $sp, $a0, $zero ... Actually, it equals to a single instruction "sub.d $sp, $sp, $a0", but there is no proper condition to check it as a label like GCC, and so the beginning thought is not a good way. Essentially, there are two special frame pointer instructions which are "addi.d $fp, $sp, imm" and "addi.d $sp, $fp, imm", the first one points fp to the original stack top and the second one restores the original stack bottom from fp. Based on the above analysis, in order to avoid adding an arch-specific update_cfi_state(), we just add a member "frame_pointer" in the "struct symbol" as a label to avoid affecting the current normal case, then set it as true only if there is "addi.d $sp, $fp, imm". The last is to check this label for the two frame pointer instructions to change the cfa base and cfa offset in update_cfi_state(). Tested with the following two configs: (1) CONFIG_RANDOMIZE_KSTACK_OFFSET=y && CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=n (2) CONFIG_RANDOMIZE_KSTACK_OFFSET=y && CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y By the way, there is no effect for x86 with this patch, tested on the x86 machine with Fedora 40 system. Cc: stable@vger.kernel.org # 6.9+ Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- tools/objtool/arch/loongarch/decode.c | 11 ++++++++++- tools/objtool/check.c | 23 ++++++++++++++++++++--- tools/objtool/include/objtool/elf.h | 1 + 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c index aee479d2191c..69b66994f2a1 100644 --- a/tools/objtool/arch/loongarch/decode.c +++ b/tools/objtool/arch/loongarch/decode.c @@ -122,7 +122,7 @@ static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst, switch (inst.reg2i12_format.opcode) { case addid_op: if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) { - /* addi.d sp,sp,si12 or addi.d fp,sp,si12 */ + /* addi.d sp,sp,si12 or addi.d fp,sp,si12 or addi.d sp,fp,si12 */ insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); ADD_OP(op) { op->src.type = OP_SRC_ADD; @@ -132,6 +132,15 @@ static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst, op->dest.reg = inst.reg2i12_format.rd; } } + if ((inst.reg2i12_format.rd == CFI_SP) && (inst.reg2i12_format.rj == CFI_FP)) { + /* addi.d sp,fp,si12 */ + struct symbol *func = find_func_containing(insn->sec, insn->offset); + + if (!func) + return false; + + func->frame_pointer = true; + } break; case ldd_op: if (inst.reg2i12_format.rj == CFI_SP) { diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 05a6335116c9..665d0c954e0d 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -2975,10 +2975,27 @@ static int update_cfi_state(struct instruction *insn, break; } - if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) { + if (op->dest.reg == CFI_BP && op->src.reg == CFI_SP && + insn->sym->frame_pointer) { + /* addi.d fp,sp,imm on LoongArch */ + if (cfa->base == CFI_SP && cfa->offset == op->src.offset) { + cfa->base = CFI_BP; + cfa->offset = 0; + } + break; + } - /* lea disp(%rbp), %rsp */ - cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset); + if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) { + /* addi.d sp,fp,imm on LoongArch */ + if (cfa->base == CFI_BP && cfa->offset == 0) { + if (insn->sym->frame_pointer) { + cfa->base = CFI_SP; + cfa->offset = -op->src.offset; + } + } else { + /* lea disp(%rbp), %rsp */ + cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset); + } break; } diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index 2b8a69de4db8..d7e815c2fd15 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -68,6 +68,7 @@ struct symbol { u8 warned : 1; u8 embedded_insn : 1; u8 local_label : 1; + u8 frame_pointer : 1; struct list_head pv_target; struct reloc *relocs; }; From 8c47f2b29a43d9fb9daa15bb00a34acf65ba6727 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 17 Sep 2024 22:23:09 +0800 Subject: [PATCH 13/23] LoongArch: Enable objtool for Clang commit b8468bd92ae19939d4844899fa05147888732519 upstream. For now, it can enable objtool for Clang, just remove !CC_IS_CLANG for HAVE_OBJTOOL in arch/loongarch/Kconfig. Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- arch/loongarch/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index cf36ce211d2b..7fd6b9168f81 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -136,7 +136,7 @@ config LOONGARCH select HAVE_LIVEPATCH select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI - select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS && AS_HAS_THIN_ADD_SUB && !CC_IS_CLANG + select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS && AS_HAS_THIN_ADD_SUB select HAVE_PCI select HAVE_PERF_EVENTS select HAVE_PERF_REGS From e0c69abdc428129e62b1cdfc48494c0b07b4c679 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 17 Sep 2024 22:23:09 +0800 Subject: [PATCH 14/23] LoongArch: Set AS_HAS_THIN_ADD_SUB as y if AS_IS_LLVM commit a7e0837724562ea8c1d869dd1a5cb1119ef651c3 upstream. When building kernel with "make CC=clang defconfig", LLVM Assembler is used due to LLVM_IAS=0 is not specified, then AS_HAS_THIN_ADD_SUB is not set, thus objtool can not be built after enable it for Clang. config AS_HAS_THIN_ADD_SUB is to check whether -mthin-add-sub option is available to know R_LARCH_{32,64}_PCREL are supported for GNU Assembler, there is no such an option for LLVM Assembler. The minimal version of Clang is 18 for building LoongArch kernel, and Clang >= 17 has already supported R_LARCH_{32,64}_PCREL, that is to say, there is no need to depend on AS_HAS_THIN_ADD_SUB for Clang, so just set AS_HAS_THIN_ADD_SUB as y if AS_IS_LLVM. Fixes: 120dd4118e58 ("LoongArch: Only allow OBJTOOL & ORC unwinder if toolchain supports -mthin-add-sub") Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen --- arch/loongarch/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 7fd6b9168f81..d31c89c87210 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -264,7 +264,7 @@ config AS_HAS_FCSR_CLASS def_bool $(as-instr,movfcsr2gr \$t0$(comma)\$fcsr0) config AS_HAS_THIN_ADD_SUB - def_bool $(cc-option,-Wa$(comma)-mthin-add-sub) + def_bool $(cc-option,-Wa$(comma)-mthin-add-sub) || AS_IS_LLVM config AS_HAS_LSX_EXTENSION def_bool $(as-instr,vld \$vr0$(comma)\$a0$(comma)0) From 79379d1629a9aaef0133bd5e69268f4368bc42e1 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 24 Sep 2024 14:27:10 +0800 Subject: [PATCH 15/23] compiler.h: specify correct attribute for .rodata..c_jump_table commit c5b1184decc819756ae549ba54c63b6790c4ddfd upstream. Currently, there is an assembler message when generating kernel/bpf/core.o under CONFIG_OBJTOOL with LoongArch compiler toolchain: Warning: setting incorrect section attributes for .rodata..c_jump_table This is because the section ".rodata..c_jump_table" should be readonly, but there is a "W" (writable) part of the flags: $ readelf -S kernel/bpf/core.o | grep -A 1 "rodata..c" [34] .rodata..c_j[...] PROGBITS 0000000000000000 0000d2e0 0000000000000800 0000000000000000 WA 0 0 8 There is no above issue on x86 due to the generated section flag is only "A" (allocatable). In order to silence the warning on LoongArch, specify the attribute like ".rodata..c_jump_table,\"a\",@progbits #" explicitly, then the section attribute of ".rodata..c_jump_table" must be readonly in the kernel/bpf/core.o file. Before: $ objdump -h kernel/bpf/core.o | grep -A 1 "rodata..c" 21 .rodata..c_jump_table 00000800 0000000000000000 0000000000000000 0000d2e0 2**3 CONTENTS, ALLOC, LOAD, RELOC, DATA After: $ objdump -h kernel/bpf/core.o | grep -A 1 "rodata..c" 21 .rodata..c_jump_table 00000800 0000000000000000 0000000000000000 0000d2e0 2**3 CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA By the way, AFAICT, maybe the root cause is related with the different compiler behavior of various archs, so to some extent this change is a workaround for LoongArch, and also there is no effect for x86 which is the only port supported by objtool before LoongArch with this patch. Link: https://lkml.kernel.org/r/20240924062710.1243-1-yangtiezhu@loongson.cn Signed-off-by: Tiezhu Yang Cc: Josh Poimboeuf Cc: Peter Zijlstra Cc: [6.9+] Signed-off-by: Andrew Morton --- include/linux/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/compiler.h b/include/linux/compiler.h index df29ddb3c862..266a74943831 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -133,7 +133,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, #define annotate_unreachable() __annotate_unreachable(__COUNTER__) /* Annotate a C jump table to allow objtool to follow the code flow */ -#define __annotate_jump_table __section(".rodata..c_jump_table") +#define __annotate_jump_table __section(".rodata..c_jump_table,\"a\",@progbits #") #else /* !CONFIG_OBJTOOL */ #define annotate_reachable() From 4efaa85c03b3f410107ea24fe32f1524cc93e46f Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 19 May 2024 22:18:56 +0800 Subject: [PATCH 16/23] LoongArch: Update Loongson-3 default config file commit 9cc1df421f00453afdcaf78b105d8e7fd03cce78 upstream. Enable ORC stack unwinder. Enable livepatch. Signed-off-by: Huacai Chen [only enable configs of orc, ftrace and livepatch - yangtiezhu] Signed-off-by: Tiezhu Yang --- arch/loongarch/configs/loongson3_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 8b5976ee228f..86f2b5071717 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -46,6 +46,7 @@ CONFIG_NUMA=y CONFIG_CPU_HAS_LSX=y CONFIG_CPU_HAS_LASX=y CONFIG_RANDOMIZE_BASE=y +CONFIG_LIVEPATCH=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y @@ -2198,7 +2199,9 @@ CONFIG_SCHEDSTATS=y CONFIG_DEBUG_LIST=y CONFIG_RCU_CPU_STALL_TIMEOUT=60 # CONFIG_RCU_TRACE is not set +CONFIG_FUNCTION_TRACER=y # CONFIG_STRICT_DEVMEM is not set +CONFIG_UNWINDER_ORC=y # CONFIG_RUNTIME_TESTING_MENU is not set CONFIG_LOONGARCH_IOMMU=m CONFIG_CMDLINE_EXTEND=y From ec92e554e76b793c8db609ba6fde71ba426bb17f Mon Sep 17 00:00:00 2001 From: xiongmengbiao Date: Thu, 6 Jun 2024 13:57:02 +0800 Subject: [PATCH 17/23] crypto: ccp: remove multi-level pointers processing for vpsp In the past, running TKM on a regular VM required copying TKM request data to the kernel buffer. However, in the CSV VM, all data is encrypted. If TKM requests contain pointers, the data pointed to by the pointers cannot be correctly parsed. Therefore, we have removed the handling of multi-level pointers in TKM requests. Now, all TKM commands must follow an offset-based model and cannot include pointers. Signed-off-by: xiongmengbiao --- arch/x86/kvm/svm/svm.c | 4 +- arch/x86/kvm/x86.c | 7 +- drivers/crypto/ccp/hygon/csv-dev.c | 93 +++++- drivers/crypto/ccp/hygon/psp-dev.c | 94 ------ drivers/crypto/ccp/hygon/vpsp.c | 480 +++-------------------------- include/linux/psp-hygon.h | 17 +- include/uapi/linux/kvm_para.h | 3 +- 7 files changed, 150 insertions(+), 548 deletions(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 8a17a5b46549..68e9af002fbf 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -5252,8 +5252,8 @@ static int kvm_hygon_arch_hypercall(struct kvm *kvm, u64 nr, u64 a0, u64 a1, u64 .read_guest = kvm_read_guest }; switch (nr) { - case KVM_HC_PSP_OP: - ret = kvm_pv_psp_op(&vpsp, a0, a1, a2, a3); + case KVM_HC_PSP_COPY_FORWARD_OP: + ret = kvm_pv_psp_copy_forward_op(&vpsp, a0, a1, a2); break; default: diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4909b33c55dc..0fd95321d860 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9879,7 +9879,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) } if (static_call(kvm_x86_get_cpl)(vcpu) != 0 && - !(is_x86_vendor_hygon() && (nr == KVM_HC_VM_ATTESTATION || nr == KVM_HC_PSP_OP))) { + !(is_x86_vendor_hygon() && (nr == KVM_HC_VM_ATTESTATION + || nr == KVM_HC_PSP_OP_OBSOLETE + || nr == KVM_HC_PSP_COPY_FORWARD_OP))) { ret = -KVM_EPERM; goto out; } @@ -9916,7 +9918,8 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) kvm_sched_yield(vcpu, a0); ret = 0; break; - case KVM_HC_PSP_OP: + case KVM_HC_PSP_OP_OBSOLETE: + case KVM_HC_PSP_COPY_FORWARD_OP: ret = -KVM_ENOSYS; if (kvm_arch_hypercall) ret = kvm_arch_hypercall(vcpu->kvm, nr, a0, a1, a2, a3); diff --git a/drivers/crypto/ccp/hygon/csv-dev.c b/drivers/crypto/ccp/hygon/csv-dev.c index 9ab2005050ba..3ab8b45649e6 100644 --- a/drivers/crypto/ccp/hygon/csv-dev.c +++ b/drivers/crypto/ccp/hygon/csv-dev.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -1065,7 +1066,97 @@ end: return rb_supported; } -int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret); +int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret) +{ + struct psp_device *psp = psp_master; + struct sev_device *sev; + phys_addr_t phys_addr; + unsigned int phys_lsb, phys_msb; + unsigned int reg, ret = 0; + + if (!psp || !psp->sev_data) + return -ENODEV; + + if (*hygon_psp_hooks.psp_dead) + return -EBUSY; + + sev = psp->sev_data; + + if (data && WARN_ON_ONCE(!virt_addr_valid(data))) + return -EINVAL; + + /* Get the physical address of the command buffer */ + phys_addr = PUT_PSP_VID(__psp_pa(data), vid); + phys_lsb = data ? lower_32_bits(phys_addr) : 0; + phys_msb = data ? upper_32_bits(phys_addr) : 0; + + dev_dbg(sev->dev, "sev command id %#x buffer 0x%08x%08x timeout %us\n", + cmd, phys_msb, phys_lsb, *hygon_psp_hooks.psp_timeout); + + print_hex_dump_debug("(in): ", DUMP_PREFIX_OFFSET, 16, 2, data, + hygon_psp_hooks.sev_cmd_buffer_len(cmd), false); + + iowrite32(phys_lsb, sev->io_regs + sev->vdata->cmdbuff_addr_lo_reg); + iowrite32(phys_msb, sev->io_regs + sev->vdata->cmdbuff_addr_hi_reg); + + sev->int_rcvd = 0; + + reg = FIELD_PREP(SEV_CMDRESP_CMD, cmd) | SEV_CMDRESP_IOC; + iowrite32(reg, sev->io_regs + sev->vdata->cmdresp_reg); + + /* wait for command completion */ + ret = hygon_psp_hooks.sev_wait_cmd_ioc(sev, ®, *hygon_psp_hooks.psp_timeout); + if (ret) { + if (psp_ret) + *psp_ret = 0; + + dev_err(sev->dev, "sev command %#x timed out, disabling PSP\n", cmd); + *hygon_psp_hooks.psp_dead = true; + + return ret; + } + + *hygon_psp_hooks.psp_timeout = *hygon_psp_hooks.psp_cmd_timeout; + + if (psp_ret) + *psp_ret = FIELD_GET(PSP_CMDRESP_STS, reg); + + if (FIELD_GET(PSP_CMDRESP_STS, reg)) { + dev_dbg(sev->dev, "sev command %#x failed (%#010lx)\n", + cmd, FIELD_GET(PSP_CMDRESP_STS, reg)); + ret = -EIO; + } + + print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data, + hygon_psp_hooks.sev_cmd_buffer_len(cmd), false); + + return ret; +} + +int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret) +{ + int rc; + int mutex_enabled = READ_ONCE(hygon_psp_hooks.psp_mutex_enabled); + + if (is_vendor_hygon() && mutex_enabled) { + if (psp_mutex_lock_timeout(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex, + PSP_MUTEX_TIMEOUT) != 1) { + return -EBUSY; + } + } else { + mutex_lock(hygon_psp_hooks.sev_cmd_mutex); + } + + rc = __vpsp_do_cmd_locked(vid, cmd, data, psp_ret); + + if (is_vendor_hygon() && mutex_enabled) + psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex); + else + mutex_unlock(hygon_psp_hooks.sev_cmd_mutex); + + return rc; +} + /* * Try to obtain the result again by the command index, this * interface is used in ringbuffer mode diff --git a/drivers/crypto/ccp/hygon/psp-dev.c b/drivers/crypto/ccp/hygon/psp-dev.c index 620e65228ed9..0fede96616d3 100644 --- a/drivers/crypto/ccp/hygon/psp-dev.c +++ b/drivers/crypto/ccp/hygon/psp-dev.c @@ -507,100 +507,6 @@ static int __psp_do_cmd_locked(int cmd, void *data, int *psp_ret) return ret; } -int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret) -{ - struct psp_device *psp = psp_master; - struct sev_device *sev; - phys_addr_t phys_addr; - unsigned int phys_lsb, phys_msb; - unsigned int reg, ret = 0; - - if (!psp || !psp->sev_data || !hygon_psp_hooks.sev_dev_hooks_installed) - return -ENODEV; - - if (*hygon_psp_hooks.psp_dead) - return -EBUSY; - - sev = psp->sev_data; - - if (data && WARN_ON_ONCE(!virt_addr_valid(data))) - return -EINVAL; - - /* Get the physical address of the command buffer */ - phys_addr = PUT_PSP_VID(__psp_pa(data), vid); - phys_lsb = data ? lower_32_bits(phys_addr) : 0; - phys_msb = data ? upper_32_bits(phys_addr) : 0; - - dev_dbg(sev->dev, "sev command id %#x buffer 0x%08x%08x timeout %us\n", - cmd, phys_msb, phys_lsb, *hygon_psp_hooks.psp_timeout); - - print_hex_dump_debug("(in): ", DUMP_PREFIX_OFFSET, 16, 2, data, - hygon_psp_hooks.sev_cmd_buffer_len(cmd), false); - - iowrite32(phys_lsb, sev->io_regs + sev->vdata->cmdbuff_addr_lo_reg); - iowrite32(phys_msb, sev->io_regs + sev->vdata->cmdbuff_addr_hi_reg); - - sev->int_rcvd = 0; - - reg = FIELD_PREP(SEV_CMDRESP_CMD, cmd) | SEV_CMDRESP_IOC; - iowrite32(reg, sev->io_regs + sev->vdata->cmdresp_reg); - - /* wait for command completion */ - ret = hygon_psp_hooks.sev_wait_cmd_ioc(sev, ®, *hygon_psp_hooks.psp_timeout); - if (ret) { - if (psp_ret) - *psp_ret = 0; - - dev_err(sev->dev, "sev command %#x timed out, disabling PSP\n", cmd); - *hygon_psp_hooks.psp_dead = true; - - return ret; - } - - *hygon_psp_hooks.psp_timeout = *hygon_psp_hooks.psp_cmd_timeout; - - if (psp_ret) - *psp_ret = FIELD_GET(PSP_CMDRESP_STS, reg); - - if (FIELD_GET(PSP_CMDRESP_STS, reg)) { - dev_dbg(sev->dev, "sev command %#x failed (%#010lx)\n", - cmd, FIELD_GET(PSP_CMDRESP_STS, reg)); - ret = -EIO; - } - - print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data, - hygon_psp_hooks.sev_cmd_buffer_len(cmd), false); - - return ret; -} - -int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret) -{ - int rc; - int mutex_enabled = READ_ONCE(hygon_psp_hooks.psp_mutex_enabled); - - if (!hygon_psp_hooks.sev_dev_hooks_installed) - return -ENODEV; - - if (mutex_enabled) { - if (psp_mutex_lock_timeout(&psp_misc->data_pg_aligned->mb_mutex, - PSP_MUTEX_TIMEOUT) != 1) { - return -EBUSY; - } - } else { - mutex_lock(hygon_psp_hooks.sev_cmd_mutex); - } - - rc = __vpsp_do_cmd_locked(vid, cmd, data, psp_ret); - - if (is_vendor_hygon() && mutex_enabled) - psp_mutex_unlock(&psp_misc->data_pg_aligned->mb_mutex); - else - mutex_unlock(hygon_psp_hooks.sev_cmd_mutex); - - return rc; -} - int psp_do_cmd(int cmd, void *data, int *psp_ret) { int rc; diff --git a/drivers/crypto/ccp/hygon/vpsp.c b/drivers/crypto/ccp/hygon/vpsp.c index 13208fe2c4b3..bcdad2bff848 100644 --- a/drivers/crypto/ccp/hygon/vpsp.c +++ b/drivers/crypto/ccp/hygon/vpsp.c @@ -33,379 +33,53 @@ * The primary implementation logic of virtual PSP in kernel mode * call trace: * guest command(vmmcall) - * | - * | |-> kvm_pv_psp_cmd_pre_op - * | | - * | | -> guest_addr_map_table_op - * | | - * | | -> guest_multiple_level_gpa_replace - * | - * kvm_pv_psp_op->|-> vpsp_try_do_cmd/vpsp_try_get_result <====> psp device driver + * |-> kvm_pv_psp_cmd_pre_op * | + * kvm_pv_psp_copy_forward_op->|-> vpsp_try_do_cmd/vpsp_try_get_result <====> psp device driver * | * |-> kvm_pv_psp_cmd_post_op - * | - * | -> guest_addr_map_table_op - * | - * | -> guest_multiple_level_gpa_restore */ -#define TKM_CMD_ID_MIN 0x120 -#define TKM_CMD_ID_MAX 0x12f - struct psp_cmdresp_head { uint32_t buf_size; uint32_t cmdresp_size; uint32_t cmdresp_code; } __packed; -/** - * struct map_tbl - multilevel pointer address mapping table - * - * @parent_pa: parent address block's physics address - * @offset: offset in parent address block - * @size: submemory size - * @align: submemory align size, hva need to keep size alignment in kernel - * @hva: submemory copy block in kernel virtual address - */ -struct map_tbl { - uint64_t parent_pa; - uint32_t offset; - uint32_t size; - uint32_t align; - uint64_t hva; -} __packed; - -struct addr_map_tbls { - uint32_t tbl_nums; - struct map_tbl tbl[]; -} __packed; - -/* gpa and hva conversion maintenance table for internal use */ -struct gpa2hva_t { - void *hva; - gpa_t gpa; -}; - -struct gpa2hva_tbls { - uint32_t max_nums; - uint32_t tbl_nums; - struct gpa2hva_t tbl[]; -}; - /* save command data for restoring later */ struct vpsp_hbuf_wrapper { void *data; uint32_t data_size; - struct addr_map_tbls *map_tbls; - struct gpa2hva_tbls *g2h_tbls; }; /* Virtual PSP host memory information maintenance, used in ringbuffer mode */ struct vpsp_hbuf_wrapper g_hbuf_wrap[CSV_COMMAND_PRIORITY_NUM][CSV_RING_BUFFER_SIZE / CSV_RING_BUFFER_ESIZE] = {0}; -void __maybe_unused map_tbl_dump(const char *title, struct addr_map_tbls *tbls) -{ - int i; - - pr_info("[%s]-> map_tbl_nums: %d", title, tbls->tbl_nums); - for (i = 0; i < tbls->tbl_nums; i++) { - pr_info("\t[%d]: parent_pa: 0x%llx, offset: 0x%x, size: 0x%x, align: 0x%x hva: 0x%llx", - i, tbls->tbl[i].parent_pa, tbls->tbl[i].offset, - tbls->tbl[i].size, tbls->tbl[i].align, tbls->tbl[i].hva); - } - pr_info("\n"); -} - -void __maybe_unused g2h_tbl_dump(const char *title, struct gpa2hva_tbls *tbls) -{ - int i; - - pr_info("[%s]-> g2h_tbl_nums: %d, max_nums: %d", title, tbls->tbl_nums, - tbls->max_nums); - for (i = 0; i < tbls->tbl_nums; i++) - pr_info("\t[%d]: hva: 0x%llx, gpa: 0x%llx", i, - (uint64_t)tbls->tbl[i].hva, tbls->tbl[i].gpa); - pr_info("\n"); -} - -static int gpa2hva_tbl_fill(struct gpa2hva_tbls *tbls, void *hva, gpa_t gpa) -{ - uint32_t fill_idx = tbls->tbl_nums; - - if (fill_idx >= tbls->max_nums) - return -EFAULT; - - tbls->tbl[fill_idx].hva = hva; - tbls->tbl[fill_idx].gpa = gpa; - tbls->tbl_nums = fill_idx + 1; - - return 0; -} - -static void clear_hva_in_g2h_tbls(struct gpa2hva_tbls *g2h, void *hva) -{ - int i; - - for (i = 0; i < g2h->tbl_nums; i++) { - if (g2h->tbl[i].hva == hva) - g2h->tbl[i].hva = NULL; - } -} - -static void *get_hva_from_gpa(struct gpa2hva_tbls *g2h, gpa_t gpa) -{ - int i; - - for (i = 0; i < g2h->tbl_nums; i++) { - if (g2h->tbl[i].gpa == gpa) - return (void *)g2h->tbl[i].hva; - } - - return NULL; -} - -static gpa_t get_gpa_from_hva(struct gpa2hva_tbls *g2h, void *hva) -{ - int i; - - for (i = 0; i < g2h->tbl_nums; i++) { - if (g2h->tbl[i].hva == hva) - return g2h->tbl[i].gpa; - } - - return 0; -} - -/* - * The virtual machine multilevel pointer command buffer handles the - * execution entity, synchronizes the data in the original gpa to the - * newly allocated hva(host virtual address) and updates the mapping - * relationship in the parent memory - */ -static int guest_multiple_level_gpa_replace(struct kvm_vpsp *vpsp, - struct map_tbl *tbl, struct gpa2hva_tbls *g2h) -{ - int ret = 0; - uint32_t sub_block_size; - uint64_t sub_paddr; - void *parent_kva = NULL; - - /* kmalloc memory for child block */ - sub_block_size = max(tbl->size, tbl->align); - tbl->hva = (uint64_t)kzalloc(sub_block_size, GFP_KERNEL); - if (!tbl->hva) - return -ENOMEM; - - /* get child gpa from parent gpa */ - if (unlikely(vpsp->read_guest(vpsp->kvm, tbl->parent_pa + tbl->offset, - &sub_paddr, sizeof(sub_paddr)))) { - pr_err("[%s]: kvm_read_guest for parent gpa failed\n", - __func__); - ret = -EFAULT; - goto e_free; - } - - /* copy child block data from gpa to hva */ - if (unlikely(vpsp->read_guest(vpsp->kvm, sub_paddr, (void *)tbl->hva, - tbl->size))) { - pr_err("[%s]: kvm_read_guest for sub_data failed\n", - __func__); - ret = -EFAULT; - goto e_free; - } - - /* get hva from gpa */ - parent_kva = get_hva_from_gpa(g2h, tbl->parent_pa); - if (unlikely(!parent_kva)) { - pr_err("[%s]: get_hva_from_gpa for parent_pa failed\n", - __func__); - ret = -EFAULT; - goto e_free; - } - - /* replace pa of hva from gpa */ - *(uint64_t *)((uint8_t *)parent_kva + tbl->offset) = __psp_pa(tbl->hva); - - /* fill in gpa and hva to map table for restoring later */ - if (unlikely(gpa2hva_tbl_fill(g2h, (void *)tbl->hva, sub_paddr))) { - pr_err("[%s]: gpa2hva_tbl_fill for sub_addr failed\n", - __func__); - ret = -EFAULT; - goto e_free; - } - - return ret; - -e_free: - kfree((const void *)tbl->hva); - return ret; -} - -/* The virtual machine multi-level pointer command memory handles the - * execution entity, synchronizes the data in the hva(host virtual - * address) back to the memory corresponding to the gpa, and restores - * the mapping relationship in the original parent memory - */ -static int guest_multiple_level_gpa_restore(struct kvm_vpsp *vpsp, - struct map_tbl *tbl, struct gpa2hva_tbls *g2h) -{ - int ret = 0; - gpa_t sub_gpa; - void *parent_hva = NULL; - - /* get gpa from hva */ - sub_gpa = get_gpa_from_hva(g2h, (void *)tbl->hva); - if (unlikely(!sub_gpa)) { - pr_err("[%s]: get_gpa_from_hva for sub_gpa failed\n", - __func__); - ret = -EFAULT; - goto end; - } - - /* copy child block data from hva to gpa */ - if (unlikely(vpsp->write_guest(vpsp->kvm, sub_gpa, (void *)tbl->hva, - tbl->size))) { - pr_err("[%s]: kvm_write_guest for sub_gpa failed\n", - __func__); - ret = -EFAULT; - goto end; - } - - /* get parent hva from parent gpa */ - parent_hva = get_hva_from_gpa(g2h, tbl->parent_pa); - if (unlikely(!parent_hva)) { - pr_err("[%s]: get_hva_from_gpa for parent_pa failed\n", - __func__); - ret = -EFAULT; - goto end; - } - - /* restore gpa from pa of hva in parent block */ - *(uint64_t *)((uint8_t *)parent_hva + tbl->offset) = sub_gpa; - - /* free child block memory */ - clear_hva_in_g2h_tbls(g2h, (void *)tbl->hva); - kfree((const void *)tbl->hva); - tbl->hva = 0; - -end: - return ret; -} - -/* - * The virtual machine multilevel pointer command memory processing - * executes upper-layer abstract interfaces, including replacing and - * restoring two sub-processing functions - */ -static int guest_addr_map_table_op(struct kvm_vpsp *vpsp, struct gpa2hva_tbls *g2h, - struct addr_map_tbls *map_tbls, int op) -{ - int ret = 0; - int i; - uint64_t *sub_paddr_ptr; - - if (op) { - for (i = map_tbls->tbl_nums - 1; i >= 0; i--) { - /* check if the gpa of root points to itself */ - if (map_tbls->tbl[i].parent_pa == g2h->tbl[0].gpa) { - sub_paddr_ptr = (uint64_t *)((uint8_t *)g2h->tbl[0].hva - + map_tbls->tbl[i].offset); - /* if the child paddr is equal to the parent paddr */ - if ((uint64_t)g2h->tbl[0].hva == map_tbls->tbl[i].hva) { - *sub_paddr_ptr = g2h->tbl[0].gpa; - continue; - } - } - - /* restore new pa of kva with the gpa from guest */ - if (unlikely(guest_multiple_level_gpa_restore(vpsp, - &map_tbls->tbl[i], g2h))) { - pr_err("[%s]: guest_multiple_level_gpa_restore failed\n", - __func__); - ret = -EFAULT; - goto end; - } - } - } else { - for (i = 0; i < map_tbls->tbl_nums; i++) { - /* check if the gpa of root points to itself */ - if (map_tbls->tbl[i].parent_pa == g2h->tbl[0].gpa) { - sub_paddr_ptr = (uint64_t *)((uint8_t *)g2h->tbl[0].hva - + map_tbls->tbl[i].offset); - /* if the child paddr is equal to the parent paddr */ - if (*sub_paddr_ptr == map_tbls->tbl[i].parent_pa) { - *sub_paddr_ptr = __psp_pa(g2h->tbl[0].hva); - map_tbls->tbl[i].hva = (uint64_t)g2h->tbl[0].hva; - continue; - } - } - - /* check if parent_pa is valid */ - if (unlikely(!get_hva_from_gpa(g2h, map_tbls->tbl[i].parent_pa))) { - pr_err("[%s]: g2h->tbl[%d].parent_pa: 0x%llx is invalid\n", - __func__, i, map_tbls->tbl[i].parent_pa); - ret = -EFAULT; - goto end; - } - - /* replace the gpa from guest with the new pa of kva */ - if (unlikely(guest_multiple_level_gpa_replace(vpsp, - &map_tbls->tbl[i], g2h))) { - pr_err("[%s]: guest_multiple_level_gpa_replace failed\n", - __func__); - ret = -EFAULT; - goto end; - } - } - } - -end: - return ret; -} - -static void kvm_pv_psp_mem_free(struct gpa2hva_tbls *g2h, struct addr_map_tbls - *map_tbl, void *data) -{ - int i; - - if (g2h) { - for (i = 0; i < g2h->tbl_nums; i++) { - if (g2h->tbl[i].hva && (g2h->tbl[i].hva != data)) { - kfree(g2h->tbl[i].hva); - g2h->tbl[i].hva = NULL; - } - } - kfree(g2h); - } - - kfree(map_tbl); - kfree(data); -} - /* * Obtain the VM command and preprocess the pointer mapping table * information in the command buffer, the processed data will be * used to interact with the psp device */ static int kvm_pv_psp_cmd_pre_op(struct kvm_vpsp *vpsp, gpa_t data_gpa, - gpa_t table_gpa, struct vpsp_hbuf_wrapper *hbuf) + struct vpsp_hbuf_wrapper *hbuf) { int ret = 0; void *data = NULL; struct psp_cmdresp_head psp_head; uint32_t data_size; - struct addr_map_tbls map_head, *map_tbls = NULL; - uint32_t map_tbl_size; - struct gpa2hva_tbls *g2h = NULL; - uint32_t g2h_tbl_size; if (unlikely(vpsp->read_guest(vpsp->kvm, data_gpa, &psp_head, sizeof(struct psp_cmdresp_head)))) return -EFAULT; data_size = psp_head.buf_size; + if ((((uintptr_t)data_gpa + data_size - 1) & ~PSP_2MB_MASK) + != ((uintptr_t)data_gpa & ~PSP_2MB_MASK)) { + pr_err("data_gpa %llx, data_size %d crossing 2MB\n", (u64)data_gpa, data_size); + return -EFAULT; + } + data = kzalloc(data_size, GFP_KERNEL); if (!data) return -ENOMEM; @@ -415,87 +89,18 @@ static int kvm_pv_psp_cmd_pre_op(struct kvm_vpsp *vpsp, gpa_t data_gpa, goto end; } - if (table_gpa) { - /* parse address map table from guest */ - if (unlikely(vpsp->read_guest(vpsp->kvm, table_gpa, &map_head, - sizeof(struct addr_map_tbls)))) { - pr_err("[%s]: kvm_read_guest for map_head failed\n", - __func__); - ret = -EFAULT; - goto end; - } - - map_tbl_size = sizeof(struct addr_map_tbls) + map_head.tbl_nums - * sizeof(struct map_tbl); - map_tbls = kzalloc(map_tbl_size, GFP_KERNEL); - if (!map_tbls) { - ret = -ENOMEM; - goto end; - } - - if (unlikely(vpsp->read_guest(vpsp->kvm, table_gpa, map_tbls, - map_tbl_size))) { - pr_err("[%s]: kvm_read_guest for map_tbls failed\n", - __func__); - ret = -EFAULT; - goto end; - } - - /* init for gpa2hva table*/ - g2h_tbl_size = sizeof(struct gpa2hva_tbls) + (map_head.tbl_nums - + 1) * sizeof(struct gpa2hva_t); - g2h = kzalloc(g2h_tbl_size, GFP_KERNEL); - if (!g2h) { - ret = -ENOMEM; - goto end; - } - g2h->max_nums = map_head.tbl_nums + 1; - - /* fill the root parent address */ - if (gpa2hva_tbl_fill(g2h, data, data_gpa)) { - pr_err("[%s]: gpa2hva_tbl_fill for root data address failed\n", - __func__); - ret = -EFAULT; - goto end; - } - - if (guest_addr_map_table_op(vpsp, g2h, map_tbls, 0)) { - pr_err("[%s]: guest_addr_map_table_op for replacing failed\n", - __func__); - ret = -EFAULT; - goto end; - } - } - hbuf->data = data; hbuf->data_size = data_size; - hbuf->map_tbls = map_tbls; - hbuf->g2h_tbls = g2h; end: return ret; } -/* - * The executed command data is recovered according to the multilevel - * pointer of the mapping table when the command has finished - * interacting with the psp device - */ static int kvm_pv_psp_cmd_post_op(struct kvm_vpsp *vpsp, gpa_t data_gpa, - struct vpsp_hbuf_wrapper *hbuf) + struct vpsp_hbuf_wrapper *hbuf) { int ret = 0; - if (hbuf->map_tbls) { - if (guest_addr_map_table_op(vpsp, hbuf->g2h_tbls, - hbuf->map_tbls, 1)) { - pr_err("[%s]: guest_addr_map_table_op for restoring failed\n", - __func__); - ret = -EFAULT; - goto end; - } - } - /* restore cmdresp's buffer from context */ if (unlikely(vpsp->write_guest(vpsp->kvm, data_gpa, hbuf->data, hbuf->data_size))) { @@ -504,12 +109,9 @@ static int kvm_pv_psp_cmd_post_op(struct kvm_vpsp *vpsp, gpa_t data_gpa, ret = -EFAULT; goto end; } - end: - /* release memory and clear hbuf */ - kvm_pv_psp_mem_free(hbuf->g2h_tbls, hbuf->map_tbls, hbuf->data); + kfree(hbuf->data); memset(hbuf, 0, sizeof(*hbuf)); - return ret; } @@ -520,11 +122,16 @@ static int cmd_type_is_tkm(int cmd) return 0; } -/* - * The primary implementation interface of virtual PSP in kernel mode +/** + * @brief kvm_pv_psp_copy_forward_op is used for ordinary virtual machines to copy data + * in gpa to host memory and send it to psp for processing. + * + * @param vpsp points to kvm related data + * @param cmd psp cmd id, bit 31 indicates queue priority + * @param data_gpa guest physical address of input data + * @param psp_ret_gpa guest physical address of psp_ret */ -int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_gpa, - gpa_t table_gpa) +int kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_gpa) { int ret = 0; struct vpsp_ret psp_ret = {0}; @@ -534,13 +141,18 @@ int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_ uint32_t index = 0; uint32_t vid = 0; + if (vcmd->cmd_id != TKM_PSP_CMDID_OFFSET) { + pr_err("[%s]: unsupported cmd id %x\n", __func__, vcmd->cmd_id); + return -EINVAL; + } + // only tkm cmd need vid if (cmd_type_is_tkm(vcmd->cmd_id)) { // check the permission to use the default vid when no vid is set ret = vpsp_get_vid(&vid, vpsp->kvm->userspace_pid); if (ret && !vpsp_get_default_vid_permission()) { pr_err("[%s]: not allowed tkm command without vid\n", __func__); - return -EFAULT; + return -EPERM; } } @@ -550,8 +162,8 @@ int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_ switch (psp_ret.status) { case VPSP_INIT: - /* multilevel pointer replace*/ - ret = kvm_pv_psp_cmd_pre_op(vpsp, data_gpa, table_gpa, &hbuf); + /* copy data from guest */ + ret = kvm_pv_psp_cmd_pre_op(vpsp, data_gpa, &hbuf); if (unlikely(ret)) { psp_ret.status = VPSP_FINISH; pr_err("[%s]: kvm_pv_psp_cmd_pre_op failed\n", @@ -564,21 +176,18 @@ int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_ ret = vpsp_try_do_cmd(vid, cmd, (void *)hbuf.data, (struct vpsp_ret *)&psp_ret); if (unlikely(ret)) { - pr_err("[%s]: vpsp_do_cmd failed\n", __func__); + pr_err("[%s]: vpsp_try_do_cmd failed\n", __func__); ret = -EFAULT; goto end; } - switch (psp_ret.status) { - case VPSP_RUNNING: - /* backup host memory message for restoring later*/ + if (psp_ret.status == VPSP_RUNNING) { prio = vcmd->is_high_rb ? CSV_COMMAND_PRIORITY_HIGH : CSV_COMMAND_PRIORITY_LOW; g_hbuf_wrap[prio][psp_ret.index] = hbuf; break; - case VPSP_FINISH: - /* restore multilevel pointer data */ + } else if (psp_ret.status == VPSP_FINISH) { ret = kvm_pv_psp_cmd_post_op(vpsp, data_gpa, &hbuf); if (unlikely(ret)) { pr_err("[%s]: kvm_pv_psp_cmd_post_op failed\n", @@ -586,11 +195,6 @@ int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_ ret = -EFAULT; goto end; } - break; - - default: - ret = -EFAULT; - break; } break; @@ -607,26 +211,21 @@ int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_ goto end; } - switch (psp_ret.status) { - case VPSP_RUNNING: - break; - - case VPSP_FINISH: - /* restore multilevel pointer data */ + if (psp_ret.status == VPSP_RUNNING) { + ret = 0; + goto end; + } else if (psp_ret.status == VPSP_FINISH) { + /* copy data to guest */ ret = kvm_pv_psp_cmd_post_op(vpsp, data_gpa, &g_hbuf_wrap[prio][index]); if (unlikely(ret)) { pr_err("[%s]: kvm_pv_psp_cmd_post_op failed\n", __func__); ret = -EFAULT; - goto end; } - break; - - default: - ret = -EFAULT; - break; + goto end; } + ret = -EFAULT; break; default: @@ -638,4 +237,5 @@ end: /* return psp_ret to guest */ vpsp->write_guest(vpsp->kvm, psp_ret_gpa, &psp_ret, sizeof(psp_ret)); return ret; -} EXPORT_SYMBOL_GPL(kvm_pv_psp_op); +} +EXPORT_SYMBOL_GPL(kvm_pv_psp_copy_forward_op); diff --git a/include/linux/psp-hygon.h b/include/linux/psp-hygon.h index 26b271ea81a5..556b7b0f5fcf 100644 --- a/include/linux/psp-hygon.h +++ b/include/linux/psp-hygon.h @@ -449,6 +449,11 @@ struct kvm_vpsp { int (*read_guest)(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len); }; +#define PSP_2MB_MASK (2*1024*1024 - 1) +#define TKM_CMD_ID_MIN 0x120 +#define TKM_CMD_ID_MAX 0x12f +#define TKM_PSP_CMDID TKM_CMD_ID_MIN +#define TKM_PSP_CMDID_OFFSET 0x128 #define PSP_VID_MASK 0xff #define PSP_VID_SHIFT 56 #define PUT_PSP_VID(hpa, vid) ((__u64)(hpa) | ((__u64)(PSP_VID_MASK & vid) << PSP_VID_SHIFT)) @@ -457,8 +462,6 @@ struct kvm_vpsp { #ifdef CONFIG_CRYPTO_DEV_SP_PSP -int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret); - int psp_do_cmd(int cmd, void *data, int *psp_ret); int csv_ring_buffer_queue_init(void); @@ -481,11 +484,9 @@ int vpsp_get_vid(uint32_t *vid, pid_t pid); int vpsp_get_default_vid_permission(void); -int kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_gpa, - gpa_t table_gpa); -#else /* !CONFIG_CRYPTO_DEV_SP_PSP */ +int kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_gpa); -static inline int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret) { return -ENODEV; } +#else /* !CONFIG_CRYPTO_DEV_SP_PSP */ static inline int psp_do_cmd(int cmd, void *data, int *psp_ret) { return -ENODEV; } @@ -512,8 +513,8 @@ static inline int vpsp_get_default_vid_permission(void) { return -ENODEV; } static inline int -kvm_pv_psp_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, - gpa_t psp_ret_gpa, gpa_t table_gpa) { return -ENODEV; } +kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, + gpa_t psp_ret_gpa) { return -ENODEV; } #endif /* CONFIG_CRYPTO_DEV_SP_PSP */ typedef int (*p2c_notifier_t)(uint32_t id, uint64_t data); diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index 86369b7a5733..f2fc642db945 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h @@ -31,7 +31,8 @@ #define KVM_HC_SCHED_YIELD 11 #define KVM_HC_MAP_GPA_RANGE 12 #define KVM_HC_VM_ATTESTATION 100 /* Specific to Hygon CPU */ -#define KVM_HC_PSP_OP 101 /* Specific to Hygon platform */ +#define KVM_HC_PSP_OP_OBSOLETE 101 /* Specific to Hygon platform */ +#define KVM_HC_PSP_COPY_FORWARD_OP 102 /* Specific to Hygon platform */ /* * hypercalls use architecture specific From 430f1f43544faca3c80cde42a3b569b0af3c87d0 Mon Sep 17 00:00:00 2001 From: xiongmengbiao Date: Wed, 29 May 2024 11:00:23 +0800 Subject: [PATCH 18/23] crypto: ccp: support TKM run on CSV In the CSV VM, data is encrypted in the host machine. To support running TKM in the CSV VM, a new KVM_HC_PSP_FORWARD_OP operation was introduced. In this mode, all TKM request data is converted from GPA to HPA and directly sent to PSP for processing. PSP can decrypt the encrypted data on HPA using the correct ASID. Signed-off-by: xiongmengbiao --- arch/x86/kvm/svm/svm.c | 13 +- arch/x86/kvm/x86.c | 4 +- drivers/crypto/ccp/hygon/csv-dev.c | 40 ++-- drivers/crypto/ccp/hygon/psp-dev.c | 86 +++++--- drivers/crypto/ccp/hygon/vpsp.c | 331 +++++++++++++++++++++++++---- include/linux/psp-hygon.h | 47 +++- include/uapi/linux/kvm_para.h | 1 + 7 files changed, 416 insertions(+), 106 deletions(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 68e9af002fbf..2fee97a29bb9 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -5249,13 +5249,22 @@ static int kvm_hygon_arch_hypercall(struct kvm *kvm, u64 nr, u64 a0, u64 a1, u64 struct kvm_vpsp vpsp = { .kvm = kvm, .write_guest = kvm_write_guest, - .read_guest = kvm_read_guest + .read_guest = kvm_read_guest, + .gfn_to_pfn = gfn_to_pfn, }; + + if (sev_guest(kvm)) { + vpsp.vm_handle = to_kvm_svm(kvm)->sev_info.handle; + vpsp.is_csv_guest = 1; + } + switch (nr) { case KVM_HC_PSP_COPY_FORWARD_OP: ret = kvm_pv_psp_copy_forward_op(&vpsp, a0, a1, a2); break; - + case KVM_HC_PSP_FORWARD_OP: + ret = kvm_pv_psp_forward_op(&vpsp, a0, a1, a2); + break; default: ret = -KVM_ENOSYS; break; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0fd95321d860..f34c9afefb8b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9881,7 +9881,8 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) if (static_call(kvm_x86_get_cpl)(vcpu) != 0 && !(is_x86_vendor_hygon() && (nr == KVM_HC_VM_ATTESTATION || nr == KVM_HC_PSP_OP_OBSOLETE - || nr == KVM_HC_PSP_COPY_FORWARD_OP))) { + || nr == KVM_HC_PSP_COPY_FORWARD_OP + || nr == KVM_HC_PSP_FORWARD_OP))) { ret = -KVM_EPERM; goto out; } @@ -9920,6 +9921,7 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) break; case KVM_HC_PSP_OP_OBSOLETE: case KVM_HC_PSP_COPY_FORWARD_OP: + case KVM_HC_PSP_FORWARD_OP: ret = -KVM_ENOSYS; if (kvm_arch_hypercall) ret = kvm_arch_hypercall(vcpu->kvm, nr, a0, a1, a2, a3); diff --git a/drivers/crypto/ccp/hygon/csv-dev.c b/drivers/crypto/ccp/hygon/csv-dev.c index 3ab8b45649e6..781af8531208 100644 --- a/drivers/crypto/ccp/hygon/csv-dev.c +++ b/drivers/crypto/ccp/hygon/csv-dev.c @@ -761,12 +761,12 @@ static int vpsp_dequeue_cmd(int prio, int index, * Populate the command from the virtual machine to the queue to * support execution in ringbuffer mode */ -static int vpsp_fill_cmd_queue(uint32_t vid, int prio, int cmd, void *data, uint16_t flags) +static int vpsp_fill_cmd_queue(int prio, int cmd, phys_addr_t phy_addr, uint16_t flags) { struct csv_cmdptr_entry cmdptr = { }; int index = -1; - cmdptr.cmd_buf_ptr = PUT_PSP_VID(__psp_pa(data), vid); + cmdptr.cmd_buf_ptr = phy_addr; cmdptr.cmd_id = cmd; cmdptr.cmd_flags = flags; @@ -1066,11 +1066,10 @@ end: return rb_supported; } -int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret) +static int __vpsp_do_cmd_locked(int cmd, phys_addr_t phy_addr, int *psp_ret) { struct psp_device *psp = psp_master; struct sev_device *sev; - phys_addr_t phys_addr; unsigned int phys_lsb, phys_msb; unsigned int reg, ret = 0; @@ -1082,20 +1081,13 @@ int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret) sev = psp->sev_data; - if (data && WARN_ON_ONCE(!virt_addr_valid(data))) - return -EINVAL; - /* Get the physical address of the command buffer */ - phys_addr = PUT_PSP_VID(__psp_pa(data), vid); - phys_lsb = data ? lower_32_bits(phys_addr) : 0; - phys_msb = data ? upper_32_bits(phys_addr) : 0; + phys_lsb = phy_addr ? lower_32_bits(phy_addr) : 0; + phys_msb = phy_addr ? upper_32_bits(phy_addr) : 0; dev_dbg(sev->dev, "sev command id %#x buffer 0x%08x%08x timeout %us\n", cmd, phys_msb, phys_lsb, *hygon_psp_hooks.psp_timeout); - print_hex_dump_debug("(in): ", DUMP_PREFIX_OFFSET, 16, 2, data, - hygon_psp_hooks.sev_cmd_buffer_len(cmd), false); - iowrite32(phys_lsb, sev->io_regs + sev->vdata->cmdbuff_addr_lo_reg); iowrite32(phys_msb, sev->io_regs + sev->vdata->cmdbuff_addr_hi_reg); @@ -1127,13 +1119,10 @@ int __vpsp_do_cmd_locked(uint32_t vid, int cmd, void *data, int *psp_ret) ret = -EIO; } - print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data, - hygon_psp_hooks.sev_cmd_buffer_len(cmd), false); - return ret; } -int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret) +int vpsp_do_cmd(int cmd, phys_addr_t phy_addr, int *psp_ret) { int rc; int mutex_enabled = READ_ONCE(hygon_psp_hooks.psp_mutex_enabled); @@ -1147,7 +1136,7 @@ int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret) mutex_lock(hygon_psp_hooks.sev_cmd_mutex); } - rc = __vpsp_do_cmd_locked(vid, cmd, data, psp_ret); + rc = __vpsp_do_cmd_locked(cmd, phy_addr, psp_ret); if (is_vendor_hygon() && mutex_enabled) psp_mutex_unlock(&hygon_psp_hooks.psp_misc->data_pg_aligned->mb_mutex); @@ -1161,7 +1150,7 @@ int vpsp_do_cmd(uint32_t vid, int cmd, void *data, int *psp_ret) * Try to obtain the result again by the command index, this * interface is used in ringbuffer mode */ -int vpsp_try_get_result(uint32_t vid, uint8_t prio, uint32_t index, void *data, +int vpsp_try_get_result(uint8_t prio, uint32_t index, phys_addr_t phy_addr, struct vpsp_ret *psp_ret) { int ret = 0; @@ -1184,8 +1173,7 @@ int vpsp_try_get_result(uint32_t vid, uint8_t prio, uint32_t index, void *data, /* dequeue command from queue*/ vpsp_dequeue_cmd(prio, index, &cmd); - ret = __vpsp_do_cmd_locked(vid, cmd.cmd_id, data, - (int *)psp_ret); + ret = __vpsp_do_cmd_locked(cmd.cmd_id, phy_addr, (int *)psp_ret); psp_ret->status = VPSP_FINISH; vpsp_psp_mutex_unlock(); if (unlikely(ret)) { @@ -1228,7 +1216,7 @@ EXPORT_SYMBOL_GPL(vpsp_try_get_result); * vpsp_try_get_result interface will be used to obtain the result * later again */ -int vpsp_try_do_cmd(uint32_t vid, int cmd, void *data, struct vpsp_ret *psp_ret) +int vpsp_try_do_cmd(int cmd, phys_addr_t phy_addr, struct vpsp_ret *psp_ret) { int ret = 0; int rb_supported; @@ -1243,10 +1231,10 @@ int vpsp_try_do_cmd(uint32_t vid, int cmd, void *data, struct vpsp_ret *psp_ret) (struct vpsp_cmd *)&cmd); if (rb_supported) { /* fill command in ringbuffer's queue and get index */ - index = vpsp_fill_cmd_queue(vid, prio, cmd, data, 0); + index = vpsp_fill_cmd_queue(prio, cmd, phy_addr, 0); if (unlikely(index < 0)) { /* do mailbox command if queuing failed*/ - ret = vpsp_do_cmd(vid, cmd, data, (int *)psp_ret); + ret = vpsp_do_cmd(cmd, phy_addr, (int *)psp_ret); if (unlikely(ret)) { if (ret == -EIO) { ret = 0; @@ -1262,14 +1250,14 @@ int vpsp_try_do_cmd(uint32_t vid, int cmd, void *data, struct vpsp_ret *psp_ret) } /* try to get result from the ringbuffer command */ - ret = vpsp_try_get_result(vid, prio, index, data, psp_ret); + ret = vpsp_try_get_result(prio, index, phy_addr, psp_ret); if (unlikely(ret)) { pr_err("[%s]: vpsp_try_get_result failed %d\n", __func__, ret); goto end; } } else { /* mailbox mode */ - ret = vpsp_do_cmd(vid, cmd, data, (int *)psp_ret); + ret = vpsp_do_cmd(cmd, phy_addr, (int *)psp_ret); if (unlikely(ret)) { if (ret == -EIO) { ret = 0; diff --git a/drivers/crypto/ccp/hygon/psp-dev.c b/drivers/crypto/ccp/hygon/psp-dev.c index 0fede96616d3..95e9b934db6d 100644 --- a/drivers/crypto/ccp/hygon/psp-dev.c +++ b/drivers/crypto/ccp/hygon/psp-dev.c @@ -38,16 +38,26 @@ enum VPSP_DEV_CTRL_OPCODE { VPSP_OP_VID_DEL, VPSP_OP_SET_DEFAULT_VID_PERMISSION, VPSP_OP_GET_DEFAULT_VID_PERMISSION, + VPSP_OP_SET_GPA, }; struct vpsp_dev_ctrl { unsigned char op; + /** + * To be compatible with old user mode, + * struct vpsp_dev_ctrl must be kept at 132 bytes. + */ + unsigned char resv[3]; union { unsigned int vid; // Set or check the permissions for the default VID unsigned int def_vid_perm; + struct { + u64 gpa_start; + u64 gpa_end; + } gpa; unsigned char reserved[128]; - } data; + } __packed data; }; uint64_t atomic64_exchange(volatile uint64_t *dst, uint64_t val) @@ -169,19 +179,15 @@ DEFINE_RWLOCK(vpsp_rwlock); #define VPSP_VID_MAX_ENTRIES 2048 #define VPSP_VID_NUM_MAX 64 -struct vpsp_vid_entry { - uint32_t vid; - pid_t pid; -}; -static struct vpsp_vid_entry g_vpsp_vid_array[VPSP_VID_MAX_ENTRIES]; +static struct vpsp_context g_vpsp_context_array[VPSP_VID_MAX_ENTRIES]; static uint32_t g_vpsp_vid_num; static int compare_vid_entries(const void *a, const void *b) { - return ((struct vpsp_vid_entry *)a)->pid - ((struct vpsp_vid_entry *)b)->pid; + return ((struct vpsp_context *)a)->pid - ((struct vpsp_context *)b)->pid; } static void swap_vid_entries(void *a, void *b, int size) { - struct vpsp_vid_entry entry; + struct vpsp_context entry; memcpy(&entry, a, size); memcpy(a, b, size); @@ -206,43 +212,41 @@ int vpsp_get_default_vid_permission(void) EXPORT_SYMBOL_GPL(vpsp_get_default_vid_permission); /** - * When the virtual machine executes the 'tkm' command, - * it needs to retrieve the corresponding 'vid' - * by performing a binary search using 'kvm->userspace_pid'. + * get a vpsp context from pid */ -int vpsp_get_vid(uint32_t *vid, pid_t pid) +int vpsp_get_context(struct vpsp_context **ctx, pid_t pid) { - struct vpsp_vid_entry new_entry = {.pid = pid}; - struct vpsp_vid_entry *existing_entry = NULL; + struct vpsp_context new_entry = {.pid = pid}; + struct vpsp_context *existing_entry = NULL; read_lock(&vpsp_rwlock); - existing_entry = bsearch(&new_entry, g_vpsp_vid_array, g_vpsp_vid_num, - sizeof(struct vpsp_vid_entry), compare_vid_entries); + existing_entry = bsearch(&new_entry, g_vpsp_context_array, g_vpsp_vid_num, + sizeof(struct vpsp_context), compare_vid_entries); read_unlock(&vpsp_rwlock); if (!existing_entry) return -ENOENT; - if (vid) { - *vid = existing_entry->vid; - pr_debug("PSP: %s %d, by pid %d\n", __func__, *vid, pid); - } + + if (ctx) + *ctx = existing_entry; + return 0; } -EXPORT_SYMBOL_GPL(vpsp_get_vid); +EXPORT_SYMBOL_GPL(vpsp_get_context); /** * Upon qemu startup, this section checks whether * the '-device psp,vid' parameter is specified. * If set, it utilizes the 'vpsp_add_vid' function - * to insert the 'vid' and 'pid' values into the 'g_vpsp_vid_array'. + * to insert the 'vid' and 'pid' values into the 'g_vpsp_context_array'. * The insertion is done in ascending order of 'pid'. */ static int vpsp_add_vid(uint32_t vid) { pid_t cur_pid = task_pid_nr(current); - struct vpsp_vid_entry new_entry = {.vid = vid, .pid = cur_pid}; + struct vpsp_context new_entry = {.vid = vid, .pid = cur_pid}; - if (vpsp_get_vid(NULL, cur_pid) == 0) + if (vpsp_get_context(NULL, cur_pid) == 0) return -EEXIST; if (g_vpsp_vid_num == VPSP_VID_MAX_ENTRIES) return -ENOMEM; @@ -250,8 +254,8 @@ static int vpsp_add_vid(uint32_t vid) return -EINVAL; write_lock(&vpsp_rwlock); - memcpy(&g_vpsp_vid_array[g_vpsp_vid_num++], &new_entry, sizeof(struct vpsp_vid_entry)); - sort(g_vpsp_vid_array, g_vpsp_vid_num, sizeof(struct vpsp_vid_entry), + memcpy(&g_vpsp_context_array[g_vpsp_vid_num++], &new_entry, sizeof(struct vpsp_context)); + sort(g_vpsp_context_array, g_vpsp_vid_num, sizeof(struct vpsp_context), compare_vid_entries, swap_vid_entries); pr_info("PSP: add vid %d, by pid %d, total vid num is %d\n", vid, cur_pid, g_vpsp_vid_num); write_unlock(&vpsp_rwlock); @@ -270,12 +274,12 @@ static int vpsp_del_vid(void) write_lock(&vpsp_rwlock); for (i = 0; i < g_vpsp_vid_num; ++i) { - if (g_vpsp_vid_array[i].pid == cur_pid) { + if (g_vpsp_context_array[i].pid == cur_pid) { --g_vpsp_vid_num; pr_info("PSP: delete vid %d, by pid %d, total vid num is %d\n", - g_vpsp_vid_array[i].vid, cur_pid, g_vpsp_vid_num); - memcpy(&g_vpsp_vid_array[i], &g_vpsp_vid_array[i + 1], - sizeof(struct vpsp_vid_entry) * (g_vpsp_vid_num - i)); + g_vpsp_context_array[i].vid, cur_pid, g_vpsp_vid_num); + memcpy(&g_vpsp_context_array[i], &g_vpsp_context_array[i + 1], + sizeof(struct vpsp_context) * (g_vpsp_vid_num - i)); ret = 0; goto end; } @@ -286,6 +290,24 @@ end: return ret; } +static int vpsp_set_gpa_range(u64 gpa_start, u64 gpa_end) +{ + pid_t cur_pid = task_pid_nr(current); + struct vpsp_context *ctx = NULL; + + vpsp_get_context(&ctx, cur_pid); + if (!ctx) { + pr_err("PSP: %s get vpsp_context failed from pid %d\n", __func__, cur_pid); + return -ENOENT; + } + + ctx->gpa_start = gpa_start; + ctx->gpa_end = gpa_end; + pr_info("PSP: set gpa range (start 0x%llx, end 0x%llx), by pid %d\n", + gpa_start, gpa_end, cur_pid); + return 0; +} + static int do_vpsp_op_ioctl(struct vpsp_dev_ctrl *ctrl) { int ret = 0; @@ -308,6 +330,10 @@ static int do_vpsp_op_ioctl(struct vpsp_dev_ctrl *ctrl) ctrl->data.def_vid_perm = vpsp_get_default_vid_permission(); break; + case VPSP_OP_SET_GPA: + ret = vpsp_set_gpa_range(ctrl->data.gpa.gpa_start, ctrl->data.gpa.gpa_end); + break; + default: ret = -EINVAL; break; diff --git a/drivers/crypto/ccp/hygon/vpsp.c b/drivers/crypto/ccp/hygon/vpsp.c index bcdad2bff848..10a42df444ce 100644 --- a/drivers/crypto/ccp/hygon/vpsp.c +++ b/drivers/crypto/ccp/hygon/vpsp.c @@ -18,26 +18,32 @@ #undef pr_fmt #endif #define pr_fmt(fmt) "vpsp: " fmt +#define VTKM_VM_BIND 0x904 /* - * The file mainly implements the base execution - * logic of virtual PSP in kernel mode, which mainly includes: - * (1) Obtain the VM command and preprocess the pointer - * mapping table information in the command buffer - * (2) The command that has been converted will interact - * with the channel of the psp through the driver and - * try to obtain the execution result - * (3) The executed command data is recovered according to - * the multilevel pointer of the mapping table, and then returned to the VM + * The file mainly implements the base execution logic of virtual PSP in kernel mode, + * which mainly includes: + * (1) Preprocess the guest data in the host kernel + * (2) The command that has been converted will interact with the channel of the + * psp through the driver and try to obtain the execution result + * (3) The executed command data is recovered, and then returned to the VM * * The primary implementation logic of virtual PSP in kernel mode * call trace: - * guest command(vmmcall) - * |-> kvm_pv_psp_cmd_pre_op + * guest command(vmmcall, KVM_HC_PSP_COPY_FORWARD_OP) * | - * kvm_pv_psp_copy_forward_op->|-> vpsp_try_do_cmd/vpsp_try_get_result <====> psp device driver + * kvm_pv_psp_copy_op----> | -> kvm_pv_psp_cmd_pre_op + * | + * | -> vpsp_try_do_cmd/vpsp_try_get_result + * | |<=> psp device driver + * | + * | + * |-> kvm_pv_psp_cmd_post_op + * + * guest command(vmmcall, KVM_HC_PSP_FORWARD_OP) * | - * |-> kvm_pv_psp_cmd_post_op + * kvm_pv_psp_forward_op-> |-> vpsp_try_do_cmd/vpsp_try_get_result + * |<=> psp device driver */ struct psp_cmdresp_head { @@ -56,10 +62,36 @@ struct vpsp_hbuf_wrapper { struct vpsp_hbuf_wrapper g_hbuf_wrap[CSV_COMMAND_PRIORITY_NUM][CSV_RING_BUFFER_SIZE / CSV_RING_BUFFER_ESIZE] = {0}; -/* - * Obtain the VM command and preprocess the pointer mapping table - * information in the command buffer, the processed data will be - * used to interact with the psp device +static int check_gpa_range(struct vpsp_context *vpsp_ctx, gpa_t addr, uint32_t size) +{ + if (!vpsp_ctx || !addr) + return -EFAULT; + + if (addr >= vpsp_ctx->gpa_start && (addr + size) <= vpsp_ctx->gpa_end) + return 0; + return -EFAULT; +} + +static int check_psp_mem_range(struct vpsp_context *vpsp_ctx, + void *data, uint32_t size) +{ + if ((((uintptr_t)data + size - 1) & ~PSP_2MB_MASK) != + ((uintptr_t)data & ~PSP_2MB_MASK)) { + pr_err("data %llx, size %d crossing 2MB\n", (u64)data, size); + return -EFAULT; + } + + if (vpsp_ctx) + return check_gpa_range(vpsp_ctx, (gpa_t)data, size); + + return 0; +} + +/** + * Copy the guest data to the host kernel buffer + * and record the host buffer address in 'hbuf'. + * This 'hbuf' is used to restore context information + * during asynchronous processing. */ static int kvm_pv_psp_cmd_pre_op(struct kvm_vpsp *vpsp, gpa_t data_gpa, struct vpsp_hbuf_wrapper *hbuf) @@ -74,11 +106,8 @@ static int kvm_pv_psp_cmd_pre_op(struct kvm_vpsp *vpsp, gpa_t data_gpa, return -EFAULT; data_size = psp_head.buf_size; - if ((((uintptr_t)data_gpa + data_size - 1) & ~PSP_2MB_MASK) - != ((uintptr_t)data_gpa & ~PSP_2MB_MASK)) { - pr_err("data_gpa %llx, data_size %d crossing 2MB\n", (u64)data_gpa, data_size); + if (check_psp_mem_range(NULL, data_gpa, data_size)) return -EFAULT; - } data = kzalloc(data_size, GFP_KERNEL); if (!data) @@ -122,9 +151,234 @@ static int cmd_type_is_tkm(int cmd) return 0; } +static int cmd_type_is_allowed(int cmd) +{ + if (cmd >= TKM_PSP_CMDID_OFFSET && cmd <= TKM_CMD_ID_MAX) + return 1; + return 0; +} + +struct psp_cmdresp_vtkm_vm_bind { + struct psp_cmdresp_head head; + uint16_t vid; + uint32_t vm_handle; + uint8_t reserved[46]; +} __packed; + +static int kvm_bind_vtkm(uint32_t vm_handle, uint32_t cmd_id, uint32_t vid, uint32_t *pret) +{ + int ret = 0; + struct psp_cmdresp_vtkm_vm_bind *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->head.buf_size = sizeof(*data); + data->head.cmdresp_size = sizeof(*data); + data->head.cmdresp_code = VTKM_VM_BIND; + data->vid = vid; + data->vm_handle = vm_handle; + + ret = psp_do_cmd(cmd_id, data, pret); + if (ret == -EIO) + ret = 0; + + kfree(data); + return ret; +} + +static phys_addr_t gpa_to_hpa(struct kvm_vpsp *vpsp, unsigned long data_gpa) +{ + phys_addr_t hpa = 0; + unsigned long pfn = vpsp->gfn_to_pfn(vpsp->kvm, data_gpa >> PAGE_SHIFT); + + if (!is_error_pfn(pfn)) + hpa = ((pfn << PAGE_SHIFT) + offset_in_page(data_gpa)) | sme_get_me_mask(); + + pr_debug("gpa %lx, hpa %llx\n", data_gpa, hpa); + return hpa; + +} + +static int check_cmd_forward_op_permission(struct kvm_vpsp *vpsp, struct vpsp_context *vpsp_ctx, + uint64_t data, uint32_t cmd) +{ + int ret; + struct vpsp_cmd *vcmd = (struct vpsp_cmd *)&cmd; + struct psp_cmdresp_head psp_head; + + if (!cmd_type_is_allowed(vcmd->cmd_id)) { + pr_err("[%s]: unsupported cmd id %x\n", __func__, vcmd->cmd_id); + return -EINVAL; + } + + if (vpsp->is_csv_guest) { + /** + * If the gpa address range exists, + * it means there must be a legal vid + */ + if (!vpsp_ctx || !vpsp_ctx->gpa_start || !vpsp_ctx->gpa_end) { + pr_err("[%s]: No set gpa range or vid in csv guest\n", __func__); + return -EPERM; + } + + ret = check_psp_mem_range(vpsp_ctx, (void *)data, 0); + if (ret) + return -EFAULT; + } else { + if (!vpsp_ctx && cmd_type_is_tkm(vcmd->cmd_id) + && !vpsp_get_default_vid_permission()) { + pr_err("[%s]: not allowed tkm command without vid\n", __func__); + return -EPERM; + } + + // the 'data' is gpa address + if (unlikely(vpsp->read_guest(vpsp->kvm, data, &psp_head, + sizeof(struct psp_cmdresp_head)))) + return -EFAULT; + + ret = check_psp_mem_range(vpsp_ctx, (void *)data, psp_head.buf_size); + if (ret) + return -EFAULT; + } + return 0; +} + +static int +check_cmd_copy_forward_op_permission(struct kvm_vpsp *vpsp, + struct vpsp_context *vpsp_ctx, + uint64_t data, uint32_t cmd) +{ + int ret = 0; + struct vpsp_cmd *vcmd = (struct vpsp_cmd *)&cmd; + + if (!cmd_type_is_allowed(vcmd->cmd_id)) { + pr_err("[%s]: unsupported cmd id %x\n", __func__, vcmd->cmd_id); + return -EINVAL; + } + + if (vpsp->is_csv_guest) { + pr_err("[%s]: unsupported run on csv guest\n", __func__); + ret = -EPERM; + } else { + if (!vpsp_ctx && cmd_type_is_tkm(vcmd->cmd_id) + && !vpsp_get_default_vid_permission()) { + pr_err("[%s]: not allowed tkm command without vid\n", __func__); + ret = -EPERM; + } + } + return ret; +} + +static int vpsp_try_bind_vtkm(struct kvm_vpsp *vpsp, struct vpsp_context *vpsp_ctx, + uint32_t cmd, uint32_t *psp_ret) +{ + int ret; + struct vpsp_cmd *vcmd = (struct vpsp_cmd *)&cmd; + + if (vpsp_ctx && !vpsp_ctx->vm_is_bound && vpsp->is_csv_guest) { + ret = kvm_bind_vtkm(vpsp->vm_handle, vcmd->cmd_id, + vpsp_ctx->vid, psp_ret); + if (ret || *psp_ret) { + pr_err("[%s] kvm bind vtkm failed with ret: %d, pspret: %d\n", + __func__, ret, *psp_ret); + return ret; + } + vpsp_ctx->vm_is_bound = 1; + } + return 0; +} + /** - * @brief kvm_pv_psp_copy_forward_op is used for ordinary virtual machines to copy data - * in gpa to host memory and send it to psp for processing. + * @brief Directly convert the gpa address into hpa and forward it to PSP, + * It is another form of kvm_pv_psp_copy_op, mainly used for csv VMs. + * + * @param vpsp points to kvm related data + * @param cmd psp cmd id, bit 31 indicates queue priority + * @param data_gpa guest physical address of input data + * @param psp_ret indicates Asynchronous context information + * + * Since the csv guest memory cannot be read or written directly, + * the shared asynchronous context information is shared through psp_ret and return value. + */ +int kvm_pv_psp_forward_op(struct kvm_vpsp *vpsp, uint32_t cmd, + gpa_t data_gpa, uint32_t psp_ret) +{ + int ret; + uint64_t data_hpa; + uint32_t index = 0, vid = 0; + struct vpsp_ret psp_async = {0}; + struct vpsp_context *vpsp_ctx = NULL; + struct vpsp_cmd *vcmd = (struct vpsp_cmd *)&cmd; + uint8_t prio = CSV_COMMAND_PRIORITY_LOW; + + vpsp_get_context(&vpsp_ctx, vpsp->kvm->userspace_pid); + + ret = check_cmd_forward_op_permission(vpsp, vpsp_ctx, data_gpa, cmd); + if (unlikely(ret)) { + pr_err("directly operation not allowed\n"); + goto end; + } + + ret = vpsp_try_bind_vtkm(vpsp, vpsp_ctx, cmd, (uint32_t *)&psp_async); + if (unlikely(ret || *(uint32_t *)&psp_async)) { + pr_err("try to bind vtkm failed (ret %x, psp_async %x)\n", + ret, *(uint32_t *)&psp_async); + goto end; + } + + if (vpsp_ctx) + vid = vpsp_ctx->vid; + + *((uint32_t *)&psp_async) = psp_ret; + data_hpa = PUT_PSP_VID(gpa_to_hpa(vpsp, data_gpa), vid); + + switch (psp_async.status) { + case VPSP_INIT: + /* try to send command to the device for execution*/ + ret = vpsp_try_do_cmd(cmd, data_hpa, &psp_async); + if (unlikely(ret)) { + pr_err("[%s]: vpsp_do_cmd failed\n", __func__); + goto end; + } + break; + + case VPSP_RUNNING: + prio = vcmd->is_high_rb ? CSV_COMMAND_PRIORITY_HIGH : + CSV_COMMAND_PRIORITY_LOW; + index = psp_async.index; + /* try to get the execution result from ringbuffer*/ + ret = vpsp_try_get_result(prio, index, data_hpa, &psp_async); + if (unlikely(ret)) { + pr_err("[%s]: vpsp_try_get_result failed\n", __func__); + goto end; + } + break; + + default: + pr_err("[%s]: invalid command status\n", __func__); + break; + } + +end: + /** + * In order to indicate both system errors and PSP errors, + * the psp_async.pret field needs to be reused. + */ + psp_async.format = VPSP_RET_PSP_FORMAT; + if (ret) { + psp_async.format = VPSP_RET_SYS_FORMAT; + if (ret > 0) + ret = -ret; + psp_async.pret = (uint16_t)ret; + } + return *((int *)&psp_async); +} +EXPORT_SYMBOL_GPL(kvm_pv_psp_forward_op); + +/** + * @brief copy data in gpa to host memory and send it to psp for processing. * * @param vpsp points to kvm related data * @param cmd psp cmd id, bit 31 indicates queue priority @@ -137,24 +391,22 @@ int kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, g struct vpsp_ret psp_ret = {0}; struct vpsp_hbuf_wrapper hbuf = {0}; struct vpsp_cmd *vcmd = (struct vpsp_cmd *)&cmd; + struct vpsp_context *vpsp_ctx = NULL; + phys_addr_t data_paddr = 0; uint8_t prio = CSV_COMMAND_PRIORITY_LOW; uint32_t index = 0; uint32_t vid = 0; - if (vcmd->cmd_id != TKM_PSP_CMDID_OFFSET) { - pr_err("[%s]: unsupported cmd id %x\n", __func__, vcmd->cmd_id); - return -EINVAL; + vpsp_get_context(&vpsp_ctx, vpsp->kvm->userspace_pid); + + ret = check_cmd_copy_forward_op_permission(vpsp, vpsp_ctx, data_gpa, cmd); + if (unlikely(ret)) { + pr_err("copy operation not allowed\n"); + return -EPERM; } - // only tkm cmd need vid - if (cmd_type_is_tkm(vcmd->cmd_id)) { - // check the permission to use the default vid when no vid is set - ret = vpsp_get_vid(&vid, vpsp->kvm->userspace_pid); - if (ret && !vpsp_get_default_vid_permission()) { - pr_err("[%s]: not allowed tkm command without vid\n", __func__); - return -EPERM; - } - } + if (vpsp_ctx) + vid = vpsp_ctx->vid; if (unlikely(vpsp->read_guest(vpsp->kvm, psp_ret_gpa, &psp_ret, sizeof(psp_ret)))) @@ -172,9 +424,9 @@ int kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, g goto end; } + data_paddr = PUT_PSP_VID(__psp_pa(hbuf.data), vid); /* try to send command to the device for execution*/ - ret = vpsp_try_do_cmd(vid, cmd, (void *)hbuf.data, - (struct vpsp_ret *)&psp_ret); + ret = vpsp_try_do_cmd(cmd, data_paddr, (struct vpsp_ret *)&psp_ret); if (unlikely(ret)) { pr_err("[%s]: vpsp_try_do_cmd failed\n", __func__); ret = -EFAULT; @@ -202,9 +454,10 @@ int kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, g prio = vcmd->is_high_rb ? CSV_COMMAND_PRIORITY_HIGH : CSV_COMMAND_PRIORITY_LOW; index = psp_ret.index; + data_paddr = PUT_PSP_VID(__psp_pa(g_hbuf_wrap[prio][index].data), vid); /* try to get the execution result from ringbuffer*/ - ret = vpsp_try_get_result(vid, prio, index, g_hbuf_wrap[prio][index].data, - (struct vpsp_ret *)&psp_ret); + ret = vpsp_try_get_result(prio, index, data_paddr, + (struct vpsp_ret *)&psp_ret); if (unlikely(ret)) { pr_err("[%s]: vpsp_try_get_result failed\n", __func__); ret = -EFAULT; diff --git a/include/linux/psp-hygon.h b/include/linux/psp-hygon.h index 556b7b0f5fcf..1888d9d72592 100644 --- a/include/linux/psp-hygon.h +++ b/include/linux/psp-hygon.h @@ -433,23 +433,32 @@ struct vpsp_cmd { * * @pret: the return code from device * @resv: reserved bits + * @format: indicates that the error is a unix error code(is 0) or a psp error(is 1) * @index: used to distinguish the position of command in the ringbuffer * @status: indicates the current status of the related command */ struct vpsp_ret { u32 pret : 16; - u32 resv : 2; + u32 resv : 1; + u32 format : 1; u32 index : 12; u32 status : 2; }; +#define VPSP_RET_SYS_FORMAT 1 +#define VPSP_RET_PSP_FORMAT 0 struct kvm_vpsp { struct kvm *kvm; int (*write_guest)(struct kvm *kvm, gpa_t gpa, const void *data, unsigned long len); int (*read_guest)(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len); + kvm_pfn_t (*gfn_to_pfn)(struct kvm *kvm, gfn_t gfn); + u32 vm_handle; + u8 is_csv_guest; }; #define PSP_2MB_MASK (2*1024*1024 - 1) +#define PSP_HUGEPAGE_2MB (2*1024*1024) +#define PSP_HUGEPAGE_NUM_MAX 128 #define TKM_CMD_ID_MIN 0x120 #define TKM_CMD_ID_MAX 0x12f #define TKM_PSP_CMDID TKM_CMD_ID_MIN @@ -460,6 +469,17 @@ struct kvm_vpsp { #define GET_PSP_VID(hpa) ((__u16)((__u64)(hpa) >> PSP_VID_SHIFT) & PSP_VID_MASK) #define CLEAR_PSP_VID(hpa) ((__u64)(hpa) & ~((__u64)PSP_VID_MASK << PSP_VID_SHIFT)) +struct vpsp_context { + u32 vid; + pid_t pid; + u64 gpa_start; + u64 gpa_end; + + // `vm_is_bound` indicates whether the binding operation has been performed + u32 vm_is_bound; + u32 vm_handle; // only for csv +}; + #ifdef CONFIG_CRYPTO_DEV_SP_PSP int psp_do_cmd(int cmd, void *data, int *psp_ret); @@ -475,17 +495,19 @@ int csv_check_stat_queue_status(int *psp_ret); */ int csv_issue_ringbuf_cmds_external_user(struct file *filep, int *psp_ret); -int vpsp_try_get_result(uint32_t vid, uint8_t prio, uint32_t index, - void *data, struct vpsp_ret *psp_ret); +int vpsp_try_get_result(uint8_t prio, uint32_t index, + phys_addr_t phy_addr, struct vpsp_ret *psp_ret); -int vpsp_try_do_cmd(uint32_t vid, int cmd, void *data, struct vpsp_ret *psp_ret); +int vpsp_try_do_cmd(int cmd, phys_addr_t phy_addr, struct vpsp_ret *psp_ret); -int vpsp_get_vid(uint32_t *vid, pid_t pid); +int vpsp_get_context(struct vpsp_context **ctx, pid_t pid); int vpsp_get_default_vid_permission(void); int kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_gpa); +int kvm_pv_psp_forward_op(struct kvm_vpsp *vpsp, uint32_t cmd, + gpa_t data_gpa, uint32_t psp_ret); #else /* !CONFIG_CRYPTO_DEV_SP_PSP */ static inline int psp_do_cmd(int cmd, void *data, int *psp_ret) { return -ENODEV; } @@ -499,15 +521,19 @@ static inline int csv_issue_ringbuf_cmds_external_user(struct file *filep, int *psp_ret) { return -ENODEV; } static inline int -vpsp_try_get_result(uint32_t vid, uint8_t prio, - uint32_t index, void *data, struct vpsp_ret *psp_ret) { return -ENODEV; } +vpsp_try_get_result(uint8_t prio, + uint32_t index, phys_addr_t phy_addr, struct vpsp_ret *psp_ret) { return -ENODEV; } static inline int vpsp_try_do_cmd(uint32_t vid, int cmd, void *data, struct vpsp_ret *psp_ret) { return -ENODEV; } static inline int -vpsp_get_vid(uint32_t *vid, pid_t pid) { return -ENODEV; } +vpsp_try_do_cmd(int cmd, phys_addr_t phy_addr, + struct vpsp_ret *psp_ret) { return -ENODEV; } + +static inline int +vpsp_get_context(struct vpsp_context **ctx, pid_t pid) { return -ENODEV; } static inline int vpsp_get_default_vid_permission(void) { return -ENODEV; } @@ -515,6 +541,11 @@ vpsp_get_default_vid_permission(void) { return -ENODEV; } static inline int kvm_pv_psp_copy_forward_op(struct kvm_vpsp *vpsp, int cmd, gpa_t data_gpa, gpa_t psp_ret_gpa) { return -ENODEV; } + +static inline int +kvm_pv_psp_forward_op(struct kvm_vpsp *vpsp, uint32_t cmd, + gpa_t data_gpa, uint32_t psp_ret) { return -ENODEV; } + #endif /* CONFIG_CRYPTO_DEV_SP_PSP */ typedef int (*p2c_notifier_t)(uint32_t id, uint64_t data); diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index f2fc642db945..944fe133ae3c 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h @@ -33,6 +33,7 @@ #define KVM_HC_VM_ATTESTATION 100 /* Specific to Hygon CPU */ #define KVM_HC_PSP_OP_OBSOLETE 101 /* Specific to Hygon platform */ #define KVM_HC_PSP_COPY_FORWARD_OP 102 /* Specific to Hygon platform */ +#define KVM_HC_PSP_FORWARD_OP 103 /* Specific to Hygon platform */ /* * hypercalls use architecture specific From c0c4c54954748d14571ad17c7dea5819f61858b9 Mon Sep 17 00:00:00 2001 From: xiongmengbiao Date: Tue, 9 Jul 2024 14:31:54 +0800 Subject: [PATCH 19/23] driver/crypto/ccp: fix vtkm without C-bit when host SME deactivate CSV guests can run without SME enabled. Regardless of the host's SME status, the C-bit must be set for the physical address. Memory will be encrypted with a different key than SME. Signed-off-by: xiongmengbiao --- drivers/crypto/ccp/hygon/vpsp.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/ccp/hygon/vpsp.c b/drivers/crypto/ccp/hygon/vpsp.c index 10a42df444ce..230d8217abd4 100644 --- a/drivers/crypto/ccp/hygon/vpsp.c +++ b/drivers/crypto/ccp/hygon/vpsp.c @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef pr_fmt #undef pr_fmt @@ -188,13 +189,43 @@ static int kvm_bind_vtkm(uint32_t vm_handle, uint32_t cmd_id, uint32_t vid, uint return ret; } +static unsigned long vpsp_get_me_mask(void) +{ + unsigned int eax, ebx, ecx, edx; + unsigned long me_mask; + +#define AMD_SME_BIT BIT(0) +#define AMD_SEV_BIT BIT(1) + /* + * Check for the SME/SEV feature: + * CPUID Fn8000_001F[EAX] + * - Bit 0 - Secure Memory Encryption support + * - Bit 1 - Secure Encrypted Virtualization support + * CPUID Fn8000_001F[EBX] + * - Bits 5:0 - Pagetable bit position used to indicate encryption + */ + eax = 0x8000001f; + ecx = 0; + native_cpuid(&eax, &ebx, &ecx, &edx); + /* Check whether SEV or SME is supported */ + if (!(eax & (AMD_SEV_BIT | AMD_SME_BIT))) + return 0; + + me_mask = 1UL << (ebx & 0x3f); + return me_mask; +} + static phys_addr_t gpa_to_hpa(struct kvm_vpsp *vpsp, unsigned long data_gpa) { phys_addr_t hpa = 0; unsigned long pfn = vpsp->gfn_to_pfn(vpsp->kvm, data_gpa >> PAGE_SHIFT); + unsigned long me_mask = sme_get_me_mask(); + + if (me_mask == 0 && vpsp->is_csv_guest) + me_mask = vpsp_get_me_mask(); if (!is_error_pfn(pfn)) - hpa = ((pfn << PAGE_SHIFT) + offset_in_page(data_gpa)) | sme_get_me_mask(); + hpa = ((pfn << PAGE_SHIFT) + offset_in_page(data_gpa)) | me_mask; pr_debug("gpa %lx, hpa %llx\n", data_gpa, hpa); return hpa; From 8e1f6da2fdcd5cd1d01c9ce99acc6b1cb0d2b1c8 Mon Sep 17 00:00:00 2001 From: xiongmengbiao Date: Thu, 4 Jul 2024 10:57:27 +0800 Subject: [PATCH 20/23] drivers/crypto/ccp: add ioctl API to pin TKM hugepage A new set of ioctl interfaces for pinning memory pages has been added. These interfaces allow user-space drivers to prevent large memory pages from being unexpectedly migrated. Signed-off-by: xiongmengbiao --- drivers/crypto/ccp/hygon/psp-dev.c | 71 ++++++++++++++++++++++++++++++ drivers/crypto/ccp/hygon/vpsp.c | 2 +- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/ccp/hygon/psp-dev.c b/drivers/crypto/ccp/hygon/psp-dev.c index 95e9b934db6d..9306d42c4708 100644 --- a/drivers/crypto/ccp/hygon/psp-dev.c +++ b/drivers/crypto/ccp/hygon/psp-dev.c @@ -30,6 +30,8 @@ enum HYGON_PSP_OPCODE { HYGON_PSP_MUTEX_ENABLE = 1, HYGON_PSP_MUTEX_DISABLE, HYGON_VPSP_CTRL_OPT, + HYGON_PSP_OP_PIN_USER_PAGE, + HYGON_PSP_OP_UNPIN_USER_PAGE, HYGON_PSP_OPCODE_MAX_NR, }; @@ -308,6 +310,67 @@ static int vpsp_set_gpa_range(u64 gpa_start, u64 gpa_end) return 0; } +/** + * Try to pin a page + * + * @vaddr: the userspace virtual address, must be aligned to PAGE_SIZE + */ +static int psp_pin_user_page(u64 vaddr) +{ + struct page *page; + long npinned = 0; + int ref_count = 0; + + // check must be aligned to PAGE_SIZE + if (vaddr & (PAGE_SIZE - 1)) { + pr_err("vaddr %llx not aligned to 0x%lx\n", vaddr, PAGE_SIZE); + return -EFAULT; + } + + npinned = pin_user_pages_fast(vaddr, 1, FOLL_WRITE, &page); + if (npinned != 1) { + pr_err("PSP: pin_user_pages_fast fail\n"); + return -ENOMEM; + } + + ref_count = page_ref_count(page); + pr_debug("pin user page with address %llx, page ref_count %d\n", vaddr, ref_count); + return 0; +} + +/** + * Try to unpin a page + * + * @vaddr: the userspace virtual address, must be aligned to PAGE_SIZE + */ +static int psp_unpin_user_page(u64 vaddr) +{ + struct page *page; + long npinned = 0; + int ref_count = 0; + + // check must be aligned to PAGE_SIZE + if (vaddr & (PAGE_SIZE - 1)) { + pr_err("vaddr %llx not aligned to 0x%lx\n", vaddr, PAGE_SIZE); + return -EFAULT; + } + + // page reference count increment by 1 + npinned = get_user_pages_fast(vaddr, 1, FOLL_WRITE, &page); + if (npinned != 1) { + pr_err("PSP: pin_user_pages_fast fail\n"); + return -ENOMEM; + } + + // page reference count decrement by 2 + put_page(page); + put_page(page); + + ref_count = page_ref_count(page); + pr_debug("unpin user page with address %llx, page ref_count %d\n", vaddr, ref_count); + return 0; +} + static int do_vpsp_op_ioctl(struct vpsp_dev_ctrl *ctrl) { int ret = 0; @@ -390,6 +453,14 @@ static long ioctl_psp(struct file *file, unsigned int ioctl, unsigned long arg) return -EFAULT; break; + case HYGON_PSP_OP_PIN_USER_PAGE: + ret = psp_pin_user_page((u64)arg); + break; + + case HYGON_PSP_OP_UNPIN_USER_PAGE: + ret = psp_unpin_user_page((u64)arg); + break; + default: printk(KERN_INFO "%s: invalid ioctl number: %d\n", __func__, opcode); return -EINVAL; diff --git a/drivers/crypto/ccp/hygon/vpsp.c b/drivers/crypto/ccp/hygon/vpsp.c index 230d8217abd4..4ece51f1e654 100644 --- a/drivers/crypto/ccp/hygon/vpsp.c +++ b/drivers/crypto/ccp/hygon/vpsp.c @@ -107,7 +107,7 @@ static int kvm_pv_psp_cmd_pre_op(struct kvm_vpsp *vpsp, gpa_t data_gpa, return -EFAULT; data_size = psp_head.buf_size; - if (check_psp_mem_range(NULL, data_gpa, data_size)) + if (check_psp_mem_range(NULL, (void *)data_gpa, data_size)) return -EFAULT; data = kzalloc(data_size, GFP_KERNEL); From 85449db3513caca2602083867b4516d3a4ae756f Mon Sep 17 00:00:00 2001 From: niuyongwen Date: Fri, 2 Aug 2024 13:04:08 +0800 Subject: [PATCH 21/23] drivers/crypto/ccp: memmove is used instead of memcpy in overlapped memmory for tkm When QEMU exits, the context information for the corresponding VID is removed from an array. However, memcpy was incorrectly used to overwrite invalid data on overlapping memory. memmove should be used instead to handle overlapping memory regions correctly. Signed-off-by: niuyongwen --- drivers/crypto/ccp/hygon/psp-dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/ccp/hygon/psp-dev.c b/drivers/crypto/ccp/hygon/psp-dev.c index 9306d42c4708..cc6c0351f205 100644 --- a/drivers/crypto/ccp/hygon/psp-dev.c +++ b/drivers/crypto/ccp/hygon/psp-dev.c @@ -280,7 +280,7 @@ static int vpsp_del_vid(void) --g_vpsp_vid_num; pr_info("PSP: delete vid %d, by pid %d, total vid num is %d\n", g_vpsp_context_array[i].vid, cur_pid, g_vpsp_vid_num); - memcpy(&g_vpsp_context_array[i], &g_vpsp_context_array[i + 1], + memmove(&g_vpsp_context_array[i], &g_vpsp_context_array[i + 1], sizeof(struct vpsp_context) * (g_vpsp_vid_num - i)); ret = 0; goto end; From 9baada0c80cd8a2d8a944d5baa50502d4276241f Mon Sep 17 00:00:00 2001 From: niuyongwen Date: Mon, 30 Sep 2024 11:23:59 +0800 Subject: [PATCH 22/23] drivers/crypto/ccp: fix the increase in page references caused by gfn_to_pfn gfn_to_pfn causes the refcount to increment atomically by one, which needs to be released. Signed-off-by: niuyongwen --- drivers/crypto/ccp/hygon/vpsp.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/ccp/hygon/vpsp.c b/drivers/crypto/ccp/hygon/vpsp.c index 4ece51f1e654..df62dab035b8 100644 --- a/drivers/crypto/ccp/hygon/vpsp.c +++ b/drivers/crypto/ccp/hygon/vpsp.c @@ -220,12 +220,28 @@ static phys_addr_t gpa_to_hpa(struct kvm_vpsp *vpsp, unsigned long data_gpa) phys_addr_t hpa = 0; unsigned long pfn = vpsp->gfn_to_pfn(vpsp->kvm, data_gpa >> PAGE_SHIFT); unsigned long me_mask = sme_get_me_mask(); + struct page *page; if (me_mask == 0 && vpsp->is_csv_guest) me_mask = vpsp_get_me_mask(); if (!is_error_pfn(pfn)) hpa = ((pfn << PAGE_SHIFT) + offset_in_page(data_gpa)) | me_mask; + else { + pr_err("[%s] pfn: %lx is invalid, gpa %lx", + __func__, pfn, data_gpa); + return 0; + } + + /* + * Using gfn_to_pfn causes the refcount to increment + * atomically by one, which needs to be released. + */ + page = pfn_to_page(pfn); + if (PageCompound(page)) + page = compound_head(page); + + put_page(page); pr_debug("gpa %lx, hpa %llx\n", data_gpa, hpa); return hpa; @@ -343,6 +359,7 @@ int kvm_pv_psp_forward_op(struct kvm_vpsp *vpsp, uint32_t cmd, struct vpsp_context *vpsp_ctx = NULL; struct vpsp_cmd *vcmd = (struct vpsp_cmd *)&cmd; uint8_t prio = CSV_COMMAND_PRIORITY_LOW; + phys_addr_t hpa; vpsp_get_context(&vpsp_ctx, vpsp->kvm->userspace_pid); @@ -363,7 +380,14 @@ int kvm_pv_psp_forward_op(struct kvm_vpsp *vpsp, uint32_t cmd, vid = vpsp_ctx->vid; *((uint32_t *)&psp_async) = psp_ret; - data_hpa = PUT_PSP_VID(gpa_to_hpa(vpsp, data_gpa), vid); + + hpa = gpa_to_hpa(vpsp, data_gpa); + if (unlikely(!hpa)) { + ret = -EFAULT; + goto end; + } + + data_hpa = PUT_PSP_VID(hpa, vid); switch (psp_async.status) { case VPSP_INIT: From d276b18c7bdbde41974d0f7dbf5cc65d6abc5c85 Mon Sep 17 00:00:00 2001 From: Ming Wang Date: Tue, 12 Nov 2024 18:59:21 +0800 Subject: [PATCH 23/23] LoongArch: Update Loongson-3 default config file Upstream: no Conflict: none Checkpatch: pass Enable BPF_JIT Enable PSI Enable ARCH_IOREMAP Enable CPU_HAS_LBT Enable SCSI_SAS_ATA Enable CRYPTO_SM4_GENERIC Enable CRYPTO_SM3_GENERIC Enable HARDLOCKUP_DETECTOR Enable FTRACE_SYSCALLS Enable BLK_DEV_IO_TRACE By the way, as part of general housekeeping, we're changing the defconfig files to sort lines based on the 'make savedefconfig' output. This will make it easier to make additional changes in the future. Signed-off-by: Ming Wang --- arch/loongarch/configs/loongson3_defconfig | 33 ++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 86f2b5071717..5c00be21770a 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -4,6 +4,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_BPF_SYSCALL=y +CONFIG_BPF_JIT=y # CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set CONFIG_PREEMPT_VOLUNTARY=y CONFIG_IRQ_TIME_ACCOUNTING=y @@ -13,6 +14,7 @@ CONFIG_TASKSTATS=y CONFIG_TASK_DELAY_ACCT=y CONFIG_TASK_XACCT=y CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_PSI=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=18 @@ -34,17 +36,20 @@ CONFIG_NAMESPACES=y CONFIG_USER_NS=y CONFIG_CHECKPOINT_RESTORE=y CONFIG_SCHED_AUTOGROUP=y -CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_PROFILING=y CONFIG_KEXEC=y CONFIG_CRASH_DUMP=y +CONFIG_CMDLINE="vfio_iommu_type1.allow_unsafe_interrupts=1 nokaslr" +CONFIG_CMDLINE_EXTEND=y CONFIG_NR_CPUS=256 CONFIG_NUMA=y +CONFIG_ARCH_IOREMAP=y CONFIG_CPU_HAS_LSX=y CONFIG_CPU_HAS_LASX=y +CONFIG_CPU_HAS_LBT=y CONFIG_RANDOMIZE_BASE=y CONFIG_LIVEPATCH=y CONFIG_CPU_FREQ=y @@ -57,6 +62,8 @@ CONFIG_ACPI_DOCK=y CONFIG_ACPI_IPMI=m CONFIG_ACPI_PCI_SLOT=y CONFIG_ACPI_HOTPLUG_MEMORY=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=m CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_FORCE_LOAD=y @@ -75,6 +82,8 @@ CONFIG_IOSCHED_BFQ=y CONFIG_BINFMT_MISC=m CONFIG_ZSWAP=y CONFIG_ZSWAP_COMPRESSOR_DEFAULT_ZSTD=y +CONFIG_Z3FOLD_DEPRECATED=y +CONFIG_ZSMALLOC=y CONFIG_ZSMALLOC_STAT=y CONFIG_SLAB_FREELIST_RANDOM=y # CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set @@ -566,7 +575,6 @@ CONFIG_BT_BNEP_MC_FILTER=y CONFIG_BT_BNEP_PROTO_FILTER=y CONFIG_BT_CMTP=m CONFIG_BT_HIDP=m -CONFIG_BT_HS=y CONFIG_BT_HCIBTUSB=m CONFIG_BT_HCIBTUSB_AUTOSUSPEND=y # CONFIG_BT_HCIBTUSB_BCM is not set @@ -618,8 +626,6 @@ CONFIG_ISCSI_IBFT=m CONFIG_EFI_ZBOOT=y CONFIG_EFI_CAPSULE_LOADER=m CONFIG_EFI_TEST=m -CONFIG_VIRTUALIZATION=y -CONFIG_KVM=m CONFIG_MTD=m CONFIG_MTD_BLOCK=m CONFIG_MTD_CFI=m @@ -653,7 +659,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_CDROM_PKTCDVD=m CONFIG_VIRTIO_BLK=m CONFIG_BLK_DEV_RBD=m -CONFIG_BLK_DEV_NVME=m +CONFIG_BLK_DEV_NVME=y CONFIG_NVME_MULTIPATH=y CONFIG_NVME_RDMA=m CONFIG_NVME_FC=m @@ -680,7 +686,7 @@ CONFIG_MISC_RTSX_PCI=m CONFIG_MISC_RTSX_USB=m CONFIG_UACCE=m CONFIG_PVPANIC=y -CONFIG_BLK_DEV_SD=m +CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=m CONFIG_BLK_DEV_SR=m CONFIG_CHR_DEV_SG=m @@ -690,6 +696,7 @@ CONFIG_SCSI_CONSTANTS=y CONFIG_SCSI_LOGGING=y CONFIG_SCSI_SCAN_ASYNC=y CONFIG_SCSI_FC_ATTRS=m +CONFIG_SCSI_SAS_ATA=y CONFIG_ISCSI_TCP=m CONFIG_SCSI_CXGB4_ISCSI=m CONFIG_SCSI_BNX2_ISCSI=m @@ -880,6 +887,7 @@ CONFIG_R8169=m # CONFIG_NET_VENDOR_SMSC is not set # CONFIG_NET_VENDOR_SOCIONEXT is not set CONFIG_STMMAC_ETH=y +CONFIG_DWMAC_LOONGSON=m # CONFIG_NET_VENDOR_SUN is not set # CONFIG_NET_VENDOR_SYNOPSYS is not set # CONFIG_NET_VENDOR_TEHUTI is not set @@ -1471,11 +1479,11 @@ CONFIG_DRM_AMDGPU_USERPTR=y CONFIG_DRM_NOUVEAU=m CONFIG_DRM_VKMS=m CONFIG_DRM_UDL=m -CONFIG_DRM_AST_LOONGSON=y CONFIG_DRM_MGAG200=m CONFIG_DRM_QXL=m CONFIG_DRM_VIRTIO_GPU=m CONFIG_DRM_LOONGSON=y +CONFIG_DRM_AST_LOONGSON=y CONFIG_DRM_BOCHS=m CONFIG_DRM_CIRRUS_QEMU=m CONFIG_FB=y @@ -1913,6 +1921,7 @@ CONFIG_COMEDI_NI_PCIDIO=m CONFIG_COMEDI_NI_PCIMIO=m CONFIG_STAGING=y CONFIG_COMMON_CLK_LOONGSON2=y +CONFIG_LOONGARCH_IOMMU=m CONFIG_LOONGSON2_GUTS=y CONFIG_LOONGSON2_PM=y CONFIG_PM_DEVFREQ=y @@ -2142,6 +2151,7 @@ CONFIG_CRYPTO_FCRYPT=m CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SEED=m CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_SM4_GENERIC=m CONFIG_CRYPTO_TEA=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_ARC4=m @@ -2157,6 +2167,7 @@ CONFIG_CRYPTO_SEQIV=y CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m +CONFIG_CRYPTO_SM3_GENERIC=m CONFIG_CRYPTO_VMAC=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_XCBC=m @@ -2178,7 +2189,6 @@ CONFIG_SIGNED_PE_FILE_VERIFICATION=y CONFIG_SECONDARY_TRUSTED_KEYRING=y CONFIG_SYSTEM_BLACKLIST_KEYRING=y CONFIG_SYSTEM_REVOCATION_LIST=y -CONFIG_CRC_T10DIF=y CONFIG_CRC_ITU_T=y CONFIG_CRC7=m CONFIG_DMA_CMA=y @@ -2194,15 +2204,16 @@ CONFIG_DEBUG_SECTION_MISMATCH=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_SHIRQ=y CONFIG_PANIC_ON_OOPS=y +CONFIG_HARDLOCKUP_DETECTOR=y +# CONFIG_DETECT_HUNG_TASK is not set # CONFIG_SCHED_DEBUG is not set CONFIG_SCHEDSTATS=y CONFIG_DEBUG_LIST=y CONFIG_RCU_CPU_STALL_TIMEOUT=60 # CONFIG_RCU_TRACE is not set CONFIG_FUNCTION_TRACER=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_BLK_DEV_IO_TRACE=y # CONFIG_STRICT_DEVMEM is not set CONFIG_UNWINDER_ORC=y # CONFIG_RUNTIME_TESTING_MENU is not set -CONFIG_LOONGARCH_IOMMU=m -CONFIG_CMDLINE_EXTEND=y -CONFIG_CMDLINE="vfio_iommu_type1.allow_unsafe_interrupts=1 nokaslr"