-
Notifications
You must be signed in to change notification settings - Fork 479
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1815 from gichoel/arch/riscv-support-mcount
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
Showing
13 changed files
with
605 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, ®_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) */ |
Oops, something went wrong.