mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
b26e2ae7d3
This patch introduces the basics of an instruction-simulator for eBPF. The simulator is based on CGEN. gdb/ChangeLog: 2020-08-04 Jose E. Marchesi <jose.marchesi@oracle.com> * configure.tgt: Set gdb_sim for bpf-*-* targets. sim/ChangeLog: 2020-08-04 Jose E. Marchesi <jose.marchesi@oracle.com> David Faust <david.faust@oracle.com> * configure.tgt (sim_arch): Add entry for bpf-*-*. * configure: Regenerate. * MAINTAINERS: Add maintainer for the BPF simulator. * bpf/Makefile.in: New file. * bpf/bpf-helpers.c: Likewise. * bpf/bpf-helpers.def: Likewise. * bpf/bpf-helpers.h: Likewise. * bpf/bpf-sim.h: Likewise. * bpf/bpf.c: Likewise. * bpf/config.in: Likewise. * bpf/configure.ac: Likewise. * bpf/decode.h: Likewise. * bpf/eng.h: Likewise. * bpf/mloop.in: Likewise. * bpf/sim-if.c: Likewise. * bpf/sim-main.h: Likewise. * bpf/traps.c: Likewise. * bpf/configure: Generate. * bpf/aclocal.m4: Likewise. sim/testsuite/ChangeLog: 2020-08-04 David Faust <david.faust@oracle.com> Jose E. Marchesi <jose.marchesi@oracle.com> * configure: Regenerate. * sim/bpf/allinsn.exp: New file. * sim/bpf/alu.s: Likewise. * sim/bpf/alu32.s: Likewise. * sim/bpf/endbe.s: Likewise. * sim/bpf/endle.s: Likewise. * sim/bpf/jmp.s: Likewise. * sim/bpf/jmp32.s: Likewise. * sim/bpf/ldabs.s: Likewise. * sim/bpf/mem.s: Likewise. * sim/bpf/mov.s: Likewise. * sim/bpf/testutils.inc: Likewise. * sim/bpf/xadd.s: Likewise.
328 lines
7.7 KiB
C
328 lines
7.7 KiB
C
/* eBPF simulator support code
|
||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||
|
||
This file is part of GDB, the GNU debugger.
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
||
#define WANT_CPU_BPFBF
|
||
#define WANT_CPU bpfbf
|
||
|
||
#include "sim-main.h"
|
||
#include "sim-fpu.h"
|
||
#include "cgen-mem.h"
|
||
#include "cgen-ops.h"
|
||
#include "cpuall.h"
|
||
#include "decode.h"
|
||
|
||
#include "defs-le.h" /* For SCACHE */
|
||
|
||
/* It is not possible to include both defs-le.h and defs-be.h due to
|
||
duplicated definitions, so we need a bunch of forward declarations
|
||
here. */
|
||
extern void bpfbf_ebpfle_init_idesc_table (SIM_CPU *);
|
||
extern void bpfbf_ebpfbe_init_idesc_table (SIM_CPU *);
|
||
|
||
uint64_t skb_data_offset;
|
||
|
||
IDESC *bpf_idesc_le;
|
||
IDESC *bpf_idesc_be;
|
||
|
||
|
||
int
|
||
bpfbf_fetch_register (SIM_CPU *current_cpu,
|
||
int rn,
|
||
unsigned char *buf,
|
||
int len)
|
||
{
|
||
if (rn == 11)
|
||
SETTDI (buf, CPU_PC_GET (current_cpu));
|
||
else if (0 <= rn && rn < 10)
|
||
SETTDI (buf, GET_H_GPR (rn));
|
||
else
|
||
return 0;
|
||
|
||
return len;
|
||
}
|
||
|
||
int
|
||
bpfbf_store_register (SIM_CPU *current_cpu,
|
||
int rn,
|
||
unsigned char *buf,
|
||
int len)
|
||
{
|
||
if (rn == 11)
|
||
CPU_PC_SET (current_cpu, GETTDI (buf));
|
||
else if (0 <= rn && rn < 10)
|
||
SET_H_GPR (rn, GETTDI (buf));
|
||
else
|
||
return 0;
|
||
|
||
return len;
|
||
}
|
||
|
||
void
|
||
bpfbf_model_insn_before (SIM_CPU *current_cpu, int first_p)
|
||
{
|
||
/* XXX */
|
||
}
|
||
|
||
void
|
||
bpfbf_model_insn_after (SIM_CPU *current_cpu, int first_p)
|
||
{
|
||
/* XXX */
|
||
}
|
||
|
||
|
||
/***** Instruction helpers. *****/
|
||
|
||
/* The semantic routines for most instructions are expressed in RTL in
|
||
the cpu/bpf.cpu file, and automatically translated to C in the
|
||
sem-*.c files in this directory.
|
||
|
||
However, some of the semantic routines make use of helper C
|
||
functions. This happens when the semantics of the instructions
|
||
can't be expressed in RTL alone in a satisfactory way, or not at
|
||
all.
|
||
|
||
The following functions implement these C helpers. */
|
||
|
||
DI
|
||
bpfbf_endle (SIM_CPU *current_cpu, DI value, UINT bitsize)
|
||
{
|
||
switch (bitsize)
|
||
{
|
||
case 16: return endian_h2le_2(endian_t2h_2(value));
|
||
case 32: return endian_h2le_4(endian_t2h_4(value));
|
||
case 64: return endian_h2le_8(endian_t2h_8(value));
|
||
default: assert(0);
|
||
}
|
||
return value;
|
||
}
|
||
|
||
DI
|
||
bpfbf_endbe (SIM_CPU *current_cpu, DI value, UINT bitsize)
|
||
{
|
||
switch (bitsize)
|
||
{
|
||
case 16: return endian_h2be_2(endian_t2h_2(value));
|
||
case 32: return endian_h2be_4(endian_t2h_4(value));
|
||
case 64: return endian_h2be_8(endian_t2h_8(value));
|
||
default: assert(0);
|
||
}
|
||
return value;
|
||
}
|
||
|
||
DI
|
||
bpfbf_skb_data_offset (SIM_CPU *current_cpu)
|
||
{
|
||
/* Simply return the user-configured value.
|
||
This will be 0 if it has not been set. */
|
||
return skb_data_offset;
|
||
}
|
||
|
||
|
||
VOID
|
||
bpfbf_call (SIM_CPU *current_cpu, INT disp32, UINT src)
|
||
{
|
||
/* eBPF supports two kind of CALL instructions: the so called pseudo
|
||
calls ("bpf to bpf") and external calls ("bpf to helper").
|
||
|
||
Both kind of calls use the same instruction (CALL). However,
|
||
external calls are constructed by passing a constant argument to
|
||
the instruction, that identifies the helper, whereas pseudo calls
|
||
result from expressions involving symbols.
|
||
|
||
We distinguish calls from pseudo-calls with the later having a 1
|
||
stored in the SRC field of the instruction. */
|
||
|
||
if (src == 1)
|
||
{
|
||
/* This is a pseudo-call. */
|
||
|
||
/* XXX allocate a new stack frame and transfer control. For
|
||
that we need to analyze the target function, like the kernel
|
||
verifier does. We better populate a cache
|
||
(function_start_address -> frame_size) so we avoid
|
||
calculating this more than once. */
|
||
/* XXX note that disp32 is PC-relative in number of 64-bit
|
||
words, _minus one_. */
|
||
}
|
||
else
|
||
{
|
||
/* This is a call to a helper.
|
||
|
||
DISP32 contains the helper number. Dispatch to the
|
||
corresponding helper emulator in bpf-helpers.c. */
|
||
|
||
switch (disp32) {
|
||
/* case TRACE_PRINTK: */
|
||
case 7:
|
||
bpf_trace_printk (current_cpu);
|
||
break;
|
||
default:;
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID
|
||
bpfbf_exit (SIM_CPU *current_cpu)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (current_cpu);
|
||
|
||
/* r0 holds "return code" */
|
||
DI r0 = GET_H_GPR (0);
|
||
|
||
printf ("exit %ld (0x%lx)\n", r0, r0);
|
||
|
||
sim_engine_halt (sd, current_cpu, NULL, CPU_PC_GET (current_cpu),
|
||
sim_exited, 0 /* sigrc */);
|
||
}
|
||
|
||
VOID
|
||
bpfbf_breakpoint (SIM_CPU *current_cpu)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (current_cpu);
|
||
|
||
sim_engine_halt (sd, current_cpu, NULL, CPU_PC_GET (current_cpu),
|
||
sim_stopped, SIM_SIGTRAP);
|
||
}
|
||
|
||
/* We use the definitions below instead of the cgen-generated model.c,
|
||
because the later is not really able to work with cpus featuring
|
||
several ISAs. This should be fixed in CGEN. */
|
||
|
||
static void
|
||
bpf_def_model_init ()
|
||
{
|
||
/* Do nothing. */
|
||
}
|
||
|
||
static void
|
||
bpfbf_prepare_run (SIM_CPU *cpu)
|
||
{
|
||
/* Nothing. */
|
||
}
|
||
|
||
void
|
||
bpf_engine_run_full (SIM_CPU *cpu)
|
||
{
|
||
if (current_target_byte_order == BFD_ENDIAN_LITTLE)
|
||
{
|
||
if (!bpf_idesc_le)
|
||
{
|
||
bpfbf_ebpfle_init_idesc_table (cpu);
|
||
bpf_idesc_le = CPU_IDESC (cpu);
|
||
}
|
||
else
|
||
CPU_IDESC (cpu) = bpf_idesc_le;
|
||
|
||
bpfbf_ebpfle_engine_run_full (cpu);
|
||
}
|
||
else
|
||
{
|
||
if (!bpf_idesc_be)
|
||
{
|
||
bpfbf_ebpfbe_init_idesc_table (cpu);
|
||
bpf_idesc_be = CPU_IDESC (cpu);
|
||
}
|
||
else
|
||
CPU_IDESC (cpu) = bpf_idesc_be;
|
||
|
||
bpfbf_ebpfbe_engine_run_full (cpu);
|
||
}
|
||
}
|
||
|
||
#if WITH_FAST
|
||
|
||
void
|
||
bpf_engine_run_fast (SIM_CPU *cpu)
|
||
{
|
||
if (current_target_byte_order == BFD_ENDIAN_LITTLE)
|
||
{
|
||
if (!bpf_idesc_le)
|
||
{
|
||
bpfbf_ebpfle_init_idesc_table (cpu);
|
||
bpf_idesc_le = CPU_IDESC (cpu);
|
||
}
|
||
else
|
||
CPU_IDESC (cpu) = bpf_idesc_le;
|
||
|
||
bpfbf_ebpfle_engine_run_fast (cpu);
|
||
}
|
||
else
|
||
{
|
||
if (!bpf_idesc_be)
|
||
{
|
||
bpfbf_ebpfbe_init_idesc_table (cpu);
|
||
bpf_idesc_be = CPU_IDESC (cpu);
|
||
}
|
||
else
|
||
CPU_IDESC (cpu) = bpf_idesc_be;
|
||
|
||
bpfbf_ebpfbe_engine_run_fast (cpu);
|
||
}
|
||
}
|
||
|
||
#endif /* WITH_FAST */
|
||
|
||
static const CGEN_INSN *
|
||
bpfbf_get_idata (SIM_CPU *cpu, int inum)
|
||
{
|
||
return CPU_IDESC (cpu) [inum].idata;
|
||
}
|
||
|
||
static void
|
||
bpf_init_cpu (SIM_CPU *cpu)
|
||
{
|
||
CPU_REG_FETCH (cpu) = bpfbf_fetch_register;
|
||
CPU_REG_STORE (cpu) = bpfbf_store_register;
|
||
CPU_PC_FETCH (cpu) = bpfbf_h_pc_get;
|
||
CPU_PC_STORE (cpu) = bpfbf_h_pc_set;
|
||
CPU_GET_IDATA (cpu) = bpfbf_get_idata;
|
||
/* Only used by profiling. 0 disables it. */
|
||
CPU_MAX_INSNS (cpu) = 0;
|
||
CPU_INSN_NAME (cpu) = cgen_insn_name;
|
||
CPU_FULL_ENGINE_FN (cpu) = bpf_engine_run_full;
|
||
#if WITH_FAST
|
||
CPU_FAST_ENGINE_FN (cpu) = bpf_engine_run_fast;
|
||
#else
|
||
CPU_FAST_ENGINE_FN (cpu) = bpf_engine_run_full;
|
||
#endif
|
||
}
|
||
|
||
static const SIM_MODEL bpf_models[] =
|
||
{
|
||
{ "bpf-def", & bpf_mach, MODEL_BPF_DEF, NULL, bpf_def_model_init },
|
||
{ 0 }
|
||
};
|
||
|
||
static const SIM_MACH_IMP_PROPERTIES bpfbf_imp_properties =
|
||
{
|
||
sizeof (SIM_CPU),
|
||
#if WITH_SCACHE
|
||
sizeof (SCACHE)
|
||
#else
|
||
0
|
||
#endif
|
||
};
|
||
|
||
const SIM_MACH bpf_mach =
|
||
{
|
||
"bpf", "bpf", MACH_BPF,
|
||
32, 32, & bpf_models[0], & bpfbf_imp_properties,
|
||
bpf_init_cpu,
|
||
bpfbf_prepare_run
|
||
};
|