diff --git a/arch/riscv64/mcount-support.c b/arch/riscv64/mcount-support.c index 53a872303..ba8fece48 100644 --- a/arch/riscv64/mcount-support.c +++ b/arch/riscv64/mcount-support.c @@ -4,24 +4,218 @@ #include "utils/filter.h" #include "utils/utils.h" -/* TODO: not implemented yet (Start) */ +static int mcount_get_register_arg(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) +{ + struct mcount_regs *regs = ctx->regs; + int reg_idx; + + switch (spec->type) { + case ARG_TYPE_REG: + reg_idx = spec->reg_idx; + break; + case ARG_TYPE_INDEX: + reg_idx = spec->idx; /* for integer arguments */ + break; + case ARG_TYPE_FLOAT: + reg_idx = spec->idx + UFT_RISCV64_REG_FLOAT_BASE; + break; + case ARG_TYPE_STACK: + default: + return -1; + } + + ctx->val.i = 0; + + switch (reg_idx) { + case UFT_RISCV64_REG_A0: + ctx->val.i = ARG1(regs); + break; + case UFT_RISCV64_REG_A1: + ctx->val.i = ARG2(regs); + break; + case UFT_RISCV64_REG_A2: + ctx->val.i = ARG3(regs); + break; + case UFT_RISCV64_REG_A3: + ctx->val.i = ARG4(regs); + break; + case UFT_RISCV64_REG_A4: + ctx->val.i = ARG5(regs); + break; + case UFT_RISCV64_REG_A5: + ctx->val.i = ARG6(regs); + break; + case UFT_RISCV64_REG_A6: + ctx->val.i = ARG7(regs); + break; + case UFT_RISCV64_REG_A7: + ctx->val.i = ARG8(regs); + break; + case UFT_RISCV64_REG_FA0: + asm volatile("fsd fa0, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA1: + asm volatile("fsd fa1, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA2: + asm volatile("fsd fa2, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA3: + asm volatile("fsd fa3, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA4: + asm volatile("fsd fa4, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA5: + asm volatile("fsd fa5, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA6: + asm volatile("fsd fa6, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA7: + asm volatile("fsd fa7, %0\n" : "=m"(ctx->val.v)); + break; + default: + return -1; + } + + return 0; +} + +static void mcount_get_stack_arg(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) +{ + int offset; + unsigned long *addr = ctx->stack_base; + + switch (spec->type) { + case ARG_TYPE_STACK: + offset = spec->stack_ofs; + break; + case ARG_TYPE_INDEX: + offset = spec->idx - ARCH_MAX_REG_ARGS; + break; + case ARG_TYPE_FLOAT: + offset = (spec->idx - ARCH_MAX_FLOAT_REGS) * 2 - 1; + break; + case ARG_TYPE_REG: + default: + /* should not reach here */ + pr_err_ns("invalid stack access for arguments\n"); + break; + } + + if (offset < 1 || offset > 100) { + pr_dbg("invalid stack offset: %d\n", offset); + mcount_memset4(ctx->val.v, 0, sizeof(ctx->val)); + return; + } + + addr += offset; + + if (check_mem_region(ctx, (unsigned long)addr)) { + /* save long double arguments properly */ + mcount_memcpy4(ctx->val.v, addr, ALIGN(spec->size, 4)); + } + else { + pr_dbg("stack address is not allowed: %p\n", addr); + mcount_memset4(ctx->val.v, 0, sizeof(ctx->val)); + } +} + +static void mcount_get_struct_arg(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) +{ + struct uftrace_arg_spec reg_spec = { + .type = ARG_TYPE_REG, + }; + void *ptr = ctx->val.p; + int i; + + for (i = 0; i < spec->struct_reg_cnt; i++) { + reg_spec.reg_idx = spec->struct_regs[i]; + + mcount_get_register_arg(ctx, ®_spec); + mcount_memcpy4(ptr, ctx->val.v, sizeof(long)); + ptr += sizeof(long); + } + + if (spec->stack_ofs > 0) { + unsigned long *addr = ctx->stack_base + spec->stack_ofs; + + /* + * it cannot call mcount_get_stack_arg() since the struct + * might be bigger than the ctx->val. It directly updates + * the argument buffer (in the ptr). + */ + if (check_mem_region(ctx, (unsigned long)addr)) + mcount_memcpy4(ptr, addr, spec->size); + else { + pr_dbg("stack address is not allowed: %p\n", addr); + mcount_memset4(ptr, 0, spec->size); + } + } + else if (spec->struct_reg_cnt == 0) { + mcount_get_register_arg(ctx, spec); + mcount_memcpy4(ptr, ctx->val.v, sizeof(long)); + } +} + void mcount_arch_get_arg(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) { - return; + if (spec->fmt == ARG_FMT_STRUCT) { + mcount_get_struct_arg(ctx, spec); + return; + } + + if (mcount_get_register_arg(ctx, spec) < 0) + mcount_get_stack_arg(ctx, spec); } void mcount_arch_get_retval(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) { - return; + if (spec->fmt == ARG_FMT_STRUCT) + mcount_memcpy4(ctx->val.v, ctx->retval, sizeof(long)); + /* type of return value cannot be FLOAT, so check format instead */ + else if (spec->fmt == ARG_FMT_FLOAT) { + long *float_retval = ctx->retval - 2; + + if (spec->size <= 4) { + asm volatile("flw fa0, %1\n" + "fsw fa0, %0\n" + : "=m"(ctx->val.v) + : "m"(*float_retval)); + } + else { + asm volatile("fld fa0, %1\n" + "fsd fa0, %0\n" + : "=m"(ctx->val.v) + : "m"(*float_retval)); + } + } + else + mcount_memcpy4(ctx->val.v, ctx->retval, spec->size); } void mcount_save_arch_context(struct mcount_arch_context *ctx) { - return; + asm volatile("fsd fa0, %0\n" : "=m"(ctx->f[0])); + asm volatile("fsd fa1, %0\n" : "=m"(ctx->f[1])); + asm volatile("fsd fa2, %0\n" : "=m"(ctx->f[2])); + asm volatile("fsd fa3, %0\n" : "=m"(ctx->f[3])); + asm volatile("fsd fa4, %0\n" : "=m"(ctx->f[4])); + asm volatile("fsd fa5, %0\n" : "=m"(ctx->f[5])); + asm volatile("fsd fa6, %0\n" : "=m"(ctx->f[6])); + asm volatile("fsd fa7, %0\n" : "=m"(ctx->f[7])); } void mcount_restore_arch_context(struct mcount_arch_context *ctx) { - return; + asm volatile("fld fa0, %0\n" ::"m"(ctx->f[0])); + asm volatile("fld fa1, %0\n" ::"m"(ctx->f[1])); + asm volatile("fld fa2, %0\n" ::"m"(ctx->f[2])); + asm volatile("fld fa3, %0\n" ::"m"(ctx->f[3])); + asm volatile("fld fa4, %0\n" ::"m"(ctx->f[4])); + asm volatile("fld fa5, %0\n" ::"m"(ctx->f[5])); + asm volatile("fld fa6, %0\n" ::"m"(ctx->f[6])); + asm volatile("fld fa7, %0\n" ::"m"(ctx->f[7])); } /* TODO: not implemented yet (End) */ diff --git a/utils/arch.h b/utils/arch.h index ff1893bfd..fb5d7eea3 100644 --- a/utils/arch.h +++ b/utils/arch.h @@ -157,6 +157,30 @@ enum uftrace_i386_reg_index { UFT_I386_REG_XMM7, }; +enum uftrace_riscv64_reg_index { + UFT_RISCV64_REG_INT_BASE = 0, + /* integer argument registers */ + UFT_RISCV64_REG_A0, + UFT_RISCV64_REG_A1, + UFT_RISCV64_REG_A2, + UFT_RISCV64_REG_A3, + UFT_RISCV64_REG_A4, + UFT_RISCV64_REG_A5, + UFT_RISCV64_REG_A6, + UFT_RISCV64_REG_A7, + + UFT_RISCV64_REG_FLOAT_BASE = 100, + /* floating-point argument registers */ + UFT_RISCV64_REG_FA0, + UFT_RISCV64_REG_FA1, + UFT_RISCV64_REG_FA2, + UFT_RISCV64_REG_FA3, + UFT_RISCV64_REG_FA4, + UFT_RISCV64_REG_FA5, + UFT_RISCV64_REG_FA6, + UFT_RISCV64_REG_FA7, +}; + int arch_register_number(enum uftrace_cpu_arch arch, char *reg_name); int arch_register_at(enum uftrace_cpu_arch arch, bool integer, int idx); int arch_register_index(enum uftrace_cpu_arch arch, int idx); diff --git a/utils/dwarf.c b/utils/dwarf.c index a4b2c9b94..1a796dd73 100644 --- a/utils/dwarf.c +++ b/utils/dwarf.c @@ -399,6 +399,13 @@ static void setup_arg_data(struct arg_data *ad, const char *name, struct uftrace ad->struct_arg_needs_ptr = true; /* struct return will use 'x8' register */ break; + case UFT_CPU_RISCV64: + ad->reg_max = 8; + ad->fpreg_max = 8; + ad->struct_arg_needs_ptr = true; + ad->struct_return_needs_ptr = true; + ad->struct_uses_fpreg = true; + break; default: /* TODO */ ad->broken = true; @@ -586,6 +593,10 @@ static void setup_param_data(struct param_data *data) case UFT_CPU_AARCH64: data->max_struct_size = 16 * 8; break; + case UFT_CPU_RISCV64: + data->max_struct_size = 16 * 8; + data->use_fpregs = true; + break; default: /* TODO */ break; diff --git a/utils/regs.c b/utils/regs.c index 08c74bb7d..9b3ba7382 100644 --- a/utils/regs.c +++ b/utils/regs.c @@ -131,8 +131,42 @@ static const struct uftrace_reg_table uft_i386_reg_table[] = { #undef X86_REG }; +static const struct uftrace_reg_table uft_riscv64_reg_table[] = { +#define RISCV64_REG(_r) \ + { \ +#_r, UFT_RISCV64_REG_##_r \ + } + + /* integer registers */ + RISCV64_REG(A0), + RISCV64_REG(A1), + RISCV64_REG(A2), + RISCV64_REG(A3), + RISCV64_REG(A4), + RISCV64_REG(A5), + RISCV64_REG(A6), + RISCV64_REG(A7), + + /* floating-point registers */ + RISCV64_REG(FA0), + RISCV64_REG(FA1), + RISCV64_REG(FA2), + RISCV64_REG(FA3), + RISCV64_REG(FA4), + RISCV64_REG(FA5), + RISCV64_REG(FA6), + RISCV64_REG(FA7), + +#undef RISCV64_REG +}; + static const struct uftrace_reg_table *arch_reg_tables[] = { - NULL, uft_x86_64_reg_table, uft_arm_reg_table, uft_aarch64_reg_table, uft_i386_reg_table, + NULL, + uft_x86_64_reg_table, + uft_arm_reg_table, + uft_aarch64_reg_table, + uft_i386_reg_table, + uft_riscv64_reg_table, }; static const size_t arch_reg_sizes[] = { @@ -141,11 +175,12 @@ static const size_t arch_reg_sizes[] = { ARRAY_SIZE(uft_arm_reg_table), ARRAY_SIZE(uft_aarch64_reg_table), ARRAY_SIZE(uft_i386_reg_table), + ARRAY_SIZE(uft_riscv64_reg_table), }; /* number of integer registers */ static const int arch_reg_int_sizes[] = { - 0, 6, 4, 8, 2, + 0, 6, 4, 8, 2, 8, }; /* returns uftrace register number for the architecture */ @@ -412,12 +447,82 @@ static const struct uftrace_reg_table uft_aarch64_dwarf_table[] = { static const struct uftrace_reg_table uft_i386_dwarf_table[] = {}; +#define RISCV64_REG_FP_BASE 32 +static const struct uftrace_reg_table uft_riscv64_dwarf_table[] = { + /* support registers used for arguments */ + { + "a0", + DW_OP_reg10, + }, + { + "a1", + DW_OP_reg11, + }, + { + "a2", + DW_OP_reg12, + }, + { + "a3", + DW_OP_reg13, + }, + { + "a4", + DW_OP_reg14, + }, + { + "a5", + DW_OP_reg15, + }, + { + "a6", + DW_OP_reg16, + }, + { + "a7", + DW_OP_reg17, + }, + { + "fa0", + RISCV64_REG_FP_BASE + 0, + }, + { + "fa1", + RISCV64_REG_FP_BASE + 1, + }, + { + "fa2", + RISCV64_REG_FP_BASE + 2, + }, + { + "fa3", + RISCV64_REG_FP_BASE + 3, + }, + { + "fa4", + RISCV64_REG_FP_BASE + 4, + }, + { + "fa5", + RISCV64_REG_FP_BASE + 5, + }, + { + "fa6", + RISCV64_REG_FP_BASE + 6, + }, + { + "fa7", + RISCV64_REG_FP_BASE + 7, + }, +}; + static const struct uftrace_reg_table *arch_dwarf_tables[] = { NULL, uft_x86_64_dwarf_table, uft_arm_dwarf_table, uft_aarch64_dwarf_table, uft_i386_dwarf_table, + uft_riscv64_dwarf_table, }; static const size_t arch_dwarf_sizes[] = { @@ -426,6 +531,7 @@ static const size_t arch_dwarf_sizes[] = { ARRAY_SIZE(uft_arm_dwarf_table), ARRAY_SIZE(uft_aarch64_dwarf_table), ARRAY_SIZE(uft_i386_dwarf_table), + ARRAY_SIZE(uft_riscv64_dwarf_table), }; const char *arch_register_dwarf_name(enum uftrace_cpu_arch arch, int dwarf_reg)