Skip to content

Commit

Permalink
arch/riscv64: Update argument handling feature to support riscv64
Browse files Browse the repository at this point in the history
This is work to support argument handling on the RISC-V 64-bit
architecture.

If you are curious about how RISC-V 64bit register numbers map to
DWARF register numbers, please refer to the 'RISC-V Run-time ABI
Specification' in 'RISC-V ELF psABI'.

Tested-by: Seonghee Jin <[email protected]>
Tested-by: Jungmin Kim <[email protected]>
Tested-by: SeokMin Kwon <[email protected]>
Reviewed-by: Honggyu Kim <[email protected]>
Signed-off-by: Gichoel Choi <[email protected]>
  • Loading branch information
gichoel committed Sep 9, 2023
1 parent 17213c2 commit aad0773
Show file tree
Hide file tree
Showing 3 changed files with 318 additions and 7 deletions.
204 changes: 199 additions & 5 deletions arch/riscv64/mcount-support.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, &reg_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) */
11 changes: 11 additions & 0 deletions utils/dwarf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
110 changes: 108 additions & 2 deletions utils/regs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand All @@ -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 */
Expand Down Expand Up @@ -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[] = {
Expand All @@ -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)
Expand Down

0 comments on commit aad0773

Please sign in to comment.