18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/* bpf_jit.S: Packet/header access helper functions
38c2ecf20Sopenharmony_ci * for PPC64 BPF compiler.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <asm/ppc_asm.h>
98c2ecf20Sopenharmony_ci#include <asm/asm-compat.h>
108c2ecf20Sopenharmony_ci#include "bpf_jit32.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/*
138c2ecf20Sopenharmony_ci * All of these routines are called directly from generated code,
148c2ecf20Sopenharmony_ci * whose register usage is:
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * r3		skb
178c2ecf20Sopenharmony_ci * r4,r5	A,X
188c2ecf20Sopenharmony_ci * r6		*** address parameter to helper ***
198c2ecf20Sopenharmony_ci * r7-r10	scratch
208c2ecf20Sopenharmony_ci * r14		skb->data
218c2ecf20Sopenharmony_ci * r15		skb headlen
228c2ecf20Sopenharmony_ci * r16-31	M[]
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * To consider: These helpers are so small it could be better to just
278c2ecf20Sopenharmony_ci * generate them inline.  Inline code can do the simple headlen check
288c2ecf20Sopenharmony_ci * then branch directly to slow_path_XXX if required.  (In fact, could
298c2ecf20Sopenharmony_ci * load a spare GPR with the address of slow_path_generic and pass size
308c2ecf20Sopenharmony_ci * as an argument, making the call site a mtlr, li and bllr.)
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci	.globl	sk_load_word
338c2ecf20Sopenharmony_cisk_load_word:
348c2ecf20Sopenharmony_ci	PPC_LCMPI	r_addr, 0
358c2ecf20Sopenharmony_ci	blt	bpf_slow_path_word_neg
368c2ecf20Sopenharmony_ci	.globl	sk_load_word_positive_offset
378c2ecf20Sopenharmony_cisk_load_word_positive_offset:
388c2ecf20Sopenharmony_ci	/* Are we accessing past headlen? */
398c2ecf20Sopenharmony_ci	subi	r_scratch1, r_HL, 4
408c2ecf20Sopenharmony_ci	PPC_LCMP	r_scratch1, r_addr
418c2ecf20Sopenharmony_ci	blt	bpf_slow_path_word
428c2ecf20Sopenharmony_ci	/* Nope, just hitting the header.  cr0 here is eq or gt! */
438c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN__
448c2ecf20Sopenharmony_ci	lwbrx	r_A, r_D, r_addr
458c2ecf20Sopenharmony_ci#else
468c2ecf20Sopenharmony_ci	lwzx	r_A, r_D, r_addr
478c2ecf20Sopenharmony_ci#endif
488c2ecf20Sopenharmony_ci	blr	/* Return success, cr0 != LT */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	.globl	sk_load_half
518c2ecf20Sopenharmony_cisk_load_half:
528c2ecf20Sopenharmony_ci	PPC_LCMPI	r_addr, 0
538c2ecf20Sopenharmony_ci	blt	bpf_slow_path_half_neg
548c2ecf20Sopenharmony_ci	.globl	sk_load_half_positive_offset
558c2ecf20Sopenharmony_cisk_load_half_positive_offset:
568c2ecf20Sopenharmony_ci	subi	r_scratch1, r_HL, 2
578c2ecf20Sopenharmony_ci	PPC_LCMP	r_scratch1, r_addr
588c2ecf20Sopenharmony_ci	blt	bpf_slow_path_half
598c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN__
608c2ecf20Sopenharmony_ci	lhbrx	r_A, r_D, r_addr
618c2ecf20Sopenharmony_ci#else
628c2ecf20Sopenharmony_ci	lhzx	r_A, r_D, r_addr
638c2ecf20Sopenharmony_ci#endif
648c2ecf20Sopenharmony_ci	blr
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	.globl	sk_load_byte
678c2ecf20Sopenharmony_cisk_load_byte:
688c2ecf20Sopenharmony_ci	PPC_LCMPI	r_addr, 0
698c2ecf20Sopenharmony_ci	blt	bpf_slow_path_byte_neg
708c2ecf20Sopenharmony_ci	.globl	sk_load_byte_positive_offset
718c2ecf20Sopenharmony_cisk_load_byte_positive_offset:
728c2ecf20Sopenharmony_ci	PPC_LCMP	r_HL, r_addr
738c2ecf20Sopenharmony_ci	ble	bpf_slow_path_byte
748c2ecf20Sopenharmony_ci	lbzx	r_A, r_D, r_addr
758c2ecf20Sopenharmony_ci	blr
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*
788c2ecf20Sopenharmony_ci * BPF_LDX | BPF_B | BPF_MSH: ldxb  4*([offset]&0xf)
798c2ecf20Sopenharmony_ci * r_addr is the offset value
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_ci	.globl sk_load_byte_msh
828c2ecf20Sopenharmony_cisk_load_byte_msh:
838c2ecf20Sopenharmony_ci	PPC_LCMPI	r_addr, 0
848c2ecf20Sopenharmony_ci	blt	bpf_slow_path_byte_msh_neg
858c2ecf20Sopenharmony_ci	.globl sk_load_byte_msh_positive_offset
868c2ecf20Sopenharmony_cisk_load_byte_msh_positive_offset:
878c2ecf20Sopenharmony_ci	PPC_LCMP	r_HL, r_addr
888c2ecf20Sopenharmony_ci	ble	bpf_slow_path_byte_msh
898c2ecf20Sopenharmony_ci	lbzx	r_X, r_D, r_addr
908c2ecf20Sopenharmony_ci	rlwinm	r_X, r_X, 2, 32-4-2, 31-2
918c2ecf20Sopenharmony_ci	blr
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* Call out to skb_copy_bits:
948c2ecf20Sopenharmony_ci * We'll need to back up our volatile regs first; we have
958c2ecf20Sopenharmony_ci * local variable space at r1+(BPF_PPC_STACK_BASIC).
968c2ecf20Sopenharmony_ci * Allocate a new stack frame here to remain ABI-compliant in
978c2ecf20Sopenharmony_ci * stashing LR.
988c2ecf20Sopenharmony_ci */
998c2ecf20Sopenharmony_ci#define bpf_slow_path_common(SIZE)				\
1008c2ecf20Sopenharmony_ci	mflr	r0;						\
1018c2ecf20Sopenharmony_ci	PPC_STL	r0, PPC_LR_STKOFF(r1);					\
1028c2ecf20Sopenharmony_ci	/* R3 goes in parameter space of caller's frame */	\
1038c2ecf20Sopenharmony_ci	PPC_STL	r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1);		\
1048c2ecf20Sopenharmony_ci	PPC_STL	r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1);		\
1058c2ecf20Sopenharmony_ci	PPC_STL	r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1);		\
1068c2ecf20Sopenharmony_ci	addi	r5, r1, BPF_PPC_STACK_BASIC+(2*REG_SZ);		\
1078c2ecf20Sopenharmony_ci	PPC_STLU	r1, -BPF_PPC_SLOWPATH_FRAME(r1);		\
1088c2ecf20Sopenharmony_ci	/* R3 = r_skb, as passed */				\
1098c2ecf20Sopenharmony_ci	mr	r4, r_addr;					\
1108c2ecf20Sopenharmony_ci	li	r6, SIZE;					\
1118c2ecf20Sopenharmony_ci	bl	skb_copy_bits;					\
1128c2ecf20Sopenharmony_ci	nop;							\
1138c2ecf20Sopenharmony_ci	/* R3 = 0 on success */					\
1148c2ecf20Sopenharmony_ci	addi	r1, r1, BPF_PPC_SLOWPATH_FRAME;			\
1158c2ecf20Sopenharmony_ci	PPC_LL	r0, PPC_LR_STKOFF(r1);					\
1168c2ecf20Sopenharmony_ci	PPC_LL	r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1);		\
1178c2ecf20Sopenharmony_ci	PPC_LL	r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1);		\
1188c2ecf20Sopenharmony_ci	mtlr	r0;						\
1198c2ecf20Sopenharmony_ci	PPC_LCMPI	r3, 0;						\
1208c2ecf20Sopenharmony_ci	blt	bpf_error;	/* cr0 = LT */			\
1218c2ecf20Sopenharmony_ci	PPC_LL	r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1);		\
1228c2ecf20Sopenharmony_ci	/* Great success! */
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cibpf_slow_path_word:
1258c2ecf20Sopenharmony_ci	bpf_slow_path_common(4)
1268c2ecf20Sopenharmony_ci	/* Data value is on stack, and cr0 != LT */
1278c2ecf20Sopenharmony_ci	lwz	r_A, BPF_PPC_STACK_BASIC+(2*REG_SZ)(r1)
1288c2ecf20Sopenharmony_ci	blr
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cibpf_slow_path_half:
1318c2ecf20Sopenharmony_ci	bpf_slow_path_common(2)
1328c2ecf20Sopenharmony_ci	lhz	r_A, BPF_PPC_STACK_BASIC+(2*8)(r1)
1338c2ecf20Sopenharmony_ci	blr
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cibpf_slow_path_byte:
1368c2ecf20Sopenharmony_ci	bpf_slow_path_common(1)
1378c2ecf20Sopenharmony_ci	lbz	r_A, BPF_PPC_STACK_BASIC+(2*8)(r1)
1388c2ecf20Sopenharmony_ci	blr
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cibpf_slow_path_byte_msh:
1418c2ecf20Sopenharmony_ci	bpf_slow_path_common(1)
1428c2ecf20Sopenharmony_ci	lbz	r_X, BPF_PPC_STACK_BASIC+(2*8)(r1)
1438c2ecf20Sopenharmony_ci	rlwinm	r_X, r_X, 2, 32-4-2, 31-2
1448c2ecf20Sopenharmony_ci	blr
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/* Call out to bpf_internal_load_pointer_neg_helper:
1478c2ecf20Sopenharmony_ci * We'll need to back up our volatile regs first; we have
1488c2ecf20Sopenharmony_ci * local variable space at r1+(BPF_PPC_STACK_BASIC).
1498c2ecf20Sopenharmony_ci * Allocate a new stack frame here to remain ABI-compliant in
1508c2ecf20Sopenharmony_ci * stashing LR.
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_ci#define sk_negative_common(SIZE)				\
1538c2ecf20Sopenharmony_ci	mflr	r0;						\
1548c2ecf20Sopenharmony_ci	PPC_STL	r0, PPC_LR_STKOFF(r1);					\
1558c2ecf20Sopenharmony_ci	/* R3 goes in parameter space of caller's frame */	\
1568c2ecf20Sopenharmony_ci	PPC_STL	r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1);		\
1578c2ecf20Sopenharmony_ci	PPC_STL	r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1);		\
1588c2ecf20Sopenharmony_ci	PPC_STL	r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1);		\
1598c2ecf20Sopenharmony_ci	PPC_STLU	r1, -BPF_PPC_SLOWPATH_FRAME(r1);		\
1608c2ecf20Sopenharmony_ci	/* R3 = r_skb, as passed */				\
1618c2ecf20Sopenharmony_ci	mr	r4, r_addr;					\
1628c2ecf20Sopenharmony_ci	li	r5, SIZE;					\
1638c2ecf20Sopenharmony_ci	bl	bpf_internal_load_pointer_neg_helper;		\
1648c2ecf20Sopenharmony_ci	nop;							\
1658c2ecf20Sopenharmony_ci	/* R3 != 0 on success */				\
1668c2ecf20Sopenharmony_ci	addi	r1, r1, BPF_PPC_SLOWPATH_FRAME;			\
1678c2ecf20Sopenharmony_ci	PPC_LL	r0, PPC_LR_STKOFF(r1);					\
1688c2ecf20Sopenharmony_ci	PPC_LL	r_A, (BPF_PPC_STACK_BASIC+(0*REG_SZ))(r1);		\
1698c2ecf20Sopenharmony_ci	PPC_LL	r_X, (BPF_PPC_STACK_BASIC+(1*REG_SZ))(r1);		\
1708c2ecf20Sopenharmony_ci	mtlr	r0;						\
1718c2ecf20Sopenharmony_ci	PPC_LCMPLI	r3, 0;						\
1728c2ecf20Sopenharmony_ci	beq	bpf_error_slow;	/* cr0 = EQ */			\
1738c2ecf20Sopenharmony_ci	mr	r_addr, r3;					\
1748c2ecf20Sopenharmony_ci	PPC_LL	r_skb, (BPF_PPC_STACKFRAME+BPF_PPC_STACK_R3_OFF)(r1);		\
1758c2ecf20Sopenharmony_ci	/* Great success! */
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cibpf_slow_path_word_neg:
1788c2ecf20Sopenharmony_ci	lis     r_scratch1,-32	/* SKF_LL_OFF */
1798c2ecf20Sopenharmony_ci	PPC_LCMP	r_addr, r_scratch1	/* addr < SKF_* */
1808c2ecf20Sopenharmony_ci	blt	bpf_error	/* cr0 = LT */
1818c2ecf20Sopenharmony_ci	.globl	sk_load_word_negative_offset
1828c2ecf20Sopenharmony_cisk_load_word_negative_offset:
1838c2ecf20Sopenharmony_ci	sk_negative_common(4)
1848c2ecf20Sopenharmony_ci	lwz	r_A, 0(r_addr)
1858c2ecf20Sopenharmony_ci	blr
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cibpf_slow_path_half_neg:
1888c2ecf20Sopenharmony_ci	lis     r_scratch1,-32	/* SKF_LL_OFF */
1898c2ecf20Sopenharmony_ci	PPC_LCMP	r_addr, r_scratch1	/* addr < SKF_* */
1908c2ecf20Sopenharmony_ci	blt	bpf_error	/* cr0 = LT */
1918c2ecf20Sopenharmony_ci	.globl	sk_load_half_negative_offset
1928c2ecf20Sopenharmony_cisk_load_half_negative_offset:
1938c2ecf20Sopenharmony_ci	sk_negative_common(2)
1948c2ecf20Sopenharmony_ci	lhz	r_A, 0(r_addr)
1958c2ecf20Sopenharmony_ci	blr
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cibpf_slow_path_byte_neg:
1988c2ecf20Sopenharmony_ci	lis     r_scratch1,-32	/* SKF_LL_OFF */
1998c2ecf20Sopenharmony_ci	PPC_LCMP	r_addr, r_scratch1	/* addr < SKF_* */
2008c2ecf20Sopenharmony_ci	blt	bpf_error	/* cr0 = LT */
2018c2ecf20Sopenharmony_ci	.globl	sk_load_byte_negative_offset
2028c2ecf20Sopenharmony_cisk_load_byte_negative_offset:
2038c2ecf20Sopenharmony_ci	sk_negative_common(1)
2048c2ecf20Sopenharmony_ci	lbz	r_A, 0(r_addr)
2058c2ecf20Sopenharmony_ci	blr
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cibpf_slow_path_byte_msh_neg:
2088c2ecf20Sopenharmony_ci	lis     r_scratch1,-32	/* SKF_LL_OFF */
2098c2ecf20Sopenharmony_ci	PPC_LCMP	r_addr, r_scratch1	/* addr < SKF_* */
2108c2ecf20Sopenharmony_ci	blt	bpf_error	/* cr0 = LT */
2118c2ecf20Sopenharmony_ci	.globl	sk_load_byte_msh_negative_offset
2128c2ecf20Sopenharmony_cisk_load_byte_msh_negative_offset:
2138c2ecf20Sopenharmony_ci	sk_negative_common(1)
2148c2ecf20Sopenharmony_ci	lbz	r_X, 0(r_addr)
2158c2ecf20Sopenharmony_ci	rlwinm	r_X, r_X, 2, 32-4-2, 31-2
2168c2ecf20Sopenharmony_ci	blr
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cibpf_error_slow:
2198c2ecf20Sopenharmony_ci	/* fabricate a cr0 = lt */
2208c2ecf20Sopenharmony_ci	li	r_scratch1, -1
2218c2ecf20Sopenharmony_ci	PPC_LCMPI	r_scratch1, 0
2228c2ecf20Sopenharmony_cibpf_error:
2238c2ecf20Sopenharmony_ci	/* Entered with cr0 = lt */
2248c2ecf20Sopenharmony_ci	li	r3, 0
2258c2ecf20Sopenharmony_ci	/* Generated code will 'blt epilogue', returning 0. */
2268c2ecf20Sopenharmony_ci	blr
227