// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package bpf

// A Register is a register of the BPF virtual machine.
type Register uint16

const (
	// RegA is the accumulator register. RegA is always the
	// destination register of ALU operations.
	RegA Register = iota
	// RegX is the indirection register, used by LoadIndirect
	// operations.
	RegX
)

// An ALUOp is an arithmetic or logic operation.
type ALUOp uint16

// ALU binary operation types.
const (
	ALUOpAdd ALUOp = iota << 4
	ALUOpSub
	ALUOpMul
	ALUOpDiv
	ALUOpOr
	ALUOpAnd
	ALUOpShiftLeft
	ALUOpShiftRight
	aluOpNeg // Not exported because it's the only unary ALU operation, and gets its own instruction type.
	ALUOpMod
	ALUOpXor
)

// A JumpTest is a comparison operator used in conditional jumps.
type JumpTest uint16

// Supported operators for conditional jumps.
const (
	// K == A
	JumpEqual JumpTest = iota
	// K != A
	JumpNotEqual
	// K > A
	JumpGreaterThan
	// K < A
	JumpLessThan
	// K >= A
	JumpGreaterOrEqual
	// K <= A
	JumpLessOrEqual
	// K & A != 0
	JumpBitsSet
	// K & A == 0
	JumpBitsNotSet
)

// An Extension is a function call provided by the kernel that
// performs advanced operations that are expensive or impossible
// within the BPF virtual machine.
//
// Extensions are only implemented by the Linux kernel.
//
// TODO: should we prune this list? Some of these extensions seem
// either broken or near-impossible to use correctly, whereas other
// (len, random, ifindex) are quite useful.
type Extension int

// Extension functions available in the Linux kernel.
const (
	// ExtLen returns the length of the packet.
	ExtLen Extension = 1
	// ExtProto returns the packet's L3 protocol type.
	ExtProto = 0
	// ExtType returns the packet's type (skb->pkt_type in the kernel)
	//
	// TODO: better documentation. How nice an API do we want to
	// provide for these esoteric extensions?
	ExtType = 4
	// ExtPayloadOffset returns the offset of the packet payload, or
	// the first protocol header that the kernel does not know how to
	// parse.
	ExtPayloadOffset = 52
	// ExtInterfaceIndex returns the index of the interface on which
	// the packet was received.
	ExtInterfaceIndex = 8
	// ExtNetlinkAttr returns the netlink attribute of type X at
	// offset A.
	ExtNetlinkAttr = 12
	// ExtNetlinkAttrNested returns the nested netlink attribute of
	// type X at offset A.
	ExtNetlinkAttrNested = 16
	// ExtMark returns the packet's mark value.
	ExtMark = 20
	// ExtQueue returns the packet's assigned hardware queue.
	ExtQueue = 24
	// ExtLinkLayerType returns the packet's hardware address type
	// (e.g. Ethernet, Infiniband).
	ExtLinkLayerType = 28
	// ExtRXHash returns the packets receive hash.
	//
	// TODO: figure out what this rxhash actually is.
	ExtRXHash = 32
	// ExtCPUID returns the ID of the CPU processing the current
	// packet.
	ExtCPUID = 36
	// ExtVLANTag returns the packet's VLAN tag.
	ExtVLANTag = 44
	// ExtVLANTagPresent returns non-zero if the packet has a VLAN
	// tag.
	//
	// TODO: I think this might be a lie: it reads bit 0x1000 of the
	// VLAN header, which changed meaning in recent revisions of the
	// spec - this extension may now return meaningless information.
	ExtVLANTagPresent = 48
	// ExtVLANProto returns 0x8100 if the frame has a VLAN header,
	// 0x88a8 if the frame has a "Q-in-Q" double VLAN header, or some
	// other value if no VLAN information is present.
	ExtVLANProto = 60
	// ExtRand returns a uniformly random uint32.
	ExtRand = 56
)

// The following gives names to various bit patterns used in opcode construction.

const (
	opMaskCls uint16 = 0x7
	// opClsLoad masks
	opMaskLoadDest  = 0x01
	opMaskLoadWidth = 0x18
	opMaskLoadMode  = 0xe0
	// opClsALU
	opMaskOperandSrc = 0x08
	opMaskOperator   = 0xf0
	// opClsJump
	opMaskJumpConst = 0x0f
	opMaskJumpCond  = 0xf0
)

const (
	// +---------------+-----------------+---+---+---+
	// | AddrMode (3b) | LoadWidth (2b)  | 0 | 0 | 0 |
	// +---------------+-----------------+---+---+---+
	opClsLoadA uint16 = iota
	// +---------------+-----------------+---+---+---+
	// | AddrMode (3b) | LoadWidth (2b)  | 0 | 0 | 1 |
	// +---------------+-----------------+---+---+---+
	opClsLoadX
	// +---+---+---+---+---+---+---+---+
	// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
	// +---+---+---+---+---+---+---+---+
	opClsStoreA
	// +---+---+---+---+---+---+---+---+
	// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
	// +---+---+---+---+---+---+---+---+
	opClsStoreX
	// +---------------+-----------------+---+---+---+
	// | Operator (4b) | OperandSrc (1b) | 1 | 0 | 0 |
	// +---------------+-----------------+---+---+---+
	opClsALU
	// +-----------------------------+---+---+---+---+
	// |      TestOperator (4b)      | 0 | 1 | 0 | 1 |
	// +-----------------------------+---+---+---+---+
	opClsJump
	// +---+-------------------------+---+---+---+---+
	// | 0 | 0 | 0 |   RetSrc (1b)   | 0 | 1 | 1 | 0 |
	// +---+-------------------------+---+---+---+---+
	opClsReturn
	// +---+-------------------------+---+---+---+---+
	// | 0 | 0 | 0 |  TXAorTAX (1b)  | 0 | 1 | 1 | 1 |
	// +---+-------------------------+---+---+---+---+
	opClsMisc
)

const (
	opAddrModeImmediate uint16 = iota << 5
	opAddrModeAbsolute
	opAddrModeIndirect
	opAddrModeScratch
	opAddrModePacketLen // actually an extension, not an addressing mode.
	opAddrModeMemShift
)

const (
	opLoadWidth4 uint16 = iota << 3
	opLoadWidth2
	opLoadWidth1
)

// Operator defined by ALUOp*

const (
	opALUSrcConstant uint16 = iota << 3
	opALUSrcX
)

const (
	opJumpAlways = iota << 4
	opJumpEqual
	opJumpGT
	opJumpGE
	opJumpSet
)

const (
	opRetSrcConstant uint16 = iota << 4
	opRetSrcA
)

const (
	opMiscTAX = 0x00
	opMiscTXA = 0x80
)
