binutils-gdb/sim/bpf/bpf.c

329 lines
7.7 KiB
C
Raw Normal View History

/* eBPF simulator support code
Copyright (C) 2020-2021 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 */
#include "bpf-helpers.h"
/* 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
};