/* 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 . */ /* 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 "sim-signal.h" #include "cgen-mem.h" #include "cgen-ops.h" #include "cpuall.h" #include "decode.h" #include "decode-be.h" #include "decode-le.h" #include "defs-le.h" /* For SCACHE */ #include "bpf-helpers.h" 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, int cycles) { /* 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 %" PRId64 " (0x%" PRIx64 ")\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 (SIM_CPU *cpu) { /* Do nothing. */ } static void bpfbf_prepare_run (SIM_CPU *cpu) { /* Nothing. */ } static 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 };