Skip to content

Commit

Permalink
Merge pull request #1815 from gichoel/arch/riscv-support-mcount
Browse files Browse the repository at this point in the history
Experimental support for RISC-V 64bit (RV64G) by Gichoel.
The PLT support is still working in progress and it has a bug
in the floating point return value handling.  But the basic
tracing should work!

Signed-off-by: Namhyung Kim <[email protected]>
  • Loading branch information
namhyung authored Oct 15, 2023
2 parents e37b05c + bacf545 commit b5c3bc6
Show file tree
Hide file tree
Showing 13 changed files with 605 additions and 3 deletions.
34 changes: 34 additions & 0 deletions arch/riscv64/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
LINKFLAGS := -r -z noexecstack

sdir := $(srcdir)/arch/riscv64
odir := $(objdir)/arch/riscv64

include $(srcdir)/Makefile.include

ARCH_ENTRY_SRC = $(wildcard $(sdir)/*.S)
ARCH_MCOUNT_SRC = $(wildcard $(sdir)/mcount-*.c)
ARCH_UFTRACE_SRC = $(sdir)/cpuinfo.c

ARCH_MCOUNT_OBJS = $(patsubst $(sdir)/%.S,$(odir)/%.op,$(ARCH_ENTRY_SRC))
ARCH_MCOUNT_OBJS += $(patsubst $(sdir)/%.c,$(odir)/%.op,$(ARCH_MCOUNT_SRC))
ARCH_UFTRACE_OBJS = $(patsubst $(sdir)/%.c,$(odir)/%.o,$(ARCH_UFTRACE_SRC))

all: $(odir)/entry.op

$(odir)/mcount-entry.op: $(ARCH_MCOUNT_OBJS)
$(QUIET_LINK)$(LD) $(LINKFLAGS) -o $@ $^

$(odir)/uftrace.o: $(ARCH_UFTRACE_OBJS)
$(QUIET_LINK)$(LD) $(LINKFLAGS) -o $@ $^

$(odir)/%.op: $(sdir)/%.S
$(QUIET_ASM)$(CC) $(LIB_CFLAGS) -c -o $@ $<

$(odir)/%.op: $(sdir)/%.c
$(QUIET_CC_FPIC)$(CC) $(LIB_CFLAGS) -c -o $@ $<

$(odir)/%.o: $(sdir)/%.c
$(QUIET_CC)$(CC) $(UFTRACE_CFLAGS) -c -o $@ $<

clean:
$(RM) $(odir)/*.op $(odir)/*.o
24 changes: 24 additions & 0 deletions arch/riscv64/cpuinfo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <stdio.h>
#include <string.h>

int arch_fill_cpuinfo_model(int fd)
{
char buf[1024];
FILE *fp;
int ret = -1;

fp = fopen("/proc/cpuinfo", "r");
if (fp == NULL)
return -1;

while (fgets(buf, sizeof(buf), fp) != NULL) {
if (!strncmp(buf, "isa\t\t: rv64", 11)) {
dprintf(fd, "cpuinfo:desc=RISCV64_%s", &buf[12]);
ret = 0;
break;
}
}

fclose(fp);
return ret;
}
52 changes: 52 additions & 0 deletions arch/riscv64/mcount-arch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#ifndef MCOUNT_ARCH_H
#define MCOUNT_ARCH_H

#define mcount_regs mcount_regs

struct mcount_regs {
unsigned long a0;
unsigned long a1;
unsigned long a2;
unsigned long a3;
unsigned long a4;
unsigned long a5;
unsigned long a6;
unsigned long a7;
};

#define ARG1(x) ((x)->a0)
#define ARG2(x) ((x)->a1)
#define ARG3(x) ((x)->a2)
#define ARG4(x) ((x)->a3)
#define ARG5(x) ((x)->a4)
#define ARG6(x) ((x)->a5)
#define ARG7(x) ((x)->a6)
#define ARG8(x) ((x)->a7)

#define ARCH_MAX_REG_ARGS 8
#define ARCH_MAX_FLOAT_REGS 8

#define HAVE_MCOUNT_ARCH_CONTEXT
struct mcount_arch_context {
double f[ARCH_MAX_FLOAT_REGS];
};

#if defined(__riscv_compressed)
#define NOP_INSN_SIZE 2
#else
#define NOP_INSN_SIZE 4
#endif

/* TODO: not implemented yet (Start) */
#define ARCH_PLT0_SIZE 0
#define ARCH_PLTHOOK_ADDR_OFFSET 0

struct mcount_disasm_engine;
struct mcount_dynamic_info;
struct mcount_disasm_info;

int disasm_check_insns(struct mcount_disasm_engine *disasm, struct mcount_dynamic_info *mdi,
struct mcount_disasm_info *info);
/* TODO: not implemented yet (End) */

#endif /* MCOUNT_ARCH_H */
221 changes: 221 additions & 0 deletions arch/riscv64/mcount-support.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
#include <stdlib.h>

#include "libmcount/internal.h"
#include "utils/filter.h"
#include "utils/utils.h"

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)
{
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)
{
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)
{
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)
{
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) */
Loading

0 comments on commit b5c3bc6

Please sign in to comment.