123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855 |
- ;; 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")
- ())
|