|  | /* eBPF simulator support code | 
|  | Copyright (C) 2020-2022 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 "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, | 
|  | void *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, | 
|  | const void *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 | 
|  | }; |