| /* 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 <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, |
| 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 |
| }; |