
The defs.h header will take care of including the various config.h headers. For now, it's just config.h, but we'll add more when we integrate gnulib in. This header should be used instead of config.h, and should be the first include in every .c file. We won't rely on the old behavior where we expected files to include the port's sim-main.h which then includes the common sim-basics.h which then includes config.h. We have a ton of code that includes things before sim-main.h, and it sometimes needs to be that way. Creating a dedicated header avoids the ordering mess and implicit inclusion that shows up otherwise.
331 lines
7.8 KiB
C
331 lines
7.8 KiB
C
/* 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/>. */
|
||
|
||
/* This must come before any other includes. */
|
||
#include "defs.h"
|
||
|
||
#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 (void)
|
||
{
|
||
/* 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
|
||
};
|