diff options
Diffstat (limited to 'sim/bpf/bpf.c')
-rw-r--r-- | sim/bpf/bpf.c | 327 |
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 +}; |