|  | ;; Linux BPF CPU description  -*- Scheme -*- | 
|  | ;; Copyright (C) 2019 Free Software Foundation, Inc. | 
|  | ;; | 
|  | ;; Contributed by Oracle Inc. | 
|  | ;; | 
|  | ;; This file is part of the GNU Binutils and of GDB. | 
|  | ;; | 
|  | ;; 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, write to the Free Software | 
|  | ;; Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA | 
|  | ;; 02110-1301, USA. | 
|  |  | 
|  | ;; This file contains a CGEN CPU description for the Linux kernel eBPF | 
|  | ;; instruction set.  eBPF is documented in the linux kernel source | 
|  | ;; tree.  See linux/Documentation/networking/filter.txt, and also the | 
|  | ;; sources in the networking subsystem, notably | 
|  | ;; linux/net/core/filter.c. | 
|  |  | 
|  | (include "simplify.inc") | 
|  |  | 
|  | (define-arch | 
|  | (name bpf) | 
|  | (comment "Linux kernel BPF") | 
|  | (insn-lsb0? #t) | 
|  | ;; XXX explain the default-alignment setting is for the simulator. | 
|  | ;; It is confusing that the simulator follows the emulated memory | 
|  | ;; access conventions for fetching instructions by pieces... | 
|  | (default-alignment unaligned) | 
|  | (machs bpf xbpf) | 
|  | (isas ebpfle ebpfbe xbpfle xbpfbe)) | 
|  |  | 
|  | ;;;; The ISAs | 
|  |  | 
|  | ;; Logically, eBPF comforms a single instruction set featuring two | 
|  | ;; kind of instructions: 64-bit instructions and 128-bit instructions. | 
|  | ;; | 
|  | ;; The 64-bit instructions have the form: | 
|  | ;; | 
|  | ;;      code:8 regs:8 offset:16 imm:32 | 
|  | ;; | 
|  | ;; Whereas the 128-bit instructions (at the moment there is only one | 
|  | ;; of such instructions, lddw) have the form: | 
|  | ;; | 
|  | ;;      code:8 regs:8 offset:16 imm:32 unused:32 imm:32 | 
|  | ;; | 
|  | ;; In both formats `regs' is itself composed by two fields: | 
|  | ;; | 
|  | ;;      dst:4 src:4 | 
|  | ;; | 
|  | ;; The ISA is supposed to be orthogonal to endianness: the endianness | 
|  | ;; of the instruction fields follow the endianness of the host running | 
|  | ;; the eBPF program, and that's all.  However, this is not entirely | 
|  | ;; true.  The definition of an eBPF code in the Linux kernel is: | 
|  | ;; | 
|  | ;; struct bpf_insn { | 
|  | ;;	__u8	code;		/* opcode */ | 
|  | ;;	__u8	dst_reg:4;	/* dest register */ | 
|  | ;;	__u8	src_reg:4;	/* source register */ | 
|  | ;;	__s16	off;		/* signed offset */ | 
|  | ;;	__s32	imm;		/* signed immediate constant */ | 
|  | ;; }; | 
|  | ;; | 
|  | ;; Since the ordering of fields in C bitmaps is defined by the | 
|  | ;; implementation, the impact of endianness in the encoding of eBPF | 
|  | ;; instructions is effectively defined by GCC.  In particular, GCC | 
|  | ;; places dst_reg before src_reg in little-endian code, and the other | 
|  | ;; way around in big-endian code. | 
|  | ;; | 
|  | ;; So, in reality, eBPF comprises two instruction sets: one for | 
|  | ;; little-endian with instructions like: | 
|  | ;; | 
|  | ;;   code:8 src:4 dst:4 offset:16 imm:32 [unused:32 imm:32] | 
|  | ;; | 
|  | ;; and another for big-endian with instructions like: | 
|  | ;; | 
|  | ;;   code:8 dst:4 src:4 offset:16 imm:32 [unused:32 imm:32] | 
|  | ;; | 
|  | ;; where `offset' and the immediate fields are encoded in | 
|  | ;; little-endian and big-endian byte-order, respectively. | 
|  |  | 
|  | (define-pmacro (define-bpf-isa x-endian) | 
|  | (define-isa | 
|  | (name (.sym ebpf x-endian)) | 
|  | (comment "The eBPF instruction set") | 
|  | ;; Default length to record in ifields.  This is used in | 
|  | ;; calculations involving bit numbers. | 
|  | (default-insn-word-bitsize 64) | 
|  | ;; Length of an unknown instruction.  Used by disassembly and by the | 
|  | ;; simulator's invalid insn handler. | 
|  | (default-insn-bitsize 64) | 
|  | ;; Number of bits of insn that can be initially fetched.  This is | 
|  | ;; the size of the smallest insn. | 
|  | (base-insn-bitsize 64))) | 
|  |  | 
|  | (define-bpf-isa le) | 
|  | (define-bpf-isa be) | 
|  |  | 
|  | (define-pmacro (define-xbpf-isa x-endian) | 
|  | (define-isa | 
|  | (name (.sym xbpf x-endian)) | 
|  | (comment "The xBPF instruction set") | 
|  | (default-insn-word-bitsize 64) | 
|  | (default-insn-bitsize 64) | 
|  | (base-insn-bitsize 64))) | 
|  |  | 
|  | (define-xbpf-isa le) | 
|  | (define-xbpf-isa be) | 
|  |  | 
|  | (define-pmacro all-isas () (ISA ebpfle,ebpfbe,xbpfle,xbpfbe)) | 
|  | (define-pmacro xbpf-isas () (ISA xbpfle,xbpfbe)) | 
|  |  | 
|  | (define-pmacro (endian-isas x-endian) | 
|  | ((ISA (.sym ebpf x-endian) (.sym xbpf x-endian)))) | 
|  |  | 
|  | ;;;; Hardware Hierarchy | 
|  |  | 
|  | ;; | 
|  | ;;         bpf            architecture | 
|  | ;;          | | 
|  | ;;        bpfbf           cpu-family | 
|  | ;;      /       \ | 
|  | ;;     bpf     xbpf       machine | 
|  | ;;      |       | | 
|  | ;;   bpf-def  xbpf-def    model | 
|  |  | 
|  | (define-cpu | 
|  | (name bpfbf) | 
|  | (comment "Linux kernel eBPF virtual CPU") | 
|  | (insn-endian big) | 
|  | (word-bitsize 64)) | 
|  |  | 
|  | (define-mach | 
|  | (name bpf) | 
|  | (comment "Linux eBPF") | 
|  | (cpu bpfbf) | 
|  | (isas ebpfle ebpfbe)) | 
|  |  | 
|  | (define-model | 
|  | (name bpf-def) | 
|  | (comment "Linux eBPF default model") | 
|  | (mach bpf) | 
|  | (unit u-exec "execution unit" () | 
|  | 1 ; issue | 
|  | 1 ; done | 
|  | () ; state | 
|  | () ; inputs | 
|  | () ; outputs | 
|  | () ; profile action (default) | 
|  | )) | 
|  |  | 
|  | (define-mach | 
|  | (name xbpf) | 
|  | (comment "Experimental BPF") | 
|  | (cpu bpfbf) | 
|  | (isas ebpfle ebpfbe xbpfle xbpfbe)) | 
|  |  | 
|  | (define-model | 
|  | (name xbpf-def) | 
|  | (comment "xBPF default model") | 
|  | (mach xbpf) | 
|  | (unit u-exec "execution unit" () | 
|  | 1 ; issue | 
|  | 1 ; done | 
|  | () ; state | 
|  | () ; inputs | 
|  | () ; outputs | 
|  | () ; profile action (default) | 
|  | )) | 
|  |  | 
|  | ;;;; Hardware Elements | 
|  |  | 
|  | ;; eBPF programs can access 10 general-purpose registers which are | 
|  | ;; 64-bit. | 
|  |  | 
|  | (define-hardware | 
|  | (name h-gpr) | 
|  | (comment "General Purpose Registers") | 
|  | (attrs all-isas (MACH bpf xbpf)) | 
|  | (type register DI (16)) | 
|  | (indices keyword "%" | 
|  | ;; XXX the frame pointer fp is read-only, so it should | 
|  | ;; go in a different hardware. | 
|  | (;; ABI names.  Take priority when disassembling. | 
|  | (r0 0) (r1 1) (r2 2) (r3 3) (r4 4) (r5 5) (r6 6) | 
|  | (r7 7) (r8 8) (r9 9) (fp 10) | 
|  | ;; Additional names recognized when assembling. | 
|  | (r0 0) (r6 6) (r10 10)))) | 
|  |  | 
|  | ;; The program counter.  CGEN requires it, even if it is not visible | 
|  | ;; to eBPF programs. | 
|  |  | 
|  | (define-hardware | 
|  | (name h-pc) | 
|  | (comment "program counter") | 
|  | (attrs PC PROFILE all-isas) | 
|  | (type pc UDI) | 
|  | (get () (raw-reg h-pc)) | 
|  | (set (newval) (set (raw-reg h-pc) newval))) | 
|  |  | 
|  | ;; A 64-bit h-sint to be used by the imm64 operand below.  XXX this | 
|  | ;; shouldn't be needed, as h-sint is supposed to be able to hold | 
|  | ;; 64-bit values.  However, in practice CGEN limits h-sint to 32 bits | 
|  | ;; in 32-bit hosts.  To be fixed in CGEN. | 
|  |  | 
|  | (dnh h-sint64 "signed 64-bit integer" (all-isas) (immediate DI) | 
|  | () () ()) | 
|  |  | 
|  | ;;;; The Instruction Sets | 
|  |  | 
|  | ;;; Fields and Opcodes | 
|  |  | 
|  | ;; Convenience macro to shorten the definition of the fields below. | 
|  | (define-pmacro (dwf x-name x-comment x-attrs | 
|  | x-word-offset x-word-length x-start x-length | 
|  | x-mode) | 
|  | "Define a field including its containing word." | 
|  | (define-ifield | 
|  | (name x-name) | 
|  | (comment x-comment) | 
|  | (.splice attrs (.unsplice x-attrs)) | 
|  | (word-offset x-word-offset) | 
|  | (word-length x-word-length) | 
|  | (start x-start) | 
|  | (length x-length) | 
|  | (mode x-mode))) | 
|  |  | 
|  | ;; For arithmetic and jump instructions the 8-bit code field is | 
|  | ;; subdivided in: | 
|  | ;; | 
|  | ;;  op-code:4 op-src:1 op-class:3 | 
|  |  | 
|  | (dwf f-op-code "eBPF opcode code" (all-isas) 0 8 7 4 UINT) | 
|  | (dwf f-op-src "eBPF opcode source" (all-isas) 0 8 3 1 UINT) | 
|  | (dwf f-op-class "eBPF opcode instruction class" (all-isas) 0 8 2 3 UINT) | 
|  |  | 
|  | (define-normal-insn-enum insn-op-code-alu "eBPF instruction codes" | 
|  | (all-isas) OP_CODE_ f-op-code | 
|  | (;; Codes for OP_CLASS_ALU and OP_CLASS_ALU64 | 
|  | (ADD #x0) (SUB #x1) (MUL #x2) (DIV #x3) (OR #x4) (AND #x5) | 
|  | (LSH #x6) (RSH #x7) (NEG #x8) (MOD #x9) (XOR #xa) (MOV #xb) | 
|  | (ARSH #xc) (END #xd) | 
|  | ;; xBPF-only: signed div, signed mod | 
|  | (SDIV #xe) (SMOD #xf) | 
|  | ;; Codes for OP_CLASS_JMP | 
|  | (JA #x0) (JEQ #x1) (JGT #x2) (JGE #x3) (JSET #x4) | 
|  | (JNE #x5) (JSGT #x6) (JSGE #x7) (CALL #x8) (EXIT #x9) | 
|  | (JLT #xa) (JLE #xb) (JSLT #xc) (JSLE #xd))) | 
|  |  | 
|  | (define-normal-insn-enum insn-op-src "eBPF instruction source" | 
|  | (all-isas) OP_SRC_ f-op-src | 
|  | ;; X => use `src' as source operand. | 
|  | ;; K => use `imm32' as source operand. | 
|  | ((K #b0) (X #b1))) | 
|  |  | 
|  | (define-normal-insn-enum insn-op-class "eBPF instruction class" | 
|  | (all-isas) OP_CLASS_ f-op-class | 
|  | ((LD    #b000) (LDX   #b001) (ST    #b010) (STX   #b011) | 
|  | (ALU   #b100) (JMP   #b101) (JMP32 #b110) (ALU64 #b111))) | 
|  |  | 
|  | ;; For load/store instructions, the 8-bit code field is subdivided in: | 
|  | ;; | 
|  | ;; op-mode:3 op-size:2 op-class:3 | 
|  |  | 
|  | (dwf f-op-mode "eBPF opcode mode" (all-isas) 0 8 7 3 UINT) | 
|  | (dwf f-op-size "eBPF opcode size" (all-isas) 0 8 4 2 UINT) | 
|  |  | 
|  | (define-normal-insn-enum insn-op-mode "eBPF load/store instruction modes" | 
|  | (all-isas) OP_MODE_ f-op-mode | 
|  | ((IMM #b000) (ABS #b001) (IND #b010) (MEM #b011) | 
|  | ;; #b100 and #b101 are used in classic BPF only, reserved in eBPF. | 
|  | (XADD #b110))) | 
|  |  | 
|  | (define-normal-insn-enum insn-op-size "eBPF load/store instruction sizes" | 
|  | (all-isas) OP_SIZE_ f-op-size | 
|  | ((W  #b00)   ;; Word:        4 byte | 
|  | (H  #b01)   ;; Half-word:   2 byte | 
|  | (B  #b10)   ;; Byte:        1 byte | 
|  | (DW #b11))) ;; Double-word: 8 byte | 
|  |  | 
|  | ;; The fields for the source and destination registers are a bit | 
|  | ;; tricky.  Due to the bizarre nibble swap between little-endian and | 
|  | ;; big-endian ISAs we need to keep different variants of the fields. | 
|  | ;; | 
|  | ;; Note that f-regs is used in the format spec of instructions that do | 
|  | ;; NOT use registers, where endianness is irrelevant i.e. f-regs is a | 
|  | ;; constant 0 opcode. | 
|  |  | 
|  | (dwf f-dstle "eBPF dst register field" ((ISA ebpfle xbpfle)) 8 8 3 4 UINT) | 
|  | (dwf f-srcle "eBPF source register field" ((ISA ebpfle xbpfle)) 8 8 7 4 UINT) | 
|  |  | 
|  | (dwf f-dstbe "eBPF dst register field" ((ISA ebpfbe xbpfbe)) 8 8 7 4 UINT) | 
|  | (dwf f-srcbe "eBPF source register field" ((ISA ebpfbe xbpfbe)) 8 8 3 4 UINT) | 
|  |  | 
|  | (dwf f-regs "eBPF registers field" (all-isas) 8 8 7 8 UINT) | 
|  |  | 
|  | ;; Finally, the fields for the immediates. | 
|  | ;; | 
|  | ;; The 16-bit offsets and 32-bit immediates do not present any special | 
|  | ;; difficulty: we put them in their own instruction word so the | 
|  | ;; byte-endianness will be properly applied. | 
|  |  | 
|  | (dwf f-offset16 "eBPF offset field" (all-isas) 16 16 15 16 HI) | 
|  | (dwf f-imm32 "eBPF 32-bit immediate field" (all-isas) 32 32 31 32 INT) | 
|  |  | 
|  | ;; For the disjoint 64-bit signed immediate, however, we need to use a | 
|  | ;; multi-ifield. | 
|  |  | 
|  | (dwf f-imm64-a "eBPF 64-bit immediate a" (all-isas) 32 32 31 32 UINT) | 
|  | (dwf f-imm64-b "eBPF 64-bit immediate b" (all-isas) 64 32 31 32 UINT) | 
|  | (dwf f-imm64-c "eBPF 64-bit immediate c" (all-isas) 96 32 31 32 UINT) | 
|  |  | 
|  | (define-multi-ifield | 
|  | (name f-imm64) | 
|  | (comment "eBPF 64-bit immediate field") | 
|  | (attrs all-isas) | 
|  | (mode DI) | 
|  | (subfields f-imm64-a f-imm64-b f-imm64-c) | 
|  | (insert (sequence () | 
|  | (set (ifield f-imm64-b) (const 0)) | 
|  | (set (ifield f-imm64-c) (srl (ifield f-imm64) (const 32))) | 
|  | (set (ifield f-imm64-a) (and (ifield f-imm64) (const #xffffffff))))) | 
|  | (extract (sequence () | 
|  | (set (ifield f-imm64) | 
|  | (or (sll UDI (zext UDI (ifield f-imm64-c)) (const 32)) | 
|  | (zext UDI (ifield f-imm64-a))))))) | 
|  |  | 
|  | ;;; Operands | 
|  |  | 
|  | ;; A couple of source and destination register operands are defined | 
|  | ;; for each ISA: ebpfle and ebpfbe. | 
|  |  | 
|  | (dno dstle "destination register" ((ISA ebpfle xbpfle)) h-gpr f-dstle) | 
|  | (dno srcle "source register" ((ISA ebpfle xbpfle)) h-gpr f-srcle) | 
|  |  | 
|  | (dno dstbe "destination register" ((ISA ebpfbe xbpfbe)) h-gpr f-dstbe) | 
|  | (dno srcbe "source register" ((ISA ebpfbe xbpfbe)) h-gpr f-srcbe) | 
|  |  | 
|  | ;; Jump instructions have a 16-bit PC-relative address. | 
|  | ;; CALL instructions have a 32-bit PC-relative address. | 
|  |  | 
|  | (dno disp16 "16-bit PC-relative address" (all-isas PCREL-ADDR) h-sint | 
|  | f-offset16) | 
|  | (dno disp32 "32-bit PC-relative address" (all-isas PCREL-ADDR) h-sint | 
|  | f-imm32) | 
|  |  | 
|  | ;; Immediate operands in eBPF are signed, and we want the disassembler | 
|  | ;; to print negative values in a sane way.  Therefore we use the macro | 
|  | ;; below to register a printer, which is itself defined as a C | 
|  | ;; function in bpf.opc. | 
|  |  | 
|  | ;; define-normal-signed-immediate-operand | 
|  | (define-pmacro (dnsio x-name x-comment x-attrs x-type x-index) | 
|  | (define-operand | 
|  | (name x-name) | 
|  | (comment x-comment) | 
|  | (.splice attrs (.unsplice x-attrs)) | 
|  | (type x-type) | 
|  | (index x-index) | 
|  | (handlers (print "immediate")))) | 
|  |  | 
|  | (dnsio imm32 "32-bit immediate" (all-isas) h-sint f-imm32) | 
|  | (dnsio offset16 "16-bit offset" (all-isas) h-sint f-offset16) | 
|  |  | 
|  | ;; The 64-bit immediate cannot use the default | 
|  | ;; cgen_parse_signed_integer, because it assumes operands are at much | 
|  | ;; 32-bit wide.  Use our own. | 
|  |  | 
|  | (define-operand | 
|  | (name imm64) | 
|  | (comment "64-bit immediate") | 
|  | (attrs all-isas) | 
|  | (type h-sint64) | 
|  | (index f-imm64) | 
|  | (handlers (parse "imm64") (print "immediate"))) | 
|  |  | 
|  | ;; The endle/endbe instructions take an operand to specify the word | 
|  | ;; width in endianness conversions.  We use both a parser and printer, | 
|  | ;; which are defined as C functions in bpf.opc. | 
|  |  | 
|  | (define-operand | 
|  | (name endsize) | 
|  | (comment "endianness size immediate: 16, 32 or 64") | 
|  | (attrs all-isas) | 
|  | (type h-uint) | 
|  | (index f-imm32) | 
|  | (handlers (parse "endsize") (print "endsize"))) | 
|  |  | 
|  | ;;; ALU instructions | 
|  |  | 
|  | ;; For each opcode in insn-op-code-alu representing and integer | 
|  | ;; arithmetic instruction (ADD, SUB, etc) we define a bunch of | 
|  | ;; instruction variants: | 
|  | ;; | 
|  | ;;   ADD[32]{i,r}le for the little-endian ISA | 
|  | ;;   ADD[32]{i,r}be for the big-endian ISA | 
|  | ;; | 
|  | ;; The `i' variants perform `dst OP imm32 -> dst' operations. | 
|  | ;; The `r' variants perform `dst OP src -> dst' operations. | 
|  | ;; | 
|  | ;; The variants with 32 in their name are of ALU class.  Otherwise | 
|  | ;; they are ALU64 class. | 
|  |  | 
|  | (define-pmacro (define-alu-insn-un x-basename x-suffix x-op-class x-op-code | 
|  | x-endian x-mode x-semop) | 
|  | (dni (.sym x-basename x-suffix x-endian) | 
|  | (.str x-basename x-suffix) | 
|  | (endian-isas x-endian) | 
|  | (.str x-basename x-suffix " $dst" x-endian) | 
|  | (+ (f-imm32 0) (f-offset16 0) ((.sym f-src x-endian) 0) (.sym dst x-endian) | 
|  | x-op-class OP_SRC_K x-op-code) | 
|  | (set x-mode (.sym dst x-endian) (x-semop x-mode (.sym dst x-endian))) | 
|  | ())) | 
|  |  | 
|  | (define-pmacro (define-alu-insn-bin x-basename x-suffix x-op-class x-op-code | 
|  | x-endian x-mode x-semop x-isas) | 
|  | (begin | 
|  | ;; dst = dst OP immediate | 
|  | (dni (.sym x-basename x-suffix "i" x-endian) | 
|  | (.str x-basename x-suffix " immediate") | 
|  | (.splice (.unsplice x-isas)) | 
|  | (.str x-basename x-suffix " $dst" x-endian ",$imm32") | 
|  | (+ imm32 (f-offset16 0) ((.sym f-src x-endian) 0) (.sym dst x-endian) | 
|  | x-op-class OP_SRC_K x-op-code) | 
|  | (set x-mode (.sym dst x-endian) (x-semop x-mode (.sym dst x-endian) imm32)) | 
|  | ()) | 
|  | ;; dst = dst OP src | 
|  | (dni (.sym x-basename x-suffix "r" x-endian) | 
|  | (.str x-basename x-suffix " register") | 
|  | (.splice (.unsplice x-isas)) | 
|  | (.str x-basename x-suffix " $dst" x-endian ",$src" x-endian) | 
|  | (+ (f-imm32 0) (f-offset16 0) (.sym src x-endian) (.sym dst x-endian) | 
|  | x-op-class OP_SRC_X x-op-code) | 
|  | (set x-mode (.sym dst x-endian) | 
|  | (x-semop x-mode (.sym dst x-endian) (.sym src x-endian))) | 
|  | ()))) | 
|  |  | 
|  | (define-pmacro (define-alu-insn-mov x-basename x-suffix x-op-class x-op-code | 
|  | x-endian x-mode) | 
|  | (begin | 
|  | (dni (.sym mov x-suffix "i" x-endian) | 
|  | (.str mov x-suffix " immediate") | 
|  | (endian-isas x-endian) | 
|  | (.str x-basename x-suffix " $dst" x-endian ",$imm32") | 
|  | (+ imm32 (f-offset16 0) ((.sym f-src x-endian) 0) (.sym dst x-endian) | 
|  | x-op-class OP_SRC_K x-op-code) | 
|  | (set x-mode (.sym dst x-endian) imm32) | 
|  | ()) | 
|  | (dni (.sym mov x-suffix "r" x-endian) | 
|  | (.str mov x-suffix " register") | 
|  | (endian-isas x-endian) | 
|  | (.str x-basename x-suffix " $dst" x-endian ",$src" x-endian) | 
|  | (+ (f-imm32 0) (f-offset16 0) (.sym src x-endian) (.sym dst x-endian) | 
|  | x-op-class OP_SRC_X x-op-code) | 
|  | (set x-mode (.sym dst x-endian) (.sym src x-endian)) | 
|  | ()))) | 
|  |  | 
|  |  | 
|  | ;; Unary ALU instructions (neg) | 
|  | (define-pmacro (daiu x-basename x-op-code x-endian x-semop) | 
|  | (begin | 
|  | (define-alu-insn-un x-basename "" OP_CLASS_ALU64 x-op-code x-endian DI x-semop) | 
|  | (define-alu-insn-un x-basename "32" OP_CLASS_ALU x-op-code x-endian USI x-semop))) | 
|  |  | 
|  | ;; Binary ALU instructions (all the others) | 
|  | ;; For ALU32: DST = (u32) DST OP (u32) SRC is correct semantics | 
|  | (define-pmacro (daib x-basename x-op-code x-endian x-semop x-isas) | 
|  | (begin | 
|  | (define-alu-insn-bin x-basename "" OP_CLASS_ALU64 x-op-code x-endian DI x-semop x-isas) | 
|  | (define-alu-insn-bin x-basename "32" OP_CLASS_ALU x-op-code x-endian USI x-semop x-isas))) | 
|  |  | 
|  | ;; Move ALU instructions (mov) | 
|  | (define-pmacro (daim x-basename x-op-code x-endian) | 
|  | (begin | 
|  | (define-alu-insn-mov x-basename "" OP_CLASS_ALU64 x-op-code x-endian DI) | 
|  | (define-alu-insn-mov x-basename "32" OP_CLASS_ALU x-op-code x-endian USI))) | 
|  |  | 
|  | (define-pmacro (define-alu-instructions x-endian) | 
|  | (begin | 
|  | (daib add OP_CODE_ADD x-endian add (endian-isas x-endian)) | 
|  | (daib sub OP_CODE_SUB x-endian sub (endian-isas x-endian)) | 
|  | (daib mul OP_CODE_MUL x-endian mul (endian-isas x-endian)) | 
|  | (daib div OP_CODE_DIV x-endian udiv (endian-isas x-endian)) | 
|  | (daib or  OP_CODE_OR x-endian or (endian-isas x-endian)) | 
|  | (daib and OP_CODE_AND x-endian and (endian-isas x-endian)) | 
|  | (daib lsh OP_CODE_LSH x-endian sll (endian-isas x-endian)) | 
|  | (daib rsh OP_CODE_RSH x-endian srl (endian-isas x-endian)) | 
|  | (daib mod OP_CODE_MOD x-endian umod (endian-isas x-endian)) | 
|  | (daib xor OP_CODE_XOR x-endian xor (endian-isas x-endian)) | 
|  | (daib arsh OP_CODE_ARSH x-endian sra (endian-isas x-endian)) | 
|  | (daib sdiv OP_CODE_SDIV x-endian div ((ISA (.sym xbpf x-endian)))) | 
|  | (daib smod OP_CODE_SMOD x-endian mod ((ISA (.sym xbpf x-endian)))) | 
|  | (daiu neg OP_CODE_NEG x-endian neg) | 
|  | (daim mov OP_CODE_MOV x-endian))) | 
|  |  | 
|  | (define-alu-instructions le) | 
|  | (define-alu-instructions be) | 
|  |  | 
|  | ;;; Endianness conversion instructions | 
|  |  | 
|  | ;; The endianness conversion instructions come in several variants: | 
|  | ;; | 
|  | ;;  END{le,be}le for the little-endian ISA | 
|  | ;;  END{le,be}be for the big-endian ISA | 
|  | ;; | 
|  | ;; Please do not be confused by the repeated `be' and `le' here.  Each | 
|  | ;; ISA has both endle and endbe instructions.  It is the disposition | 
|  | ;; of the source and destination register fields that change between | 
|  | ;; ISAs, not the semantics of the instructions themselves (see section | 
|  | ;; "The ISAs" above in this very file.) | 
|  |  | 
|  | (define-pmacro (define-endian-insn x-suffix x-op-src x-endian) | 
|  | (dni (.sym "end" x-suffix x-endian) | 
|  | (.str "end" x-suffix " register") | 
|  | (endian-isas x-endian) | 
|  | (.str "end" x-suffix " $dst" x-endian ",$endsize") | 
|  | (+  (f-offset16 0) ((.sym f-src x-endian) 0) (.sym dst x-endian) endsize | 
|  | OP_CLASS_ALU x-op-src OP_CODE_END) | 
|  | (set (.sym dst x-endian) | 
|  | (c-call DI (.str "bpfbf_end" x-suffix) (.sym dst x-endian) endsize)) | 
|  | ())) | 
|  |  | 
|  | (define-endian-insn "le" OP_SRC_K le) | 
|  | (define-endian-insn "be" OP_SRC_X le) | 
|  | (define-endian-insn "le" OP_SRC_K be) | 
|  | (define-endian-insn "be" OP_SRC_X be) | 
|  |  | 
|  | ;;; Load/Store instructions | 
|  |  | 
|  | ;; The lddw instruction takes a 64-bit immediate as an operand.  Since | 
|  | ;; this instruction also takes a `dst' operand, we need to define a | 
|  | ;; variant for each ISA: | 
|  | ;; | 
|  | ;;  LDDWle for the little-endian ISA | 
|  | ;;  LDDWbe for the big-endian ISA | 
|  |  | 
|  | (define-pmacro (define-lddw x-endian) | 
|  | (dni (.sym lddw x-endian) | 
|  | (.str "lddw" x-endian) | 
|  | (endian-isas x-endian) | 
|  | (.str "lddw $dst" x-endian ",$imm64") | 
|  | (+ imm64 (f-offset16 0) ((.sym f-src x-endian) 0) | 
|  | (.sym dst x-endian) | 
|  | OP_CLASS_LD OP_SIZE_DW OP_MODE_IMM) | 
|  | (set DI (.sym dst x-endian) imm64) | 
|  | ())) | 
|  |  | 
|  | (define-lddw le) | 
|  | (define-lddw be) | 
|  |  | 
|  | ;; The absolute load instructions are non-generic loads designed to be | 
|  | ;; used in socket filters.  They come in several variants: | 
|  | ;; | 
|  | ;; LDABS{w,h,b,dw} | 
|  |  | 
|  | (define-pmacro (dlabs x-suffix x-size x-smode) | 
|  | (dni (.sym "ldabs" x-suffix) | 
|  | (.str "ldabs" x-suffix) | 
|  | (all-isas) | 
|  | (.str "ldabs" x-suffix " $imm32") | 
|  | (+ imm32 (f-offset16 0) (f-regs 0) | 
|  | OP_CLASS_LD OP_MODE_ABS (.sym OP_SIZE_ x-size)) | 
|  | (set x-smode | 
|  | (reg x-smode h-gpr 0) | 
|  | (mem x-smode | 
|  | (add DI | 
|  | (mem DI | 
|  | (add DI | 
|  | (reg DI h-gpr 6) ;; Pointer to struct sk_buff | 
|  | (c-call "bpfbf_skb_data_offset"))) | 
|  | imm32))) | 
|  | ;; XXX this clobbers R1-R5 | 
|  | ())) | 
|  |  | 
|  | (dlabs "w" W SI) | 
|  | (dlabs "h" H HI) | 
|  | (dlabs "b" B QI) | 
|  | (dlabs "dw" DW DI) | 
|  |  | 
|  | ;; The indirect load instructions are non-generic loads designed to be | 
|  | ;; used in socket filters.  They come in several variants: | 
|  | ;; | 
|  | ;; LDIND{w,h,b,dw}le for the little-endian ISA | 
|  | ;; LDIND[w,h,b,dw}be for the big-endian ISA | 
|  |  | 
|  | (define-pmacro (dlind x-suffix x-size x-endian x-smode) | 
|  | (dni (.sym "ldind" x-suffix x-endian) | 
|  | (.str "ldind" x-suffix) | 
|  | (endian-isas x-endian) | 
|  | (.str "ldind" x-suffix " $src" x-endian ",$imm32") | 
|  | (+ imm32 (f-offset16 0) ((.sym f-dst x-endian) 0) (.sym src x-endian) | 
|  | OP_CLASS_LD OP_MODE_IND (.sym OP_SIZE_ x-size)) | 
|  | (set x-smode | 
|  | (reg x-smode h-gpr 0) | 
|  | (mem x-smode | 
|  | (add DI | 
|  | (mem DI | 
|  | (add DI | 
|  | (reg DI h-gpr 6) ;; Pointer to struct sk_buff | 
|  | (c-call "bpfbf_skb_data_offset"))) | 
|  | (add DI | 
|  | (.sym src x-endian) | 
|  | imm32)))) | 
|  | ;; XXX this clobbers R1-R5 | 
|  | ())) | 
|  |  | 
|  | (define-pmacro (define-ldind x-endian) | 
|  | (begin | 
|  | (dlind "w" W x-endian SI) | 
|  | (dlind "h" H x-endian HI) | 
|  | (dlind "b" B x-endian QI) | 
|  | (dlind "dw" DW x-endian DI))) | 
|  |  | 
|  | (define-ldind le) | 
|  | (define-ldind be) | 
|  |  | 
|  | ;; Generic load and store instructions are provided for several word | 
|  | ;; sizes.  They come in several variants: | 
|  | ;; | 
|  | ;;  LDX{b,h,w,dw}le, STX{b,h,w,dw}le for the little-endian ISA | 
|  | ;; | 
|  | ;;  LDX{b,h,w,dw}be, STX{b,h,w,dw}be for the big-endian ISA | 
|  | ;; | 
|  | ;; Loads operate on [$SRC+-OFFSET] -> $DST | 
|  | ;; Stores operate on $SRC -> [$DST+-OFFSET] | 
|  |  | 
|  | (define-pmacro (dxli x-basename x-suffix x-size x-endian x-mode) | 
|  | (dni (.sym x-basename x-suffix x-endian) | 
|  | (.str x-basename x-suffix) | 
|  | (endian-isas x-endian) | 
|  | (.str x-basename x-suffix " $dst" x-endian ",[$src" x-endian "+$offset16]") | 
|  | (+ (f-imm32 0) offset16 (.sym src x-endian) (.sym dst x-endian) | 
|  | OP_CLASS_LDX (.sym OP_SIZE_ x-size) OP_MODE_MEM) | 
|  | (set x-mode | 
|  | (.sym dst x-endian) | 
|  | (mem x-mode (add DI (.sym src x-endian) offset16))) | 
|  | ())) | 
|  |  | 
|  | (define-pmacro (dxsi x-basename x-suffix x-size x-endian x-mode) | 
|  | (dni (.sym x-basename x-suffix x-endian) | 
|  | (.str x-basename x-suffix) | 
|  | (endian-isas x-endian) | 
|  | (.str x-basename x-suffix " [$dst" x-endian "+$offset16],$src" x-endian) | 
|  | (+ (f-imm32 0) offset16 (.sym src x-endian) (.sym dst x-endian) | 
|  | OP_CLASS_STX (.sym OP_SIZE_ x-size) OP_MODE_MEM) | 
|  | (set x-mode | 
|  | (mem x-mode (add DI (.sym dst x-endian) offset16)) | 
|  | (.sym src x-endian)) ;; XXX address is section-relative | 
|  | ())) | 
|  |  | 
|  | (define-pmacro (define-ldstx-insns x-endian) | 
|  | (begin | 
|  | (dxli "ldx" "w" W x-endian SI) | 
|  | (dxli "ldx" "h" H x-endian HI) | 
|  | (dxli "ldx" "b" B x-endian QI) | 
|  | (dxli "ldx" "dw" DW x-endian DI) | 
|  |  | 
|  | (dxsi "stx" "w" W x-endian SI) | 
|  | (dxsi "stx" "h" H x-endian HI) | 
|  | (dxsi "stx" "b" B x-endian QI) | 
|  | (dxsi "stx" "dw" DW x-endian DI))) | 
|  |  | 
|  | (define-ldstx-insns le) | 
|  | (define-ldstx-insns be) | 
|  |  | 
|  | ;; Generic store instructions of the form IMM32 -> [$DST+OFFSET] are | 
|  | ;; provided in several variants: | 
|  | ;; | 
|  | ;;  ST{b,h,w,dw}le for the little-endian ISA | 
|  | ;;  ST{b,h,w,dw}be for the big-endian ISA | 
|  |  | 
|  | (define-pmacro (dsti x-suffix x-size x-endian x-mode) | 
|  | (dni (.sym "st" x-suffix x-endian) | 
|  | (.str "st" x-suffix) | 
|  | (endian-isas x-endian) | 
|  | (.str "st" x-suffix " [$dst" x-endian "+$offset16],$imm32") | 
|  | (+ imm32 offset16 ((.sym f-src x-endian) 0) (.sym dst x-endian) | 
|  | OP_CLASS_ST (.sym OP_SIZE_ x-size) OP_MODE_MEM) | 
|  | (set x-mode | 
|  | (mem x-mode (add DI (.sym dst x-endian) offset16)) | 
|  | imm32) ;; XXX address is section-relative | 
|  | ())) | 
|  |  | 
|  | (define-pmacro (define-st-insns x-endian) | 
|  | (begin | 
|  | (dsti "b" B x-endian QI) | 
|  | (dsti "h" H x-endian HI) | 
|  | (dsti "w" W x-endian SI) | 
|  | (dsti "dw" DW x-endian DI))) | 
|  |  | 
|  | (define-st-insns le) | 
|  | (define-st-insns be) | 
|  |  | 
|  | ;;; Jump instructions | 
|  |  | 
|  | ;; Compare-and-jump instructions, on the other hand, make use of | 
|  | ;; registers.  Therefore, we need to define several variants in both | 
|  | ;; ISAs: | 
|  | ;; | 
|  | ;;   J{eq,gt,ge,lt,le,set,ne,sgt,sge,slt,sle}[32]{i,r}le for the | 
|  | ;;   little-endian ISA. | 
|  | ;;   J{eq,gt,ge,lt,le,set,ne.sgt,sge,slt,sle}[32]{i,r}be for the | 
|  | ;;   big-endian ISA. | 
|  |  | 
|  | (define-pmacro (define-cond-jump-insn x-cond x-suffix x-op-class x-op-code x-endian x-mode x-semop) | 
|  | (begin | 
|  | (dni (.sym j x-cond x-suffix i x-endian) | 
|  | (.str j x-cond x-suffix " i") | 
|  | (endian-isas x-endian) | 
|  | (.str "j" x-cond x-suffix " $dst" x-endian ",$imm32,$disp16") | 
|  | (+ imm32 disp16 ((.sym f-src x-endian) 0) (.sym dst x-endian) | 
|  | x-op-class OP_SRC_K (.sym OP_CODE_ x-op-code)) | 
|  | (if VOID (x-semop x-mode (.sym dst x-endian) imm32) | 
|  | (set DI | 
|  | (reg DI h-pc) (add DI (reg DI h-pc) | 
|  | (mul DI (add HI disp16 1) 8)))) | 
|  | ()) | 
|  | (dni (.sym j x-cond x-suffix r x-endian) | 
|  | (.str j x-cond x-suffix " r") | 
|  | (endian-isas x-endian) | 
|  | (.str "j" x-cond x-suffix " $dst" x-endian ",$src" x-endian ",$disp16") | 
|  | (+ (f-imm32 0) disp16 (.sym src x-endian) (.sym dst x-endian) | 
|  | x-op-class OP_SRC_X (.sym OP_CODE_ x-op-code)) | 
|  | (if VOID (x-semop x-mode (.sym dst x-endian) (.sym src x-endian)) | 
|  | (set DI | 
|  | (reg DI h-pc) (add DI (reg DI h-pc) | 
|  | (mul DI (add HI disp16 1) 8)))) | 
|  | ()))) | 
|  |  | 
|  | (define-pmacro (dcji x-cond x-op-code x-endian x-semop) | 
|  | (begin | 
|  | (define-cond-jump-insn x-cond "" OP_CLASS_JMP x-op-code x-endian DI x-semop) | 
|  | (define-cond-jump-insn x-cond "32" OP_CLASS_JMP32 x-op-code x-endian SI x-semop ))) | 
|  |  | 
|  | (define-pmacro (define-condjump-insns x-endian) | 
|  | (begin | 
|  | (dcji "eq" JEQ x-endian eq) | 
|  | (dcji "gt" JGT x-endian gtu) | 
|  | (dcji "ge" JGE x-endian geu) | 
|  | (dcji "lt" JLT x-endian ltu) | 
|  | (dcji "le" JLE x-endian leu) | 
|  | (dcji "set" JSET x-endian and) | 
|  | (dcji "ne" JNE x-endian ne) | 
|  | (dcji "sgt" JSGT x-endian gt) | 
|  | (dcji "sge" JSGE x-endian ge) | 
|  | (dcji "slt" JSLT x-endian lt) | 
|  | (dcji "sle" JSLE x-endian le))) | 
|  |  | 
|  | (define-condjump-insns le) | 
|  | (define-condjump-insns be) | 
|  |  | 
|  | ;; The `call' instruction doesn't make use of registers, but the | 
|  | ;; semantic routine should have access to the src register in order to | 
|  | ;; properly interpret the meaning of disp32.  Therefore we need one | 
|  | ;; version per ISA. | 
|  |  | 
|  | (define-pmacro (define-call-insn x-endian) | 
|  | (dni (.sym call x-endian) | 
|  | "call" | 
|  | (endian-isas x-endian) | 
|  | "call $disp32" | 
|  | (+ disp32 (f-offset16 0) (f-regs 0) | 
|  | OP_CLASS_JMP OP_SRC_K OP_CODE_CALL) | 
|  | (c-call VOID | 
|  | "bpfbf_call" disp32 (ifield (.sym f-src x-endian))) | 
|  | ())) | 
|  |  | 
|  | (define-call-insn le) | 
|  | (define-call-insn be) | 
|  |  | 
|  | (define-pmacro (define-callr-insn x-endian) | 
|  | (dni (.sym callr x-endian) | 
|  | "callr" | 
|  | ((ISA (.sym xbpf x-endian))) | 
|  | (.str "call $dst" x-endian) | 
|  | (+ (f-imm32 0) (f-offset16 0) ((.sym f-src x-endian) 0) (.sym dst x-endian) | 
|  | OP_CLASS_JMP OP_SRC_X OP_CODE_CALL) | 
|  | (c-call VOID | 
|  | "bpfbf_callr" (ifield (.sym f-dst x-endian))) | 
|  | ())) | 
|  |  | 
|  | (define-callr-insn le) | 
|  | (define-callr-insn be) | 
|  |  | 
|  | ;; The jump-always and `exit' instructions dont make use of either | 
|  | ;; source nor destination registers, so only one variant per | 
|  | ;; instruction is defined. | 
|  |  | 
|  | (dni ja "ja" (all-isas) "ja $disp16" | 
|  | (+ (f-imm32 0) disp16 (f-regs 0) | 
|  | OP_CLASS_JMP OP_SRC_K OP_CODE_JA) | 
|  | (set DI (reg DI h-pc) (add DI (reg DI h-pc) | 
|  | (mul DI (add HI disp16 1) 8))) | 
|  | ()) | 
|  |  | 
|  | (dni "exit" "exit" (all-isas) "exit" | 
|  | (+ (f-imm32 0) (f-offset16 0) (f-regs 0) | 
|  | OP_CLASS_JMP (f-op-src 0) OP_CODE_EXIT) | 
|  | (c-call VOID "bpfbf_exit") | 
|  | ()) | 
|  |  | 
|  | ;;; Atomic instructions | 
|  |  | 
|  | ;; The atomic exchange-and-add instructions come in two flavors: one | 
|  | ;; for swapping 64-bit quantities and another for 32-bit quantities. | 
|  |  | 
|  | (define-pmacro (sem-exchange-and-add x-endian x-mode) | 
|  | (sequence VOID ((x-mode tmp)) | 
|  | ;; XXX acquire lock in simulator...  as a hardware element? | 
|  | (set x-mode tmp (mem x-mode (add DI (.sym dst x-endian) offset16))) | 
|  | (set x-mode | 
|  | (mem x-mode (add DI (.sym dst x-endian) offset16)) | 
|  | (add x-mode tmp (.sym src x-endian))))) | 
|  |  | 
|  | (define-pmacro (define-atomic-insns x-endian) | 
|  | (begin | 
|  | (dni (.str "xadddw" x-endian) | 
|  | "xadddw" | 
|  | (endian-isas x-endian) | 
|  | (.str "xadddw [$dst" x-endian "+$offset16],$src" x-endian) | 
|  | (+ (f-imm32 0) (.sym src x-endian) (.sym dst x-endian) | 
|  | offset16 OP_MODE_XADD OP_SIZE_DW OP_CLASS_STX) | 
|  | (sem-exchange-and-add x-endian DI) | 
|  | ()) | 
|  | (dni (.str "xaddw" x-endian) | 
|  | "xaddw" | 
|  | (endian-isas x-endian) | 
|  | (.str "xaddw [$dst" x-endian "+$offset16],$src" x-endian) | 
|  | (+ (f-imm32 0) (.sym src x-endian) (.sym dst x-endian) | 
|  | offset16 OP_MODE_XADD OP_SIZE_W OP_CLASS_STX) | 
|  | (sem-exchange-and-add x-endian SI) | 
|  | ()))) | 
|  |  | 
|  | (define-atomic-insns le) | 
|  | (define-atomic-insns be) | 
|  |  | 
|  | ;;; Breakpoint instruction | 
|  |  | 
|  | ;; The brkpt instruction is used by the BPF simulator and it doesn't | 
|  | ;; really belong to the eBPF instruction set. | 
|  |  | 
|  | (dni "brkpt" "brkpt" (all-isas)  "brkpt" | 
|  | (+ (f-imm32 0) (f-offset16 0) (f-regs 0) | 
|  | OP_CLASS_ALU OP_SRC_X OP_CODE_NEG) | 
|  | (c-call VOID "bpfbf_breakpoint") | 
|  | ()) |