2020-08-05 00:09:16 +08:00
|
|
|
|
/* eBPF simulator support code
|
2021-01-01 16:03:39 +08:00
|
|
|
|
Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
2020-08-05 00:09:16 +08:00
|
|
|
|
|
|
|
|
|
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 */
|
2020-09-03 22:24:51 +08:00
|
|
|
|
#include "bpf-helpers.h"
|
2020-08-05 00:09:16 +08:00
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
};
|