aboutsummaryrefslogtreecommitdiff
path: root/sim/bpf/bpf.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/bpf/bpf.c')
-rw-r--r--sim/bpf/bpf.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/sim/bpf/bpf.c b/sim/bpf/bpf.c
new file mode 100644
index 0000000..0fb8d81
--- /dev/null
+++ b/sim/bpf/bpf.c
@@ -0,0 +1,327 @@
+/* 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
+};