Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

arch/riscv64: Add basic mcount tracing support for RISC-V 64bit #1815

Merged
merged 3 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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