diff options
Diffstat (limited to 'cpu/bpf.cpu')
-rw-r--r-- | cpu/bpf.cpu | 855 |
1 files changed, 0 insertions, 855 deletions
diff --git a/cpu/bpf.cpu b/cpu/bpf.cpu deleted file mode 100644 index 2ae74fc..0000000 --- a/cpu/bpf.cpu +++ /dev/null @@ -1,855 +0,0 @@ -;; 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) (.sym src x-endian) ((.sym f-dst x-endian) 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") - ()) |