mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	powerpc/ebpf/jit: Implement JIT compiler for extended BPF
PPC64 eBPF JIT compiler. Enable with: echo 1 > /proc/sys/net/core/bpf_jit_enable or echo 2 > /proc/sys/net/core/bpf_jit_enable ... to see the generated JIT code. This can further be processed with tools/net/bpf_jit_disasm. With CONFIG_TEST_BPF=m and 'modprobe test_bpf': test_bpf: Summary: 305 PASSED, 0 FAILED, [297/297 JIT'ed] ... on both ppc64 BE and LE. The details of the approach are documented through various comments in the code. Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
This commit is contained in:
		
							parent
							
								
									6ac0ba5a4f
								
							
						
					
					
						commit
						156d0e290e
					
				
					 8 changed files with 1315 additions and 3 deletions
				
			
		| 
						 | 
					@ -128,7 +128,8 @@ config PPC
 | 
				
			||||||
	select IRQ_FORCED_THREADING
 | 
						select IRQ_FORCED_THREADING
 | 
				
			||||||
	select HAVE_RCU_TABLE_FREE if SMP
 | 
						select HAVE_RCU_TABLE_FREE if SMP
 | 
				
			||||||
	select HAVE_SYSCALL_TRACEPOINTS
 | 
						select HAVE_SYSCALL_TRACEPOINTS
 | 
				
			||||||
	select HAVE_CBPF_JIT
 | 
						select HAVE_CBPF_JIT if !PPC64
 | 
				
			||||||
 | 
						select HAVE_EBPF_JIT if PPC64
 | 
				
			||||||
	select HAVE_ARCH_JUMP_LABEL
 | 
						select HAVE_ARCH_JUMP_LABEL
 | 
				
			||||||
	select ARCH_HAVE_NMI_SAFE_CMPXCHG
 | 
						select ARCH_HAVE_NMI_SAFE_CMPXCHG
 | 
				
			||||||
	select ARCH_HAS_GCOV_PROFILE_ALL
 | 
						select ARCH_HAS_GCOV_PROFILE_ALL
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,11 +36,13 @@
 | 
				
			||||||
#define PPC_MIN_STKFRM	112
 | 
					#define PPC_MIN_STKFRM	112
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __BIG_ENDIAN__
 | 
					#ifdef __BIG_ENDIAN__
 | 
				
			||||||
 | 
					#define LHZX_BE	stringify_in_c(lhzx)
 | 
				
			||||||
#define LWZX_BE	stringify_in_c(lwzx)
 | 
					#define LWZX_BE	stringify_in_c(lwzx)
 | 
				
			||||||
#define LDX_BE	stringify_in_c(ldx)
 | 
					#define LDX_BE	stringify_in_c(ldx)
 | 
				
			||||||
#define STWX_BE	stringify_in_c(stwx)
 | 
					#define STWX_BE	stringify_in_c(stwx)
 | 
				
			||||||
#define STDX_BE	stringify_in_c(stdx)
 | 
					#define STDX_BE	stringify_in_c(stdx)
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
 | 
					#define LHZX_BE	stringify_in_c(lhbrx)
 | 
				
			||||||
#define LWZX_BE	stringify_in_c(lwbrx)
 | 
					#define LWZX_BE	stringify_in_c(lwbrx)
 | 
				
			||||||
#define LDX_BE	stringify_in_c(ldbrx)
 | 
					#define LDX_BE	stringify_in_c(ldbrx)
 | 
				
			||||||
#define STWX_BE	stringify_in_c(stwbrx)
 | 
					#define STWX_BE	stringify_in_c(stwbrx)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -142,9 +142,11 @@
 | 
				
			||||||
#define PPC_INST_ISEL			0x7c00001e
 | 
					#define PPC_INST_ISEL			0x7c00001e
 | 
				
			||||||
#define PPC_INST_ISEL_MASK		0xfc00003e
 | 
					#define PPC_INST_ISEL_MASK		0xfc00003e
 | 
				
			||||||
#define PPC_INST_LDARX			0x7c0000a8
 | 
					#define PPC_INST_LDARX			0x7c0000a8
 | 
				
			||||||
 | 
					#define PPC_INST_STDCX			0x7c0001ad
 | 
				
			||||||
#define PPC_INST_LSWI			0x7c0004aa
 | 
					#define PPC_INST_LSWI			0x7c0004aa
 | 
				
			||||||
#define PPC_INST_LSWX			0x7c00042a
 | 
					#define PPC_INST_LSWX			0x7c00042a
 | 
				
			||||||
#define PPC_INST_LWARX			0x7c000028
 | 
					#define PPC_INST_LWARX			0x7c000028
 | 
				
			||||||
 | 
					#define PPC_INST_STWCX			0x7c00012d
 | 
				
			||||||
#define PPC_INST_LWSYNC			0x7c2004ac
 | 
					#define PPC_INST_LWSYNC			0x7c2004ac
 | 
				
			||||||
#define PPC_INST_SYNC			0x7c0004ac
 | 
					#define PPC_INST_SYNC			0x7c0004ac
 | 
				
			||||||
#define PPC_INST_SYNC_MASK		0xfc0007fe
 | 
					#define PPC_INST_SYNC_MASK		0xfc0007fe
 | 
				
			||||||
| 
						 | 
					@ -211,8 +213,11 @@
 | 
				
			||||||
#define PPC_INST_LBZ			0x88000000
 | 
					#define PPC_INST_LBZ			0x88000000
 | 
				
			||||||
#define PPC_INST_LD			0xe8000000
 | 
					#define PPC_INST_LD			0xe8000000
 | 
				
			||||||
#define PPC_INST_LHZ			0xa0000000
 | 
					#define PPC_INST_LHZ			0xa0000000
 | 
				
			||||||
#define PPC_INST_LHBRX			0x7c00062c
 | 
					 | 
				
			||||||
#define PPC_INST_LWZ			0x80000000
 | 
					#define PPC_INST_LWZ			0x80000000
 | 
				
			||||||
 | 
					#define PPC_INST_LHBRX			0x7c00062c
 | 
				
			||||||
 | 
					#define PPC_INST_LDBRX			0x7c000428
 | 
				
			||||||
 | 
					#define PPC_INST_STB			0x98000000
 | 
				
			||||||
 | 
					#define PPC_INST_STH			0xb0000000
 | 
				
			||||||
#define PPC_INST_STD			0xf8000000
 | 
					#define PPC_INST_STD			0xf8000000
 | 
				
			||||||
#define PPC_INST_STDU			0xf8000001
 | 
					#define PPC_INST_STDU			0xf8000001
 | 
				
			||||||
#define PPC_INST_STW			0x90000000
 | 
					#define PPC_INST_STW			0x90000000
 | 
				
			||||||
| 
						 | 
					@ -221,22 +226,34 @@
 | 
				
			||||||
#define PPC_INST_MTLR			0x7c0803a6
 | 
					#define PPC_INST_MTLR			0x7c0803a6
 | 
				
			||||||
#define PPC_INST_CMPWI			0x2c000000
 | 
					#define PPC_INST_CMPWI			0x2c000000
 | 
				
			||||||
#define PPC_INST_CMPDI			0x2c200000
 | 
					#define PPC_INST_CMPDI			0x2c200000
 | 
				
			||||||
 | 
					#define PPC_INST_CMPW			0x7c000000
 | 
				
			||||||
 | 
					#define PPC_INST_CMPD			0x7c200000
 | 
				
			||||||
#define PPC_INST_CMPLW			0x7c000040
 | 
					#define PPC_INST_CMPLW			0x7c000040
 | 
				
			||||||
 | 
					#define PPC_INST_CMPLD			0x7c200040
 | 
				
			||||||
#define PPC_INST_CMPLWI			0x28000000
 | 
					#define PPC_INST_CMPLWI			0x28000000
 | 
				
			||||||
 | 
					#define PPC_INST_CMPLDI			0x28200000
 | 
				
			||||||
#define PPC_INST_ADDI			0x38000000
 | 
					#define PPC_INST_ADDI			0x38000000
 | 
				
			||||||
#define PPC_INST_ADDIS			0x3c000000
 | 
					#define PPC_INST_ADDIS			0x3c000000
 | 
				
			||||||
#define PPC_INST_ADD			0x7c000214
 | 
					#define PPC_INST_ADD			0x7c000214
 | 
				
			||||||
#define PPC_INST_SUB			0x7c000050
 | 
					#define PPC_INST_SUB			0x7c000050
 | 
				
			||||||
#define PPC_INST_BLR			0x4e800020
 | 
					#define PPC_INST_BLR			0x4e800020
 | 
				
			||||||
#define PPC_INST_BLRL			0x4e800021
 | 
					#define PPC_INST_BLRL			0x4e800021
 | 
				
			||||||
 | 
					#define PPC_INST_MULLD			0x7c0001d2
 | 
				
			||||||
#define PPC_INST_MULLW			0x7c0001d6
 | 
					#define PPC_INST_MULLW			0x7c0001d6
 | 
				
			||||||
#define PPC_INST_MULHWU			0x7c000016
 | 
					#define PPC_INST_MULHWU			0x7c000016
 | 
				
			||||||
#define PPC_INST_MULLI			0x1c000000
 | 
					#define PPC_INST_MULLI			0x1c000000
 | 
				
			||||||
#define PPC_INST_DIVWU			0x7c000396
 | 
					#define PPC_INST_DIVWU			0x7c000396
 | 
				
			||||||
 | 
					#define PPC_INST_DIVD			0x7c0003d2
 | 
				
			||||||
#define PPC_INST_RLWINM			0x54000000
 | 
					#define PPC_INST_RLWINM			0x54000000
 | 
				
			||||||
 | 
					#define PPC_INST_RLWIMI			0x50000000
 | 
				
			||||||
 | 
					#define PPC_INST_RLDICL			0x78000000
 | 
				
			||||||
#define PPC_INST_RLDICR			0x78000004
 | 
					#define PPC_INST_RLDICR			0x78000004
 | 
				
			||||||
#define PPC_INST_SLW			0x7c000030
 | 
					#define PPC_INST_SLW			0x7c000030
 | 
				
			||||||
 | 
					#define PPC_INST_SLD			0x7c000036
 | 
				
			||||||
#define PPC_INST_SRW			0x7c000430
 | 
					#define PPC_INST_SRW			0x7c000430
 | 
				
			||||||
 | 
					#define PPC_INST_SRD			0x7c000436
 | 
				
			||||||
 | 
					#define PPC_INST_SRAD			0x7c000634
 | 
				
			||||||
 | 
					#define PPC_INST_SRADI			0x7c000674
 | 
				
			||||||
#define PPC_INST_AND			0x7c000038
 | 
					#define PPC_INST_AND			0x7c000038
 | 
				
			||||||
#define PPC_INST_ANDDOT			0x7c000039
 | 
					#define PPC_INST_ANDDOT			0x7c000039
 | 
				
			||||||
#define PPC_INST_OR			0x7c000378
 | 
					#define PPC_INST_OR			0x7c000378
 | 
				
			||||||
| 
						 | 
					@ -247,6 +264,7 @@
 | 
				
			||||||
#define PPC_INST_XORI			0x68000000
 | 
					#define PPC_INST_XORI			0x68000000
 | 
				
			||||||
#define PPC_INST_XORIS			0x6c000000
 | 
					#define PPC_INST_XORIS			0x6c000000
 | 
				
			||||||
#define PPC_INST_NEG			0x7c0000d0
 | 
					#define PPC_INST_NEG			0x7c0000d0
 | 
				
			||||||
 | 
					#define PPC_INST_EXTSW			0x7c0007b4
 | 
				
			||||||
#define PPC_INST_BRANCH			0x48000000
 | 
					#define PPC_INST_BRANCH			0x48000000
 | 
				
			||||||
#define PPC_INST_BRANCH_COND		0x40800000
 | 
					#define PPC_INST_BRANCH_COND		0x40800000
 | 
				
			||||||
#define PPC_INST_LBZCIX			0x7c0006aa
 | 
					#define PPC_INST_LBZCIX			0x7c0006aa
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,8 @@
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Arch-specific network modules
 | 
					# Arch-specific network modules
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					ifeq ($(CONFIG_PPC64),y)
 | 
				
			||||||
 | 
					obj-$(CONFIG_BPF_JIT) += bpf_jit_asm64.o bpf_jit_comp64.o
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
obj-$(CONFIG_BPF_JIT) += bpf_jit_asm.o bpf_jit_comp.o
 | 
					obj-$(CONFIG_BPF_JIT) += bpf_jit_asm.o bpf_jit_comp.o
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@
 | 
				
			||||||
 * bpf_jit.h: BPF JIT compiler for PPC
 | 
					 * bpf_jit.h: BPF JIT compiler for PPC
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation
 | 
					 * Copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation
 | 
				
			||||||
 | 
					 * 	     2016 Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
 * modify it under the terms of the GNU General Public License
 | 
					 * modify it under the terms of the GNU General Public License
 | 
				
			||||||
| 
						 | 
					@ -13,7 +14,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef __ASSEMBLY__
 | 
					#ifndef __ASSEMBLY__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_PPC64
 | 
					#include <asm/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef PPC64_ELF_ABI_v1
 | 
				
			||||||
#define FUNCTION_DESCR_SIZE	24
 | 
					#define FUNCTION_DESCR_SIZE	24
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#define FUNCTION_DESCR_SIZE	0
 | 
					#define FUNCTION_DESCR_SIZE	0
 | 
				
			||||||
| 
						 | 
					@ -52,6 +55,10 @@
 | 
				
			||||||
				     ___PPC_RA(base) | IMM_L(i))
 | 
									     ___PPC_RA(base) | IMM_L(i))
 | 
				
			||||||
#define PPC_STWU(r, base, i)	EMIT(PPC_INST_STWU | ___PPC_RS(r) |	      \
 | 
					#define PPC_STWU(r, base, i)	EMIT(PPC_INST_STWU | ___PPC_RS(r) |	      \
 | 
				
			||||||
				     ___PPC_RA(base) | IMM_L(i))
 | 
									     ___PPC_RA(base) | IMM_L(i))
 | 
				
			||||||
 | 
					#define PPC_STH(r, base, i)	EMIT(PPC_INST_STH | ___PPC_RS(r) |	      \
 | 
				
			||||||
 | 
									     ___PPC_RA(base) | IMM_L(i))
 | 
				
			||||||
 | 
					#define PPC_STB(r, base, i)	EMIT(PPC_INST_STB | ___PPC_RS(r) |	      \
 | 
				
			||||||
 | 
									     ___PPC_RA(base) | IMM_L(i))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PPC_LBZ(r, base, i)	EMIT(PPC_INST_LBZ | ___PPC_RT(r) |	      \
 | 
					#define PPC_LBZ(r, base, i)	EMIT(PPC_INST_LBZ | ___PPC_RT(r) |	      \
 | 
				
			||||||
				     ___PPC_RA(base) | IMM_L(i))
 | 
									     ___PPC_RA(base) | IMM_L(i))
 | 
				
			||||||
| 
						 | 
					@ -63,6 +70,19 @@
 | 
				
			||||||
				     ___PPC_RA(base) | IMM_L(i))
 | 
									     ___PPC_RA(base) | IMM_L(i))
 | 
				
			||||||
#define PPC_LHBRX(r, base, b)	EMIT(PPC_INST_LHBRX | ___PPC_RT(r) |	      \
 | 
					#define PPC_LHBRX(r, base, b)	EMIT(PPC_INST_LHBRX | ___PPC_RT(r) |	      \
 | 
				
			||||||
				     ___PPC_RA(base) | ___PPC_RB(b))
 | 
									     ___PPC_RA(base) | ___PPC_RB(b))
 | 
				
			||||||
 | 
					#define PPC_LDBRX(r, base, b)	EMIT(PPC_INST_LDBRX | ___PPC_RT(r) |	      \
 | 
				
			||||||
 | 
									     ___PPC_RA(base) | ___PPC_RB(b))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PPC_BPF_LDARX(t, a, b, eh) EMIT(PPC_INST_LDARX | ___PPC_RT(t) |	      \
 | 
				
			||||||
 | 
										___PPC_RA(a) | ___PPC_RB(b) |	      \
 | 
				
			||||||
 | 
										__PPC_EH(eh))
 | 
				
			||||||
 | 
					#define PPC_BPF_LWARX(t, a, b, eh) EMIT(PPC_INST_LWARX | ___PPC_RT(t) |	      \
 | 
				
			||||||
 | 
										___PPC_RA(a) | ___PPC_RB(b) |	      \
 | 
				
			||||||
 | 
										__PPC_EH(eh))
 | 
				
			||||||
 | 
					#define PPC_BPF_STWCX(s, a, b)	EMIT(PPC_INST_STWCX | ___PPC_RS(s) |	      \
 | 
				
			||||||
 | 
										___PPC_RA(a) | ___PPC_RB(b))
 | 
				
			||||||
 | 
					#define PPC_BPF_STDCX(s, a, b)	EMIT(PPC_INST_STDCX | ___PPC_RS(s) |	      \
 | 
				
			||||||
 | 
										___PPC_RA(a) | ___PPC_RB(b))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_PPC64
 | 
					#ifdef CONFIG_PPC64
 | 
				
			||||||
#define PPC_BPF_LL(r, base, i) do { PPC_LD(r, base, i); } while(0)
 | 
					#define PPC_BPF_LL(r, base, i) do { PPC_LD(r, base, i); } while(0)
 | 
				
			||||||
| 
						 | 
					@ -76,14 +96,23 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PPC_CMPWI(a, i)		EMIT(PPC_INST_CMPWI | ___PPC_RA(a) | IMM_L(i))
 | 
					#define PPC_CMPWI(a, i)		EMIT(PPC_INST_CMPWI | ___PPC_RA(a) | IMM_L(i))
 | 
				
			||||||
#define PPC_CMPDI(a, i)		EMIT(PPC_INST_CMPDI | ___PPC_RA(a) | IMM_L(i))
 | 
					#define PPC_CMPDI(a, i)		EMIT(PPC_INST_CMPDI | ___PPC_RA(a) | IMM_L(i))
 | 
				
			||||||
 | 
					#define PPC_CMPW(a, b)		EMIT(PPC_INST_CMPW | ___PPC_RA(a) |	      \
 | 
				
			||||||
 | 
										___PPC_RB(b))
 | 
				
			||||||
 | 
					#define PPC_CMPD(a, b)		EMIT(PPC_INST_CMPD | ___PPC_RA(a) |	      \
 | 
				
			||||||
 | 
										___PPC_RB(b))
 | 
				
			||||||
#define PPC_CMPLWI(a, i)	EMIT(PPC_INST_CMPLWI | ___PPC_RA(a) | IMM_L(i))
 | 
					#define PPC_CMPLWI(a, i)	EMIT(PPC_INST_CMPLWI | ___PPC_RA(a) | IMM_L(i))
 | 
				
			||||||
 | 
					#define PPC_CMPLDI(a, i)	EMIT(PPC_INST_CMPLDI | ___PPC_RA(a) | IMM_L(i))
 | 
				
			||||||
#define PPC_CMPLW(a, b)		EMIT(PPC_INST_CMPLW | ___PPC_RA(a) |	      \
 | 
					#define PPC_CMPLW(a, b)		EMIT(PPC_INST_CMPLW | ___PPC_RA(a) |	      \
 | 
				
			||||||
					___PPC_RB(b))
 | 
										___PPC_RB(b))
 | 
				
			||||||
 | 
					#define PPC_CMPLD(a, b)		EMIT(PPC_INST_CMPLD | ___PPC_RA(a) |	      \
 | 
				
			||||||
 | 
										___PPC_RB(b))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PPC_SUB(d, a, b)	EMIT(PPC_INST_SUB | ___PPC_RT(d) |	      \
 | 
					#define PPC_SUB(d, a, b)	EMIT(PPC_INST_SUB | ___PPC_RT(d) |	      \
 | 
				
			||||||
				     ___PPC_RB(a) | ___PPC_RA(b))
 | 
									     ___PPC_RB(a) | ___PPC_RA(b))
 | 
				
			||||||
#define PPC_ADD(d, a, b)	EMIT(PPC_INST_ADD | ___PPC_RT(d) |	      \
 | 
					#define PPC_ADD(d, a, b)	EMIT(PPC_INST_ADD | ___PPC_RT(d) |	      \
 | 
				
			||||||
				     ___PPC_RA(a) | ___PPC_RB(b))
 | 
									     ___PPC_RA(a) | ___PPC_RB(b))
 | 
				
			||||||
 | 
					#define PPC_MULD(d, a, b)	EMIT(PPC_INST_MULLD | ___PPC_RT(d) |	      \
 | 
				
			||||||
 | 
									     ___PPC_RA(a) | ___PPC_RB(b))
 | 
				
			||||||
#define PPC_MULW(d, a, b)	EMIT(PPC_INST_MULLW | ___PPC_RT(d) |	      \
 | 
					#define PPC_MULW(d, a, b)	EMIT(PPC_INST_MULLW | ___PPC_RT(d) |	      \
 | 
				
			||||||
				     ___PPC_RA(a) | ___PPC_RB(b))
 | 
									     ___PPC_RA(a) | ___PPC_RB(b))
 | 
				
			||||||
#define PPC_MULHWU(d, a, b)	EMIT(PPC_INST_MULHWU | ___PPC_RT(d) |	      \
 | 
					#define PPC_MULHWU(d, a, b)	EMIT(PPC_INST_MULHWU | ___PPC_RT(d) |	      \
 | 
				
			||||||
| 
						 | 
					@ -92,6 +121,8 @@
 | 
				
			||||||
				     ___PPC_RA(a) | IMM_L(i))
 | 
									     ___PPC_RA(a) | IMM_L(i))
 | 
				
			||||||
#define PPC_DIVWU(d, a, b)	EMIT(PPC_INST_DIVWU | ___PPC_RT(d) |	      \
 | 
					#define PPC_DIVWU(d, a, b)	EMIT(PPC_INST_DIVWU | ___PPC_RT(d) |	      \
 | 
				
			||||||
				     ___PPC_RA(a) | ___PPC_RB(b))
 | 
									     ___PPC_RA(a) | ___PPC_RB(b))
 | 
				
			||||||
 | 
					#define PPC_DIVD(d, a, b)	EMIT(PPC_INST_DIVD | ___PPC_RT(d) |	      \
 | 
				
			||||||
 | 
									     ___PPC_RA(a) | ___PPC_RB(b))
 | 
				
			||||||
#define PPC_AND(d, a, b)	EMIT(PPC_INST_AND | ___PPC_RA(d) |	      \
 | 
					#define PPC_AND(d, a, b)	EMIT(PPC_INST_AND | ___PPC_RA(d) |	      \
 | 
				
			||||||
				     ___PPC_RS(a) | ___PPC_RB(b))
 | 
									     ___PPC_RS(a) | ___PPC_RB(b))
 | 
				
			||||||
#define PPC_ANDI(d, a, i)	EMIT(PPC_INST_ANDI | ___PPC_RA(d) |	      \
 | 
					#define PPC_ANDI(d, a, i)	EMIT(PPC_INST_ANDI | ___PPC_RA(d) |	      \
 | 
				
			||||||
| 
						 | 
					@ -100,6 +131,7 @@
 | 
				
			||||||
				     ___PPC_RS(a) | ___PPC_RB(b))
 | 
									     ___PPC_RS(a) | ___PPC_RB(b))
 | 
				
			||||||
#define PPC_OR(d, a, b)		EMIT(PPC_INST_OR | ___PPC_RA(d) |	      \
 | 
					#define PPC_OR(d, a, b)		EMIT(PPC_INST_OR | ___PPC_RA(d) |	      \
 | 
				
			||||||
				     ___PPC_RS(a) | ___PPC_RB(b))
 | 
									     ___PPC_RS(a) | ___PPC_RB(b))
 | 
				
			||||||
 | 
					#define PPC_MR(d, a)		PPC_OR(d, a, a)
 | 
				
			||||||
#define PPC_ORI(d, a, i)	EMIT(PPC_INST_ORI | ___PPC_RA(d) |	      \
 | 
					#define PPC_ORI(d, a, i)	EMIT(PPC_INST_ORI | ___PPC_RA(d) |	      \
 | 
				
			||||||
				     ___PPC_RS(a) | IMM_L(i))
 | 
									     ___PPC_RS(a) | IMM_L(i))
 | 
				
			||||||
#define PPC_ORIS(d, a, i)	EMIT(PPC_INST_ORIS | ___PPC_RA(d) |	      \
 | 
					#define PPC_ORIS(d, a, i)	EMIT(PPC_INST_ORIS | ___PPC_RA(d) |	      \
 | 
				
			||||||
| 
						 | 
					@ -110,13 +142,30 @@
 | 
				
			||||||
				     ___PPC_RS(a) | IMM_L(i))
 | 
									     ___PPC_RS(a) | IMM_L(i))
 | 
				
			||||||
#define PPC_XORIS(d, a, i)	EMIT(PPC_INST_XORIS | ___PPC_RA(d) |	      \
 | 
					#define PPC_XORIS(d, a, i)	EMIT(PPC_INST_XORIS | ___PPC_RA(d) |	      \
 | 
				
			||||||
				     ___PPC_RS(a) | IMM_L(i))
 | 
									     ___PPC_RS(a) | IMM_L(i))
 | 
				
			||||||
 | 
					#define PPC_EXTSW(d, a)		EMIT(PPC_INST_EXTSW | ___PPC_RA(d) |	      \
 | 
				
			||||||
 | 
									     ___PPC_RS(a))
 | 
				
			||||||
#define PPC_SLW(d, a, s)	EMIT(PPC_INST_SLW | ___PPC_RA(d) |	      \
 | 
					#define PPC_SLW(d, a, s)	EMIT(PPC_INST_SLW | ___PPC_RA(d) |	      \
 | 
				
			||||||
				     ___PPC_RS(a) | ___PPC_RB(s))
 | 
									     ___PPC_RS(a) | ___PPC_RB(s))
 | 
				
			||||||
 | 
					#define PPC_SLD(d, a, s)	EMIT(PPC_INST_SLD | ___PPC_RA(d) |	      \
 | 
				
			||||||
 | 
									     ___PPC_RS(a) | ___PPC_RB(s))
 | 
				
			||||||
#define PPC_SRW(d, a, s)	EMIT(PPC_INST_SRW | ___PPC_RA(d) |	      \
 | 
					#define PPC_SRW(d, a, s)	EMIT(PPC_INST_SRW | ___PPC_RA(d) |	      \
 | 
				
			||||||
				     ___PPC_RS(a) | ___PPC_RB(s))
 | 
									     ___PPC_RS(a) | ___PPC_RB(s))
 | 
				
			||||||
 | 
					#define PPC_SRD(d, a, s)	EMIT(PPC_INST_SRD | ___PPC_RA(d) |	      \
 | 
				
			||||||
 | 
									     ___PPC_RS(a) | ___PPC_RB(s))
 | 
				
			||||||
 | 
					#define PPC_SRAD(d, a, s)	EMIT(PPC_INST_SRAD | ___PPC_RA(d) |	      \
 | 
				
			||||||
 | 
									     ___PPC_RS(a) | ___PPC_RB(s))
 | 
				
			||||||
 | 
					#define PPC_SRADI(d, a, i)	EMIT(PPC_INST_SRADI | ___PPC_RA(d) |	      \
 | 
				
			||||||
 | 
									     ___PPC_RS(a) | __PPC_SH(i) |             \
 | 
				
			||||||
 | 
									     (((i) & 0x20) >> 4))
 | 
				
			||||||
#define PPC_RLWINM(d, a, i, mb, me)	EMIT(PPC_INST_RLWINM | ___PPC_RA(d) | \
 | 
					#define PPC_RLWINM(d, a, i, mb, me)	EMIT(PPC_INST_RLWINM | ___PPC_RA(d) | \
 | 
				
			||||||
					___PPC_RS(a) | __PPC_SH(i) |	      \
 | 
										___PPC_RS(a) | __PPC_SH(i) |	      \
 | 
				
			||||||
					__PPC_MB(mb) | __PPC_ME(me))
 | 
										__PPC_MB(mb) | __PPC_ME(me))
 | 
				
			||||||
 | 
					#define PPC_RLWIMI(d, a, i, mb, me)	EMIT(PPC_INST_RLWIMI | ___PPC_RA(d) | \
 | 
				
			||||||
 | 
										___PPC_RS(a) | __PPC_SH(i) |	      \
 | 
				
			||||||
 | 
										__PPC_MB(mb) | __PPC_ME(me))
 | 
				
			||||||
 | 
					#define PPC_RLDICL(d, a, i, mb)		EMIT(PPC_INST_RLDICL | ___PPC_RA(d) | \
 | 
				
			||||||
 | 
										___PPC_RS(a) | __PPC_SH(i) |	      \
 | 
				
			||||||
 | 
										__PPC_MB64(mb) | (((i) & 0x20) >> 4))
 | 
				
			||||||
#define PPC_RLDICR(d, a, i, me)		EMIT(PPC_INST_RLDICR | ___PPC_RA(d) | \
 | 
					#define PPC_RLDICR(d, a, i, me)		EMIT(PPC_INST_RLDICR | ___PPC_RA(d) | \
 | 
				
			||||||
					___PPC_RS(a) | __PPC_SH(i) |	      \
 | 
										___PPC_RS(a) | __PPC_SH(i) |	      \
 | 
				
			||||||
					__PPC_ME64(me) | (((i) & 0x20) >> 4))
 | 
										__PPC_ME64(me) | (((i) & 0x20) >> 4))
 | 
				
			||||||
| 
						 | 
					@ -127,6 +176,8 @@
 | 
				
			||||||
#define PPC_SRWI(d, a, i)	PPC_RLWINM(d, a, 32-(i), i, 31)
 | 
					#define PPC_SRWI(d, a, i)	PPC_RLWINM(d, a, 32-(i), i, 31)
 | 
				
			||||||
/* sldi = rldicr Rx, Ry, n, 63-n */
 | 
					/* sldi = rldicr Rx, Ry, n, 63-n */
 | 
				
			||||||
#define PPC_SLDI(d, a, i)	PPC_RLDICR(d, a, i, 63-(i))
 | 
					#define PPC_SLDI(d, a, i)	PPC_RLDICR(d, a, i, 63-(i))
 | 
				
			||||||
 | 
					/* sldi = rldicl Rx, Ry, 64-n, n */
 | 
				
			||||||
 | 
					#define PPC_SRDI(d, a, i)	PPC_RLDICL(d, a, 64-(i), i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PPC_NEG(d, a)		EMIT(PPC_INST_NEG | ___PPC_RT(d) | ___PPC_RA(a))
 | 
					#define PPC_NEG(d, a)		EMIT(PPC_INST_NEG | ___PPC_RT(d) | ___PPC_RA(a))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										102
									
								
								arch/powerpc/net/bpf_jit64.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								arch/powerpc/net/bpf_jit64.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,102 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * bpf_jit64.h: BPF JIT compiler for PPC64
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright 2016 Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
 | 
				
			||||||
 | 
					 *		  IBM Corporation
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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; version 2
 | 
				
			||||||
 | 
					 * of the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifndef _BPF_JIT64_H
 | 
				
			||||||
 | 
					#define _BPF_JIT64_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "bpf_jit.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Stack layout:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *		[	prev sp		] <-------------
 | 
				
			||||||
 | 
					 *		[   nv gpr save area	] 8*8		|
 | 
				
			||||||
 | 
					 * fp (r31) -->	[   ebpf stack space	] 512		|
 | 
				
			||||||
 | 
					 *		[  local/tmp var space	] 16		|
 | 
				
			||||||
 | 
					 *		[     frame header	] 32/112	|
 | 
				
			||||||
 | 
					 * sp (r1) --->	[    stack pointer	] --------------
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* for bpf JIT code internal usage */
 | 
				
			||||||
 | 
					#define BPF_PPC_STACK_LOCALS	16
 | 
				
			||||||
 | 
					/* for gpr non volatile registers BPG_REG_6 to 10, plus skb cache registers */
 | 
				
			||||||
 | 
					#define BPF_PPC_STACK_SAVE	(8*8)
 | 
				
			||||||
 | 
					/* Ensure this is quadword aligned */
 | 
				
			||||||
 | 
					#define BPF_PPC_STACKFRAME	(STACK_FRAME_MIN_SIZE + BPF_PPC_STACK_LOCALS + \
 | 
				
			||||||
 | 
									 MAX_BPF_STACK + BPF_PPC_STACK_SAVE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __ASSEMBLY__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* BPF register usage */
 | 
				
			||||||
 | 
					#define SKB_HLEN_REG	(MAX_BPF_REG + 0)
 | 
				
			||||||
 | 
					#define SKB_DATA_REG	(MAX_BPF_REG + 1)
 | 
				
			||||||
 | 
					#define TMP_REG_1	(MAX_BPF_REG + 2)
 | 
				
			||||||
 | 
					#define TMP_REG_2	(MAX_BPF_REG + 3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* BPF to ppc register mappings */
 | 
				
			||||||
 | 
					static const int b2p[] = {
 | 
				
			||||||
 | 
						/* function return value */
 | 
				
			||||||
 | 
						[BPF_REG_0] = 8,
 | 
				
			||||||
 | 
						/* function arguments */
 | 
				
			||||||
 | 
						[BPF_REG_1] = 3,
 | 
				
			||||||
 | 
						[BPF_REG_2] = 4,
 | 
				
			||||||
 | 
						[BPF_REG_3] = 5,
 | 
				
			||||||
 | 
						[BPF_REG_4] = 6,
 | 
				
			||||||
 | 
						[BPF_REG_5] = 7,
 | 
				
			||||||
 | 
						/* non volatile registers */
 | 
				
			||||||
 | 
						[BPF_REG_6] = 27,
 | 
				
			||||||
 | 
						[BPF_REG_7] = 28,
 | 
				
			||||||
 | 
						[BPF_REG_8] = 29,
 | 
				
			||||||
 | 
						[BPF_REG_9] = 30,
 | 
				
			||||||
 | 
						/* frame pointer aka BPF_REG_10 */
 | 
				
			||||||
 | 
						[BPF_REG_FP] = 31,
 | 
				
			||||||
 | 
						/* eBPF jit internal registers */
 | 
				
			||||||
 | 
						[SKB_HLEN_REG] = 25,
 | 
				
			||||||
 | 
						[SKB_DATA_REG] = 26,
 | 
				
			||||||
 | 
						[TMP_REG_1] = 9,
 | 
				
			||||||
 | 
						[TMP_REG_2] = 10
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Assembly helpers */
 | 
				
			||||||
 | 
					#define DECLARE_LOAD_FUNC(func)	u64 func(u64 r3, u64 r4);			\
 | 
				
			||||||
 | 
									u64 func##_negative_offset(u64 r3, u64 r4);	\
 | 
				
			||||||
 | 
									u64 func##_positive_offset(u64 r3, u64 r4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DECLARE_LOAD_FUNC(sk_load_word);
 | 
				
			||||||
 | 
					DECLARE_LOAD_FUNC(sk_load_half);
 | 
				
			||||||
 | 
					DECLARE_LOAD_FUNC(sk_load_byte);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CHOOSE_LOAD_FUNC(imm, func)						\
 | 
				
			||||||
 | 
								(imm < 0 ?						\
 | 
				
			||||||
 | 
								(imm >= SKF_LL_OFF ? func##_negative_offset : func) :	\
 | 
				
			||||||
 | 
								func##_positive_offset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SEEN_FUNC	0x1000 /* might call external helpers */
 | 
				
			||||||
 | 
					#define SEEN_STACK	0x2000 /* uses BPF stack */
 | 
				
			||||||
 | 
					#define SEEN_SKB	0x4000 /* uses sk_buff */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct codegen_context {
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * This is used to track register usage as well
 | 
				
			||||||
 | 
						 * as calls to external helpers.
 | 
				
			||||||
 | 
						 * - register usage is tracked with corresponding
 | 
				
			||||||
 | 
						 *   bits (r3-r10 and r25-r31)
 | 
				
			||||||
 | 
						 * - rest of the bits can be used to track other
 | 
				
			||||||
 | 
						 *   things -- for now, we use bits 16 to 23
 | 
				
			||||||
 | 
						 *   encoded in SEEN_* macros above
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						unsigned int seen;
 | 
				
			||||||
 | 
						unsigned int idx;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* !__ASSEMBLY__ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										180
									
								
								arch/powerpc/net/bpf_jit_asm64.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								arch/powerpc/net/bpf_jit_asm64.S
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,180 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * bpf_jit_asm64.S: Packet/header access helper functions
 | 
				
			||||||
 | 
					 * for PPC64 BPF compiler.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright 2016, Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
 | 
				
			||||||
 | 
					 * 		   IBM Corporation
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Based on bpf_jit_asm.S by Matt Evans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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; version 2
 | 
				
			||||||
 | 
					 * of the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asm/ppc_asm.h>
 | 
				
			||||||
 | 
					#include <asm/ptrace.h>
 | 
				
			||||||
 | 
					#include "bpf_jit64.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * All of these routines are called directly from generated code,
 | 
				
			||||||
 | 
					 * with the below register usage:
 | 
				
			||||||
 | 
					 * r27		skb pointer (ctx)
 | 
				
			||||||
 | 
					 * r25		skb header length
 | 
				
			||||||
 | 
					 * r26		skb->data pointer
 | 
				
			||||||
 | 
					 * r4		offset
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Result is passed back in:
 | 
				
			||||||
 | 
					 * r8		data read in host endian format (accumulator)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * r9 is used as a temporary register
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define r_skb	r27
 | 
				
			||||||
 | 
					#define r_hlen	r25
 | 
				
			||||||
 | 
					#define r_data	r26
 | 
				
			||||||
 | 
					#define r_off	r4
 | 
				
			||||||
 | 
					#define r_val	r8
 | 
				
			||||||
 | 
					#define r_tmp	r9
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_GLOBAL_TOC(sk_load_word)
 | 
				
			||||||
 | 
						cmpdi	r_off, 0
 | 
				
			||||||
 | 
						blt	bpf_slow_path_word_neg
 | 
				
			||||||
 | 
						b	sk_load_word_positive_offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_GLOBAL_TOC(sk_load_word_positive_offset)
 | 
				
			||||||
 | 
						/* Are we accessing past headlen? */
 | 
				
			||||||
 | 
						subi	r_tmp, r_hlen, 4
 | 
				
			||||||
 | 
						cmpd	r_tmp, r_off
 | 
				
			||||||
 | 
						blt	bpf_slow_path_word
 | 
				
			||||||
 | 
						/* Nope, just hitting the header.  cr0 here is eq or gt! */
 | 
				
			||||||
 | 
						LWZX_BE	r_val, r_data, r_off
 | 
				
			||||||
 | 
						blr	/* Return success, cr0 != LT */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_GLOBAL_TOC(sk_load_half)
 | 
				
			||||||
 | 
						cmpdi	r_off, 0
 | 
				
			||||||
 | 
						blt	bpf_slow_path_half_neg
 | 
				
			||||||
 | 
						b	sk_load_half_positive_offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_GLOBAL_TOC(sk_load_half_positive_offset)
 | 
				
			||||||
 | 
						subi	r_tmp, r_hlen, 2
 | 
				
			||||||
 | 
						cmpd	r_tmp, r_off
 | 
				
			||||||
 | 
						blt	bpf_slow_path_half
 | 
				
			||||||
 | 
						LHZX_BE	r_val, r_data, r_off
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_GLOBAL_TOC(sk_load_byte)
 | 
				
			||||||
 | 
						cmpdi	r_off, 0
 | 
				
			||||||
 | 
						blt	bpf_slow_path_byte_neg
 | 
				
			||||||
 | 
						b	sk_load_byte_positive_offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_GLOBAL_TOC(sk_load_byte_positive_offset)
 | 
				
			||||||
 | 
						cmpd	r_hlen, r_off
 | 
				
			||||||
 | 
						ble	bpf_slow_path_byte
 | 
				
			||||||
 | 
						lbzx	r_val, r_data, r_off
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Call out to skb_copy_bits:
 | 
				
			||||||
 | 
					 * Allocate a new stack frame here to remain ABI-compliant in
 | 
				
			||||||
 | 
					 * stashing LR.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define bpf_slow_path_common(SIZE)					\
 | 
				
			||||||
 | 
						mflr	r0;							\
 | 
				
			||||||
 | 
						std	r0, PPC_LR_STKOFF(r1);					\
 | 
				
			||||||
 | 
						stdu	r1, -(STACK_FRAME_MIN_SIZE + BPF_PPC_STACK_LOCALS)(r1);	\
 | 
				
			||||||
 | 
						mr	r3, r_skb;						\
 | 
				
			||||||
 | 
						/* r4 = r_off as passed */					\
 | 
				
			||||||
 | 
						addi	r5, r1, STACK_FRAME_MIN_SIZE;				\
 | 
				
			||||||
 | 
						li	r6, SIZE;						\
 | 
				
			||||||
 | 
						bl	skb_copy_bits;						\
 | 
				
			||||||
 | 
						nop;								\
 | 
				
			||||||
 | 
						/* save r5 */							\
 | 
				
			||||||
 | 
						addi	r5, r1, STACK_FRAME_MIN_SIZE;				\
 | 
				
			||||||
 | 
						/* r3 = 0 on success */						\
 | 
				
			||||||
 | 
						addi	r1, r1, STACK_FRAME_MIN_SIZE + BPF_PPC_STACK_LOCALS;	\
 | 
				
			||||||
 | 
						ld	r0, PPC_LR_STKOFF(r1);					\
 | 
				
			||||||
 | 
						mtlr	r0;							\
 | 
				
			||||||
 | 
						cmpdi	r3, 0;							\
 | 
				
			||||||
 | 
						blt	bpf_error;	/* cr0 = LT */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bpf_slow_path_word:
 | 
				
			||||||
 | 
						bpf_slow_path_common(4)
 | 
				
			||||||
 | 
						/* Data value is on stack, and cr0 != LT */
 | 
				
			||||||
 | 
						LWZX_BE	r_val, 0, r5
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bpf_slow_path_half:
 | 
				
			||||||
 | 
						bpf_slow_path_common(2)
 | 
				
			||||||
 | 
						LHZX_BE	r_val, 0, r5
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bpf_slow_path_byte:
 | 
				
			||||||
 | 
						bpf_slow_path_common(1)
 | 
				
			||||||
 | 
						lbzx	r_val, 0, r5
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Call out to bpf_internal_load_pointer_neg_helper
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define sk_negative_common(SIZE)				\
 | 
				
			||||||
 | 
						mflr	r0;						\
 | 
				
			||||||
 | 
						std	r0, PPC_LR_STKOFF(r1);				\
 | 
				
			||||||
 | 
						stdu	r1, -STACK_FRAME_MIN_SIZE(r1);			\
 | 
				
			||||||
 | 
						mr	r3, r_skb;					\
 | 
				
			||||||
 | 
						/* r4 = r_off, as passed */				\
 | 
				
			||||||
 | 
						li	r5, SIZE;					\
 | 
				
			||||||
 | 
						bl	bpf_internal_load_pointer_neg_helper;		\
 | 
				
			||||||
 | 
						nop;							\
 | 
				
			||||||
 | 
						addi	r1, r1, STACK_FRAME_MIN_SIZE;			\
 | 
				
			||||||
 | 
						ld	r0, PPC_LR_STKOFF(r1);				\
 | 
				
			||||||
 | 
						mtlr	r0;						\
 | 
				
			||||||
 | 
						/* R3 != 0 on success */				\
 | 
				
			||||||
 | 
						cmpldi	r3, 0;						\
 | 
				
			||||||
 | 
						beq	bpf_error_slow;	/* cr0 = EQ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bpf_slow_path_word_neg:
 | 
				
			||||||
 | 
						lis     r_tmp, -32	/* SKF_LL_OFF */
 | 
				
			||||||
 | 
						cmpd	r_off, r_tmp	/* addr < SKF_* */
 | 
				
			||||||
 | 
						blt	bpf_error	/* cr0 = LT */
 | 
				
			||||||
 | 
						b	sk_load_word_negative_offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_GLOBAL_TOC(sk_load_word_negative_offset)
 | 
				
			||||||
 | 
						sk_negative_common(4)
 | 
				
			||||||
 | 
						LWZX_BE	r_val, 0, r3
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bpf_slow_path_half_neg:
 | 
				
			||||||
 | 
						lis     r_tmp, -32	/* SKF_LL_OFF */
 | 
				
			||||||
 | 
						cmpd	r_off, r_tmp	/* addr < SKF_* */
 | 
				
			||||||
 | 
						blt	bpf_error	/* cr0 = LT */
 | 
				
			||||||
 | 
						b	sk_load_half_negative_offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_GLOBAL_TOC(sk_load_half_negative_offset)
 | 
				
			||||||
 | 
						sk_negative_common(2)
 | 
				
			||||||
 | 
						LHZX_BE	r_val, 0, r3
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bpf_slow_path_byte_neg:
 | 
				
			||||||
 | 
						lis     r_tmp, -32	/* SKF_LL_OFF */
 | 
				
			||||||
 | 
						cmpd	r_off, r_tmp	/* addr < SKF_* */
 | 
				
			||||||
 | 
						blt	bpf_error	/* cr0 = LT */
 | 
				
			||||||
 | 
						b	sk_load_byte_negative_offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_GLOBAL_TOC(sk_load_byte_negative_offset)
 | 
				
			||||||
 | 
						sk_negative_common(1)
 | 
				
			||||||
 | 
						lbzx	r_val, 0, r3
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bpf_error_slow:
 | 
				
			||||||
 | 
						/* fabricate a cr0 = lt */
 | 
				
			||||||
 | 
						li	r_tmp, -1
 | 
				
			||||||
 | 
						cmpdi	r_tmp, 0
 | 
				
			||||||
 | 
					bpf_error:
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Entered with cr0 = lt
 | 
				
			||||||
 | 
						 * Generated code will 'blt epilogue', returning 0.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						li	r_val, 0
 | 
				
			||||||
 | 
						blr
 | 
				
			||||||
							
								
								
									
										954
									
								
								arch/powerpc/net/bpf_jit_comp64.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										954
									
								
								arch/powerpc/net/bpf_jit_comp64.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,954 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * bpf_jit_comp64.c: eBPF JIT compiler
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright 2016 Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
 | 
				
			||||||
 | 
					 *		  IBM Corporation
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Based on the powerpc classic BPF JIT compiler by Matt Evans
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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; version 2
 | 
				
			||||||
 | 
					 * of the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <linux/moduleloader.h>
 | 
				
			||||||
 | 
					#include <asm/cacheflush.h>
 | 
				
			||||||
 | 
					#include <linux/netdevice.h>
 | 
				
			||||||
 | 
					#include <linux/filter.h>
 | 
				
			||||||
 | 
					#include <linux/if_vlan.h>
 | 
				
			||||||
 | 
					#include <asm/kprobes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "bpf_jit64.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int bpf_jit_enable __read_mostly;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bpf_jit_fill_ill_insns(void *area, unsigned int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int *p = area;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Fill whole space with trap instructions */
 | 
				
			||||||
 | 
						while (p < (int *)((char *)area + size))
 | 
				
			||||||
 | 
							*p++ = BREAKPOINT_INSTRUCTION;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void bpf_flush_icache(void *start, void *end)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						smp_wmb();
 | 
				
			||||||
 | 
						flush_icache_range((unsigned long)start, (unsigned long)end);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool bpf_is_seen_register(struct codegen_context *ctx, int i)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (ctx->seen & (1 << (31 - b2p[i])));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void bpf_set_seen_register(struct codegen_context *ctx, int i)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ctx->seen |= (1 << (31 - b2p[i]));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool bpf_has_stack_frame(struct codegen_context *ctx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * We only need a stack frame if:
 | 
				
			||||||
 | 
						 * - we call other functions (kernel helpers), or
 | 
				
			||||||
 | 
						 * - the bpf program uses its stack area
 | 
				
			||||||
 | 
						 * The latter condition is deduced from the usage of BPF_REG_FP
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						return ctx->seen & SEEN_FUNC || bpf_is_seen_register(ctx, BPF_REG_FP);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bpf_jit_emit_skb_loads(u32 *image, struct codegen_context *ctx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Load skb->len and skb->data_len
 | 
				
			||||||
 | 
						 * r3 points to skb
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						PPC_LWZ(b2p[SKB_HLEN_REG], 3, offsetof(struct sk_buff, len));
 | 
				
			||||||
 | 
						PPC_LWZ(b2p[TMP_REG_1], 3, offsetof(struct sk_buff, data_len));
 | 
				
			||||||
 | 
						/* header_len = len - data_len */
 | 
				
			||||||
 | 
						PPC_SUB(b2p[SKB_HLEN_REG], b2p[SKB_HLEN_REG], b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* skb->data pointer */
 | 
				
			||||||
 | 
						PPC_BPF_LL(b2p[SKB_DATA_REG], 3, offsetof(struct sk_buff, data));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bpf_jit_emit_func_call(u32 *image, struct codegen_context *ctx, u64 func)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#ifdef PPC64_ELF_ABI_v1
 | 
				
			||||||
 | 
						/* func points to the function descriptor */
 | 
				
			||||||
 | 
						PPC_LI64(b2p[TMP_REG_2], func);
 | 
				
			||||||
 | 
						/* Load actual entry point from function descriptor */
 | 
				
			||||||
 | 
						PPC_BPF_LL(b2p[TMP_REG_1], b2p[TMP_REG_2], 0);
 | 
				
			||||||
 | 
						/* ... and move it to LR */
 | 
				
			||||||
 | 
						PPC_MTLR(b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Load TOC from function descriptor at offset 8.
 | 
				
			||||||
 | 
						 * We can clobber r2 since we get called through a
 | 
				
			||||||
 | 
						 * function pointer (so caller will save/restore r2)
 | 
				
			||||||
 | 
						 * and since we don't use a TOC ourself.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						PPC_BPF_LL(2, b2p[TMP_REG_2], 8);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						/* We can clobber r12 */
 | 
				
			||||||
 | 
						PPC_FUNC_ADDR(12, func);
 | 
				
			||||||
 | 
						PPC_MTLR(12);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						PPC_BLRL();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						bool new_stack_frame = bpf_has_stack_frame(ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (new_stack_frame) {
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * We need a stack frame, but we don't necessarily need to
 | 
				
			||||||
 | 
							 * save/restore LR unless we call other functions
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (ctx->seen & SEEN_FUNC) {
 | 
				
			||||||
 | 
								EMIT(PPC_INST_MFLR | __PPC_RT(R0));
 | 
				
			||||||
 | 
								PPC_BPF_STL(0, 1, PPC_LR_STKOFF);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							PPC_BPF_STLU(1, 1, -BPF_PPC_STACKFRAME);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Back up non-volatile regs -- BPF registers 6-10
 | 
				
			||||||
 | 
						 * If we haven't created our own stack frame, we save these
 | 
				
			||||||
 | 
						 * in the protected zone below the previous stack frame
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						for (i = BPF_REG_6; i <= BPF_REG_10; i++)
 | 
				
			||||||
 | 
							if (bpf_is_seen_register(ctx, i))
 | 
				
			||||||
 | 
								PPC_BPF_STL(b2p[i], 1,
 | 
				
			||||||
 | 
									(new_stack_frame ? BPF_PPC_STACKFRAME : 0) -
 | 
				
			||||||
 | 
										(8 * (32 - b2p[i])));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Save additional non-volatile regs if we cache skb
 | 
				
			||||||
 | 
						 * Also, setup skb data
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (ctx->seen & SEEN_SKB) {
 | 
				
			||||||
 | 
							PPC_BPF_STL(b2p[SKB_HLEN_REG], 1,
 | 
				
			||||||
 | 
								BPF_PPC_STACKFRAME - (8 * (32 - b2p[SKB_HLEN_REG])));
 | 
				
			||||||
 | 
							PPC_BPF_STL(b2p[SKB_DATA_REG], 1,
 | 
				
			||||||
 | 
								BPF_PPC_STACKFRAME - (8 * (32 - b2p[SKB_DATA_REG])));
 | 
				
			||||||
 | 
							bpf_jit_emit_skb_loads(image, ctx);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Setup frame pointer to point to the bpf stack area */
 | 
				
			||||||
 | 
						if (bpf_is_seen_register(ctx, BPF_REG_FP))
 | 
				
			||||||
 | 
							PPC_ADDI(b2p[BPF_REG_FP], 1,
 | 
				
			||||||
 | 
									BPF_PPC_STACKFRAME - BPF_PPC_STACK_SAVE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						bool new_stack_frame = bpf_has_stack_frame(ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Move result to r3 */
 | 
				
			||||||
 | 
						PPC_MR(3, b2p[BPF_REG_0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Restore NVRs */
 | 
				
			||||||
 | 
						for (i = BPF_REG_6; i <= BPF_REG_10; i++)
 | 
				
			||||||
 | 
							if (bpf_is_seen_register(ctx, i))
 | 
				
			||||||
 | 
								PPC_BPF_LL(b2p[i], 1,
 | 
				
			||||||
 | 
									(new_stack_frame ? BPF_PPC_STACKFRAME : 0) -
 | 
				
			||||||
 | 
										(8 * (32 - b2p[i])));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Restore non-volatile registers used for skb cache */
 | 
				
			||||||
 | 
						if (ctx->seen & SEEN_SKB) {
 | 
				
			||||||
 | 
							PPC_BPF_LL(b2p[SKB_HLEN_REG], 1,
 | 
				
			||||||
 | 
								BPF_PPC_STACKFRAME - (8 * (32 - b2p[SKB_HLEN_REG])));
 | 
				
			||||||
 | 
							PPC_BPF_LL(b2p[SKB_DATA_REG], 1,
 | 
				
			||||||
 | 
								BPF_PPC_STACKFRAME - (8 * (32 - b2p[SKB_DATA_REG])));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Tear down our stack frame */
 | 
				
			||||||
 | 
						if (new_stack_frame) {
 | 
				
			||||||
 | 
							PPC_ADDI(1, 1, BPF_PPC_STACKFRAME);
 | 
				
			||||||
 | 
							if (ctx->seen & SEEN_FUNC) {
 | 
				
			||||||
 | 
								PPC_BPF_LL(0, 1, PPC_LR_STKOFF);
 | 
				
			||||||
 | 
								PPC_MTLR(0);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PPC_BLR();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Assemble the body code between the prologue & epilogue */
 | 
				
			||||||
 | 
					static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
 | 
				
			||||||
 | 
								      struct codegen_context *ctx,
 | 
				
			||||||
 | 
								      u32 *addrs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct bpf_insn *insn = fp->insnsi;
 | 
				
			||||||
 | 
						int flen = fp->len;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Start of epilogue code - will only be valid 2nd pass onwards */
 | 
				
			||||||
 | 
						u32 exit_addr = addrs[flen];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < flen; i++) {
 | 
				
			||||||
 | 
							u32 code = insn[i].code;
 | 
				
			||||||
 | 
							u32 dst_reg = b2p[insn[i].dst_reg];
 | 
				
			||||||
 | 
							u32 src_reg = b2p[insn[i].src_reg];
 | 
				
			||||||
 | 
							s16 off = insn[i].off;
 | 
				
			||||||
 | 
							s32 imm = insn[i].imm;
 | 
				
			||||||
 | 
							u64 imm64;
 | 
				
			||||||
 | 
							u8 *func;
 | 
				
			||||||
 | 
							u32 true_cond;
 | 
				
			||||||
 | 
							int stack_local_off;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * addrs[] maps a BPF bytecode address into a real offset from
 | 
				
			||||||
 | 
							 * the start of the body code.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							addrs[i] = ctx->idx * 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * As an optimization, we note down which non-volatile registers
 | 
				
			||||||
 | 
							 * are used so that we can only save/restore those in our
 | 
				
			||||||
 | 
							 * prologue and epilogue. We do this here regardless of whether
 | 
				
			||||||
 | 
							 * the actual BPF instruction uses src/dst registers or not
 | 
				
			||||||
 | 
							 * (for instance, BPF_CALL does not use them). The expectation
 | 
				
			||||||
 | 
							 * is that those instructions will have src_reg/dst_reg set to
 | 
				
			||||||
 | 
							 * 0. Even otherwise, we just lose some prologue/epilogue
 | 
				
			||||||
 | 
							 * optimization but everything else should work without
 | 
				
			||||||
 | 
							 * any issues.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (dst_reg >= 24 && dst_reg <= 31)
 | 
				
			||||||
 | 
								bpf_set_seen_register(ctx, insn[i].dst_reg);
 | 
				
			||||||
 | 
							if (src_reg >= 24 && src_reg <= 31)
 | 
				
			||||||
 | 
								bpf_set_seen_register(ctx, insn[i].src_reg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (code) {
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Arithmetic operations: ADD/SUB/MUL/DIV/MOD/NEG
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_ADD | BPF_X: /* (u32) dst += (u32) src */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_ADD | BPF_X: /* dst += src */
 | 
				
			||||||
 | 
								PPC_ADD(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_SUB | BPF_X: /* (u32) dst -= (u32) src */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_SUB | BPF_X: /* dst -= src */
 | 
				
			||||||
 | 
								PPC_SUB(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_ADD | BPF_K: /* (u32) dst += (u32) imm */
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_SUB | BPF_K: /* (u32) dst -= (u32) imm */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_ADD | BPF_K: /* dst += imm */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_SUB | BPF_K: /* dst -= imm */
 | 
				
			||||||
 | 
								if (BPF_OP(code) == BPF_SUB)
 | 
				
			||||||
 | 
									imm = -imm;
 | 
				
			||||||
 | 
								if (imm) {
 | 
				
			||||||
 | 
									if (imm >= -32768 && imm < 32768)
 | 
				
			||||||
 | 
										PPC_ADDI(dst_reg, dst_reg, IMM_L(imm));
 | 
				
			||||||
 | 
									else {
 | 
				
			||||||
 | 
										PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
										PPC_ADD(dst_reg, dst_reg, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_MUL | BPF_X: /* (u32) dst *= (u32) src */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_MUL | BPF_X: /* dst *= src */
 | 
				
			||||||
 | 
								if (BPF_CLASS(code) == BPF_ALU)
 | 
				
			||||||
 | 
									PPC_MULW(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									PPC_MULD(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_MUL | BPF_K: /* (u32) dst *= (u32) imm */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_MUL | BPF_K: /* dst *= imm */
 | 
				
			||||||
 | 
								if (imm >= -32768 && imm < 32768)
 | 
				
			||||||
 | 
									PPC_MULI(dst_reg, dst_reg, IMM_L(imm));
 | 
				
			||||||
 | 
								else {
 | 
				
			||||||
 | 
									PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
									if (BPF_CLASS(code) == BPF_ALU)
 | 
				
			||||||
 | 
										PPC_MULW(dst_reg, dst_reg,
 | 
				
			||||||
 | 
												b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
										PPC_MULD(dst_reg, dst_reg,
 | 
				
			||||||
 | 
												b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_DIV | BPF_X: /* (u32) dst /= (u32) src */
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_MOD | BPF_X: /* (u32) dst %= (u32) src */
 | 
				
			||||||
 | 
								PPC_CMPWI(src_reg, 0);
 | 
				
			||||||
 | 
								PPC_BCC_SHORT(COND_NE, (ctx->idx * 4) + 12);
 | 
				
			||||||
 | 
								PPC_LI(b2p[BPF_REG_0], 0);
 | 
				
			||||||
 | 
								PPC_JMP(exit_addr);
 | 
				
			||||||
 | 
								if (BPF_OP(code) == BPF_MOD) {
 | 
				
			||||||
 | 
									PPC_DIVWU(b2p[TMP_REG_1], dst_reg, src_reg);
 | 
				
			||||||
 | 
									PPC_MULW(b2p[TMP_REG_1], src_reg,
 | 
				
			||||||
 | 
											b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									PPC_SUB(dst_reg, dst_reg, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
								} else
 | 
				
			||||||
 | 
									PPC_DIVWU(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_DIV | BPF_X: /* dst /= src */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_MOD | BPF_X: /* dst %= src */
 | 
				
			||||||
 | 
								PPC_CMPDI(src_reg, 0);
 | 
				
			||||||
 | 
								PPC_BCC_SHORT(COND_NE, (ctx->idx * 4) + 12);
 | 
				
			||||||
 | 
								PPC_LI(b2p[BPF_REG_0], 0);
 | 
				
			||||||
 | 
								PPC_JMP(exit_addr);
 | 
				
			||||||
 | 
								if (BPF_OP(code) == BPF_MOD) {
 | 
				
			||||||
 | 
									PPC_DIVD(b2p[TMP_REG_1], dst_reg, src_reg);
 | 
				
			||||||
 | 
									PPC_MULD(b2p[TMP_REG_1], src_reg,
 | 
				
			||||||
 | 
											b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									PPC_SUB(dst_reg, dst_reg, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
								} else
 | 
				
			||||||
 | 
									PPC_DIVD(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_MOD | BPF_K: /* (u32) dst %= (u32) imm */
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_DIV | BPF_K: /* (u32) dst /= (u32) imm */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_MOD | BPF_K: /* dst %= imm */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_DIV | BPF_K: /* dst /= imm */
 | 
				
			||||||
 | 
								if (imm == 0)
 | 
				
			||||||
 | 
									return -EINVAL;
 | 
				
			||||||
 | 
								else if (imm == 1)
 | 
				
			||||||
 | 
									goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
								switch (BPF_CLASS(code)) {
 | 
				
			||||||
 | 
								case BPF_ALU:
 | 
				
			||||||
 | 
									if (BPF_OP(code) == BPF_MOD) {
 | 
				
			||||||
 | 
										PPC_DIVWU(b2p[TMP_REG_2], dst_reg,
 | 
				
			||||||
 | 
												b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
										PPC_MULW(b2p[TMP_REG_1],
 | 
				
			||||||
 | 
												b2p[TMP_REG_1],
 | 
				
			||||||
 | 
												b2p[TMP_REG_2]);
 | 
				
			||||||
 | 
										PPC_SUB(dst_reg, dst_reg,
 | 
				
			||||||
 | 
												b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									} else
 | 
				
			||||||
 | 
										PPC_DIVWU(dst_reg, dst_reg,
 | 
				
			||||||
 | 
												b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case BPF_ALU64:
 | 
				
			||||||
 | 
									if (BPF_OP(code) == BPF_MOD) {
 | 
				
			||||||
 | 
										PPC_DIVD(b2p[TMP_REG_2], dst_reg,
 | 
				
			||||||
 | 
												b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
										PPC_MULD(b2p[TMP_REG_1],
 | 
				
			||||||
 | 
												b2p[TMP_REG_1],
 | 
				
			||||||
 | 
												b2p[TMP_REG_2]);
 | 
				
			||||||
 | 
										PPC_SUB(dst_reg, dst_reg,
 | 
				
			||||||
 | 
												b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									} else
 | 
				
			||||||
 | 
										PPC_DIVD(dst_reg, dst_reg,
 | 
				
			||||||
 | 
												b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_NEG: /* (u32) dst = -dst */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_NEG: /* dst = -dst */
 | 
				
			||||||
 | 
								PPC_NEG(dst_reg, dst_reg);
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Logical operations: AND/OR/XOR/[A]LSH/[A]RSH
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_AND | BPF_X: /* (u32) dst = dst & src */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_AND | BPF_X: /* dst = dst & src */
 | 
				
			||||||
 | 
								PPC_AND(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_AND | BPF_K: /* (u32) dst = dst & imm */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_AND | BPF_K: /* dst = dst & imm */
 | 
				
			||||||
 | 
								if (!IMM_H(imm))
 | 
				
			||||||
 | 
									PPC_ANDI(dst_reg, dst_reg, IMM_L(imm));
 | 
				
			||||||
 | 
								else {
 | 
				
			||||||
 | 
									/* Sign-extended */
 | 
				
			||||||
 | 
									PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
									PPC_AND(dst_reg, dst_reg, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_OR | BPF_X: /* dst = (u32) dst | (u32) src */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_OR | BPF_X: /* dst = dst | src */
 | 
				
			||||||
 | 
								PPC_OR(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_OR | BPF_K:/* dst = (u32) dst | (u32) imm */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_OR | BPF_K:/* dst = dst | imm */
 | 
				
			||||||
 | 
								if (imm < 0 && BPF_CLASS(code) == BPF_ALU64) {
 | 
				
			||||||
 | 
									/* Sign-extended */
 | 
				
			||||||
 | 
									PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
									PPC_OR(dst_reg, dst_reg, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if (IMM_L(imm))
 | 
				
			||||||
 | 
										PPC_ORI(dst_reg, dst_reg, IMM_L(imm));
 | 
				
			||||||
 | 
									if (IMM_H(imm))
 | 
				
			||||||
 | 
										PPC_ORIS(dst_reg, dst_reg, IMM_H(imm));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_XOR | BPF_X: /* (u32) dst ^= src */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_XOR | BPF_X: /* dst ^= src */
 | 
				
			||||||
 | 
								PPC_XOR(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_XOR | BPF_K: /* (u32) dst ^= (u32) imm */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_XOR | BPF_K: /* dst ^= imm */
 | 
				
			||||||
 | 
								if (imm < 0 && BPF_CLASS(code) == BPF_ALU64) {
 | 
				
			||||||
 | 
									/* Sign-extended */
 | 
				
			||||||
 | 
									PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
									PPC_XOR(dst_reg, dst_reg, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if (IMM_L(imm))
 | 
				
			||||||
 | 
										PPC_XORI(dst_reg, dst_reg, IMM_L(imm));
 | 
				
			||||||
 | 
									if (IMM_H(imm))
 | 
				
			||||||
 | 
										PPC_XORIS(dst_reg, dst_reg, IMM_H(imm));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_LSH | BPF_X: /* (u32) dst <<= (u32) src */
 | 
				
			||||||
 | 
								/* slw clears top 32 bits */
 | 
				
			||||||
 | 
								PPC_SLW(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_LSH | BPF_X: /* dst <<= src; */
 | 
				
			||||||
 | 
								PPC_SLD(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_LSH | BPF_K: /* (u32) dst <<== (u32) imm */
 | 
				
			||||||
 | 
								/* with imm 0, we still need to clear top 32 bits */
 | 
				
			||||||
 | 
								PPC_SLWI(dst_reg, dst_reg, imm);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_LSH | BPF_K: /* dst <<== imm */
 | 
				
			||||||
 | 
								if (imm != 0)
 | 
				
			||||||
 | 
									PPC_SLDI(dst_reg, dst_reg, imm);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_RSH | BPF_X: /* (u32) dst >>= (u32) src */
 | 
				
			||||||
 | 
								PPC_SRW(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_RSH | BPF_X: /* dst >>= src */
 | 
				
			||||||
 | 
								PPC_SRD(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_RSH | BPF_K: /* (u32) dst >>= (u32) imm */
 | 
				
			||||||
 | 
								PPC_SRWI(dst_reg, dst_reg, imm);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_RSH | BPF_K: /* dst >>= imm */
 | 
				
			||||||
 | 
								if (imm != 0)
 | 
				
			||||||
 | 
									PPC_SRDI(dst_reg, dst_reg, imm);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_ARSH | BPF_X: /* (s64) dst >>= src */
 | 
				
			||||||
 | 
								PPC_SRAD(dst_reg, dst_reg, src_reg);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_ARSH | BPF_K: /* (s64) dst >>= imm */
 | 
				
			||||||
 | 
								if (imm != 0)
 | 
				
			||||||
 | 
									PPC_SRADI(dst_reg, dst_reg, imm);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * MOV
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_MOV | BPF_X: /* (u32) dst = src */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_MOV | BPF_X: /* dst = src */
 | 
				
			||||||
 | 
								PPC_MR(dst_reg, src_reg);
 | 
				
			||||||
 | 
								goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_MOV | BPF_K: /* (u32) dst = imm */
 | 
				
			||||||
 | 
							case BPF_ALU64 | BPF_MOV | BPF_K: /* dst = (s64) imm */
 | 
				
			||||||
 | 
								PPC_LI32(dst_reg, imm);
 | 
				
			||||||
 | 
								if (imm < 0)
 | 
				
			||||||
 | 
									goto bpf_alu32_trunc;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bpf_alu32_trunc:
 | 
				
			||||||
 | 
							/* Truncate to 32-bits */
 | 
				
			||||||
 | 
							if (BPF_CLASS(code) == BPF_ALU)
 | 
				
			||||||
 | 
								PPC_RLWINM(dst_reg, dst_reg, 0, 0, 31);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * BPF_FROM_BE/LE
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_END | BPF_FROM_LE:
 | 
				
			||||||
 | 
							case BPF_ALU | BPF_END | BPF_FROM_BE:
 | 
				
			||||||
 | 
					#ifdef __BIG_ENDIAN__
 | 
				
			||||||
 | 
								if (BPF_SRC(code) == BPF_FROM_BE)
 | 
				
			||||||
 | 
									goto emit_clear;
 | 
				
			||||||
 | 
					#else /* !__BIG_ENDIAN__ */
 | 
				
			||||||
 | 
								if (BPF_SRC(code) == BPF_FROM_LE)
 | 
				
			||||||
 | 
									goto emit_clear;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
								switch (imm) {
 | 
				
			||||||
 | 
								case 16:
 | 
				
			||||||
 | 
									/* Rotate 8 bits left & mask with 0x0000ff00 */
 | 
				
			||||||
 | 
									PPC_RLWINM(b2p[TMP_REG_1], dst_reg, 8, 16, 23);
 | 
				
			||||||
 | 
									/* Rotate 8 bits right & insert LSB to reg */
 | 
				
			||||||
 | 
									PPC_RLWIMI(b2p[TMP_REG_1], dst_reg, 24, 24, 31);
 | 
				
			||||||
 | 
									/* Move result back to dst_reg */
 | 
				
			||||||
 | 
									PPC_MR(dst_reg, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case 32:
 | 
				
			||||||
 | 
									/*
 | 
				
			||||||
 | 
									 * Rotate word left by 8 bits:
 | 
				
			||||||
 | 
									 * 2 bytes are already in their final position
 | 
				
			||||||
 | 
									 * -- byte 2 and 4 (of bytes 1, 2, 3 and 4)
 | 
				
			||||||
 | 
									 */
 | 
				
			||||||
 | 
									PPC_RLWINM(b2p[TMP_REG_1], dst_reg, 8, 0, 31);
 | 
				
			||||||
 | 
									/* Rotate 24 bits and insert byte 1 */
 | 
				
			||||||
 | 
									PPC_RLWIMI(b2p[TMP_REG_1], dst_reg, 24, 0, 7);
 | 
				
			||||||
 | 
									/* Rotate 24 bits and insert byte 3 */
 | 
				
			||||||
 | 
									PPC_RLWIMI(b2p[TMP_REG_1], dst_reg, 24, 16, 23);
 | 
				
			||||||
 | 
									PPC_MR(dst_reg, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case 64:
 | 
				
			||||||
 | 
									/*
 | 
				
			||||||
 | 
									 * Way easier and faster(?) to store the value
 | 
				
			||||||
 | 
									 * into stack and then use ldbrx
 | 
				
			||||||
 | 
									 *
 | 
				
			||||||
 | 
									 * First, determine where in stack we can store
 | 
				
			||||||
 | 
									 * this:
 | 
				
			||||||
 | 
									 * - if we have allotted a stack frame, then we
 | 
				
			||||||
 | 
									 *   will utilize the area set aside by
 | 
				
			||||||
 | 
									 *   BPF_PPC_STACK_LOCALS
 | 
				
			||||||
 | 
									 * - else, we use the area beneath the NV GPR
 | 
				
			||||||
 | 
									 *   save area
 | 
				
			||||||
 | 
									 *
 | 
				
			||||||
 | 
									 * ctx->seen will be reliable in pass2, but
 | 
				
			||||||
 | 
									 * the instructions generated will remain the
 | 
				
			||||||
 | 
									 * same across all passes
 | 
				
			||||||
 | 
									 */
 | 
				
			||||||
 | 
									if (bpf_has_stack_frame(ctx))
 | 
				
			||||||
 | 
										stack_local_off = STACK_FRAME_MIN_SIZE;
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
										stack_local_off = -(BPF_PPC_STACK_SAVE + 8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									PPC_STD(dst_reg, 1, stack_local_off);
 | 
				
			||||||
 | 
									PPC_ADDI(b2p[TMP_REG_1], 1, stack_local_off);
 | 
				
			||||||
 | 
									PPC_LDBRX(dst_reg, 0, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					emit_clear:
 | 
				
			||||||
 | 
								switch (imm) {
 | 
				
			||||||
 | 
								case 16:
 | 
				
			||||||
 | 
									/* zero-extend 16 bits into 64 bits */
 | 
				
			||||||
 | 
									PPC_RLDICL(dst_reg, dst_reg, 0, 48);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case 32:
 | 
				
			||||||
 | 
									/* zero-extend 32 bits into 64 bits */
 | 
				
			||||||
 | 
									PPC_RLDICL(dst_reg, dst_reg, 0, 32);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case 64:
 | 
				
			||||||
 | 
									/* nop */
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * BPF_ST(X)
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							case BPF_STX | BPF_MEM | BPF_B: /* *(u8 *)(dst + off) = src */
 | 
				
			||||||
 | 
							case BPF_ST | BPF_MEM | BPF_B: /* *(u8 *)(dst + off) = imm */
 | 
				
			||||||
 | 
								if (BPF_CLASS(code) == BPF_ST) {
 | 
				
			||||||
 | 
									PPC_LI(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
									src_reg = b2p[TMP_REG_1];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								PPC_STB(src_reg, dst_reg, off);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_STX | BPF_MEM | BPF_H: /* (u16 *)(dst + off) = src */
 | 
				
			||||||
 | 
							case BPF_ST | BPF_MEM | BPF_H: /* (u16 *)(dst + off) = imm */
 | 
				
			||||||
 | 
								if (BPF_CLASS(code) == BPF_ST) {
 | 
				
			||||||
 | 
									PPC_LI(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
									src_reg = b2p[TMP_REG_1];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								PPC_STH(src_reg, dst_reg, off);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_STX | BPF_MEM | BPF_W: /* *(u32 *)(dst + off) = src */
 | 
				
			||||||
 | 
							case BPF_ST | BPF_MEM | BPF_W: /* *(u32 *)(dst + off) = imm */
 | 
				
			||||||
 | 
								if (BPF_CLASS(code) == BPF_ST) {
 | 
				
			||||||
 | 
									PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
									src_reg = b2p[TMP_REG_1];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								PPC_STW(src_reg, dst_reg, off);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BPF_STX | BPF_MEM | BPF_DW: /* (u64 *)(dst + off) = src */
 | 
				
			||||||
 | 
							case BPF_ST | BPF_MEM | BPF_DW: /* *(u64 *)(dst + off) = imm */
 | 
				
			||||||
 | 
								if (BPF_CLASS(code) == BPF_ST) {
 | 
				
			||||||
 | 
									PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
									src_reg = b2p[TMP_REG_1];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								PPC_STD(src_reg, dst_reg, off);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * BPF_STX XADD (atomic_add)
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							/* *(u32 *)(dst + off) += src */
 | 
				
			||||||
 | 
							case BPF_STX | BPF_XADD | BPF_W:
 | 
				
			||||||
 | 
								/* Get EA into TMP_REG_1 */
 | 
				
			||||||
 | 
								PPC_ADDI(b2p[TMP_REG_1], dst_reg, off);
 | 
				
			||||||
 | 
								/* error if EA is not word-aligned */
 | 
				
			||||||
 | 
								PPC_ANDI(b2p[TMP_REG_2], b2p[TMP_REG_1], 0x03);
 | 
				
			||||||
 | 
								PPC_BCC_SHORT(COND_EQ, (ctx->idx * 4) + 12);
 | 
				
			||||||
 | 
								PPC_LI(b2p[BPF_REG_0], 0);
 | 
				
			||||||
 | 
								PPC_JMP(exit_addr);
 | 
				
			||||||
 | 
								/* load value from memory into TMP_REG_2 */
 | 
				
			||||||
 | 
								PPC_BPF_LWARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0);
 | 
				
			||||||
 | 
								/* add value from src_reg into this */
 | 
				
			||||||
 | 
								PPC_ADD(b2p[TMP_REG_2], b2p[TMP_REG_2], src_reg);
 | 
				
			||||||
 | 
								/* store result back */
 | 
				
			||||||
 | 
								PPC_BPF_STWCX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
								/* we're done if this succeeded */
 | 
				
			||||||
 | 
								PPC_BCC_SHORT(COND_EQ, (ctx->idx * 4) + (7*4));
 | 
				
			||||||
 | 
								/* otherwise, let's try once more */
 | 
				
			||||||
 | 
								PPC_BPF_LWARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0);
 | 
				
			||||||
 | 
								PPC_ADD(b2p[TMP_REG_2], b2p[TMP_REG_2], src_reg);
 | 
				
			||||||
 | 
								PPC_BPF_STWCX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
								/* exit if the store was not successful */
 | 
				
			||||||
 | 
								PPC_LI(b2p[BPF_REG_0], 0);
 | 
				
			||||||
 | 
								PPC_BCC(COND_NE, exit_addr);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							/* *(u64 *)(dst + off) += src */
 | 
				
			||||||
 | 
							case BPF_STX | BPF_XADD | BPF_DW:
 | 
				
			||||||
 | 
								PPC_ADDI(b2p[TMP_REG_1], dst_reg, off);
 | 
				
			||||||
 | 
								/* error if EA is not doubleword-aligned */
 | 
				
			||||||
 | 
								PPC_ANDI(b2p[TMP_REG_2], b2p[TMP_REG_1], 0x07);
 | 
				
			||||||
 | 
								PPC_BCC_SHORT(COND_EQ, (ctx->idx * 4) + (3*4));
 | 
				
			||||||
 | 
								PPC_LI(b2p[BPF_REG_0], 0);
 | 
				
			||||||
 | 
								PPC_JMP(exit_addr);
 | 
				
			||||||
 | 
								PPC_BPF_LDARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0);
 | 
				
			||||||
 | 
								PPC_ADD(b2p[TMP_REG_2], b2p[TMP_REG_2], src_reg);
 | 
				
			||||||
 | 
								PPC_BPF_STDCX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
								PPC_BCC_SHORT(COND_EQ, (ctx->idx * 4) + (7*4));
 | 
				
			||||||
 | 
								PPC_BPF_LDARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0);
 | 
				
			||||||
 | 
								PPC_ADD(b2p[TMP_REG_2], b2p[TMP_REG_2], src_reg);
 | 
				
			||||||
 | 
								PPC_BPF_STDCX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
								PPC_LI(b2p[BPF_REG_0], 0);
 | 
				
			||||||
 | 
								PPC_BCC(COND_NE, exit_addr);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * BPF_LDX
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							/* dst = *(u8 *)(ul) (src + off) */
 | 
				
			||||||
 | 
							case BPF_LDX | BPF_MEM | BPF_B:
 | 
				
			||||||
 | 
								PPC_LBZ(dst_reg, src_reg, off);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							/* dst = *(u16 *)(ul) (src + off) */
 | 
				
			||||||
 | 
							case BPF_LDX | BPF_MEM | BPF_H:
 | 
				
			||||||
 | 
								PPC_LHZ(dst_reg, src_reg, off);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							/* dst = *(u32 *)(ul) (src + off) */
 | 
				
			||||||
 | 
							case BPF_LDX | BPF_MEM | BPF_W:
 | 
				
			||||||
 | 
								PPC_LWZ(dst_reg, src_reg, off);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							/* dst = *(u64 *)(ul) (src + off) */
 | 
				
			||||||
 | 
							case BPF_LDX | BPF_MEM | BPF_DW:
 | 
				
			||||||
 | 
								PPC_LD(dst_reg, src_reg, off);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Doubleword load
 | 
				
			||||||
 | 
							 * 16 byte instruction that uses two 'struct bpf_insn'
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							case BPF_LD | BPF_IMM | BPF_DW: /* dst = (u64) imm */
 | 
				
			||||||
 | 
								imm64 = ((u64)(u32) insn[i].imm) |
 | 
				
			||||||
 | 
									    (((u64)(u32) insn[i+1].imm) << 32);
 | 
				
			||||||
 | 
								/* Adjust for two bpf instructions */
 | 
				
			||||||
 | 
								addrs[++i] = ctx->idx * 4;
 | 
				
			||||||
 | 
								PPC_LI64(dst_reg, imm64);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Return/Exit
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_EXIT:
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * If this isn't the very last instruction, branch to
 | 
				
			||||||
 | 
								 * the epilogue. If we _are_ the last instruction,
 | 
				
			||||||
 | 
								 * we'll just fall through to the epilogue.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								if (i != flen - 1)
 | 
				
			||||||
 | 
									PPC_JMP(exit_addr);
 | 
				
			||||||
 | 
								/* else fall through to the epilogue */
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Call kernel helper
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_CALL:
 | 
				
			||||||
 | 
								ctx->seen |= SEEN_FUNC;
 | 
				
			||||||
 | 
								func = (u8 *) __bpf_call_base + imm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Save skb pointer if we need to re-cache skb data */
 | 
				
			||||||
 | 
								if (bpf_helper_changes_skb_data(func))
 | 
				
			||||||
 | 
									PPC_BPF_STL(3, 1, STACK_FRAME_MIN_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								bpf_jit_emit_func_call(image, ctx, (u64)func);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* move return value from r3 to BPF_REG_0 */
 | 
				
			||||||
 | 
								PPC_MR(b2p[BPF_REG_0], 3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* refresh skb cache */
 | 
				
			||||||
 | 
								if (bpf_helper_changes_skb_data(func)) {
 | 
				
			||||||
 | 
									/* reload skb pointer to r3 */
 | 
				
			||||||
 | 
									PPC_BPF_LL(3, 1, STACK_FRAME_MIN_SIZE);
 | 
				
			||||||
 | 
									bpf_jit_emit_skb_loads(image, ctx);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Jumps and branches
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JA:
 | 
				
			||||||
 | 
								PPC_JMP(addrs[i + 1 + off]);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JGT | BPF_K:
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JGT | BPF_X:
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JSGT | BPF_K:
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JSGT | BPF_X:
 | 
				
			||||||
 | 
								true_cond = COND_GT;
 | 
				
			||||||
 | 
								goto cond_branch;
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JGE | BPF_K:
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JGE | BPF_X:
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JSGE | BPF_K:
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JSGE | BPF_X:
 | 
				
			||||||
 | 
								true_cond = COND_GE;
 | 
				
			||||||
 | 
								goto cond_branch;
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JEQ | BPF_K:
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JEQ | BPF_X:
 | 
				
			||||||
 | 
								true_cond = COND_EQ;
 | 
				
			||||||
 | 
								goto cond_branch;
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JNE | BPF_K:
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JNE | BPF_X:
 | 
				
			||||||
 | 
								true_cond = COND_NE;
 | 
				
			||||||
 | 
								goto cond_branch;
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JSET | BPF_K:
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_JSET | BPF_X:
 | 
				
			||||||
 | 
								true_cond = COND_NE;
 | 
				
			||||||
 | 
								/* Fall through */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cond_branch:
 | 
				
			||||||
 | 
								switch (code) {
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JGT | BPF_X:
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JGE | BPF_X:
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JEQ | BPF_X:
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JNE | BPF_X:
 | 
				
			||||||
 | 
									/* unsigned comparison */
 | 
				
			||||||
 | 
									PPC_CMPLD(dst_reg, src_reg);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JSGT | BPF_X:
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JSGE | BPF_X:
 | 
				
			||||||
 | 
									/* signed comparison */
 | 
				
			||||||
 | 
									PPC_CMPD(dst_reg, src_reg);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JSET | BPF_X:
 | 
				
			||||||
 | 
									PPC_AND_DOT(b2p[TMP_REG_1], dst_reg, src_reg);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JNE | BPF_K:
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JEQ | BPF_K:
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JGT | BPF_K:
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JGE | BPF_K:
 | 
				
			||||||
 | 
									/*
 | 
				
			||||||
 | 
									 * Need sign-extended load, so only positive
 | 
				
			||||||
 | 
									 * values can be used as imm in cmpldi
 | 
				
			||||||
 | 
									 */
 | 
				
			||||||
 | 
									if (imm >= 0 && imm < 32768)
 | 
				
			||||||
 | 
										PPC_CMPLDI(dst_reg, imm);
 | 
				
			||||||
 | 
									else {
 | 
				
			||||||
 | 
										/* sign-extending load */
 | 
				
			||||||
 | 
										PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
										/* ... but unsigned comparison */
 | 
				
			||||||
 | 
										PPC_CMPLD(dst_reg, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JSGT | BPF_K:
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JSGE | BPF_K:
 | 
				
			||||||
 | 
									/*
 | 
				
			||||||
 | 
									 * signed comparison, so any 16-bit value
 | 
				
			||||||
 | 
									 * can be used in cmpdi
 | 
				
			||||||
 | 
									 */
 | 
				
			||||||
 | 
									if (imm >= -32768 && imm < 32768)
 | 
				
			||||||
 | 
										PPC_CMPDI(dst_reg, imm);
 | 
				
			||||||
 | 
									else {
 | 
				
			||||||
 | 
										PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
										PPC_CMPD(dst_reg, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case BPF_JMP | BPF_JSET | BPF_K:
 | 
				
			||||||
 | 
									/* andi does not sign-extend the immediate */
 | 
				
			||||||
 | 
									if (imm >= 0 && imm < 32768)
 | 
				
			||||||
 | 
										/* PPC_ANDI is _only/always_ dot-form */
 | 
				
			||||||
 | 
										PPC_ANDI(b2p[TMP_REG_1], dst_reg, imm);
 | 
				
			||||||
 | 
									else {
 | 
				
			||||||
 | 
										PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
										PPC_AND_DOT(b2p[TMP_REG_1], dst_reg,
 | 
				
			||||||
 | 
											    b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								PPC_BCC(true_cond, addrs[i + 1 + off]);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Loads from packet header/data
 | 
				
			||||||
 | 
							 * Assume 32-bit input value in imm and X (src_reg)
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Absolute loads */
 | 
				
			||||||
 | 
							case BPF_LD | BPF_W | BPF_ABS:
 | 
				
			||||||
 | 
								func = (u8 *)CHOOSE_LOAD_FUNC(imm, sk_load_word);
 | 
				
			||||||
 | 
								goto common_load_abs;
 | 
				
			||||||
 | 
							case BPF_LD | BPF_H | BPF_ABS:
 | 
				
			||||||
 | 
								func = (u8 *)CHOOSE_LOAD_FUNC(imm, sk_load_half);
 | 
				
			||||||
 | 
								goto common_load_abs;
 | 
				
			||||||
 | 
							case BPF_LD | BPF_B | BPF_ABS:
 | 
				
			||||||
 | 
								func = (u8 *)CHOOSE_LOAD_FUNC(imm, sk_load_byte);
 | 
				
			||||||
 | 
					common_load_abs:
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * Load from [imm]
 | 
				
			||||||
 | 
								 * Load into r4, which can just be passed onto
 | 
				
			||||||
 | 
								 *  skb load helpers as the second parameter
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								PPC_LI32(4, imm);
 | 
				
			||||||
 | 
								goto common_load;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Indirect loads */
 | 
				
			||||||
 | 
							case BPF_LD | BPF_W | BPF_IND:
 | 
				
			||||||
 | 
								func = (u8 *)sk_load_word;
 | 
				
			||||||
 | 
								goto common_load_ind;
 | 
				
			||||||
 | 
							case BPF_LD | BPF_H | BPF_IND:
 | 
				
			||||||
 | 
								func = (u8 *)sk_load_half;
 | 
				
			||||||
 | 
								goto common_load_ind;
 | 
				
			||||||
 | 
							case BPF_LD | BPF_B | BPF_IND:
 | 
				
			||||||
 | 
								func = (u8 *)sk_load_byte;
 | 
				
			||||||
 | 
					common_load_ind:
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * Load from [src_reg + imm]
 | 
				
			||||||
 | 
								 * Treat src_reg as a 32-bit value
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								PPC_EXTSW(4, src_reg);
 | 
				
			||||||
 | 
								if (imm) {
 | 
				
			||||||
 | 
									if (imm >= -32768 && imm < 32768)
 | 
				
			||||||
 | 
										PPC_ADDI(4, 4, IMM_L(imm));
 | 
				
			||||||
 | 
									else {
 | 
				
			||||||
 | 
										PPC_LI32(b2p[TMP_REG_1], imm);
 | 
				
			||||||
 | 
										PPC_ADD(4, 4, b2p[TMP_REG_1]);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					common_load:
 | 
				
			||||||
 | 
								ctx->seen |= SEEN_SKB;
 | 
				
			||||||
 | 
								ctx->seen |= SEEN_FUNC;
 | 
				
			||||||
 | 
								bpf_jit_emit_func_call(image, ctx, (u64)func);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * Helper returns 'lt' condition on error, and an
 | 
				
			||||||
 | 
								 * appropriate return value in BPF_REG_0
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								PPC_BCC(COND_LT, exit_addr);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * TODO: Tail call
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							case BPF_JMP | BPF_CALL | BPF_X:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * The filter contains something cruel & unusual.
 | 
				
			||||||
 | 
								 * We don't handle it, but also there shouldn't be
 | 
				
			||||||
 | 
								 * anything missing from our list.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								pr_err_ratelimited("eBPF filter opcode %04x (@%d) unsupported\n",
 | 
				
			||||||
 | 
										code, i);
 | 
				
			||||||
 | 
								return -ENOTSUPP;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Set end-of-body-code address for exit. */
 | 
				
			||||||
 | 
						addrs[i] = ctx->idx * 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bpf_jit_compile(struct bpf_prog *fp) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 proglen;
 | 
				
			||||||
 | 
						u32 alloclen;
 | 
				
			||||||
 | 
						u8 *image = NULL;
 | 
				
			||||||
 | 
						u32 *code_base;
 | 
				
			||||||
 | 
						u32 *addrs;
 | 
				
			||||||
 | 
						struct codegen_context cgctx;
 | 
				
			||||||
 | 
						int pass;
 | 
				
			||||||
 | 
						int flen;
 | 
				
			||||||
 | 
						struct bpf_binary_header *bpf_hdr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!bpf_jit_enable)
 | 
				
			||||||
 | 
							return fp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						flen = fp->len;
 | 
				
			||||||
 | 
						addrs = kzalloc((flen+1) * sizeof(*addrs), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (addrs == NULL)
 | 
				
			||||||
 | 
							return fp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cgctx.idx = 0;
 | 
				
			||||||
 | 
						cgctx.seen = 0;
 | 
				
			||||||
 | 
						/* Scouting faux-generate pass 0 */
 | 
				
			||||||
 | 
						if (bpf_jit_build_body(fp, 0, &cgctx, addrs))
 | 
				
			||||||
 | 
							/* We hit something illegal or unsupported. */
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Pretend to build prologue, given the features we've seen.  This will
 | 
				
			||||||
 | 
						 * update ctgtx.idx as it pretends to output instructions, then we can
 | 
				
			||||||
 | 
						 * calculate total size from idx.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						bpf_jit_build_prologue(0, &cgctx);
 | 
				
			||||||
 | 
						bpf_jit_build_epilogue(0, &cgctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						proglen = cgctx.idx * 4;
 | 
				
			||||||
 | 
						alloclen = proglen + FUNCTION_DESCR_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bpf_hdr = bpf_jit_binary_alloc(alloclen, &image, 4,
 | 
				
			||||||
 | 
								bpf_jit_fill_ill_insns);
 | 
				
			||||||
 | 
						if (!bpf_hdr)
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						code_base = (u32 *)(image + FUNCTION_DESCR_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Code generation passes 1-2 */
 | 
				
			||||||
 | 
						for (pass = 1; pass < 3; pass++) {
 | 
				
			||||||
 | 
							/* Now build the prologue, body code & epilogue for real. */
 | 
				
			||||||
 | 
							cgctx.idx = 0;
 | 
				
			||||||
 | 
							bpf_jit_build_prologue(code_base, &cgctx);
 | 
				
			||||||
 | 
							bpf_jit_build_body(fp, code_base, &cgctx, addrs);
 | 
				
			||||||
 | 
							bpf_jit_build_epilogue(code_base, &cgctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (bpf_jit_enable > 1)
 | 
				
			||||||
 | 
								pr_info("Pass %d: shrink = %d, seen = 0x%x\n", pass,
 | 
				
			||||||
 | 
									proglen - (cgctx.idx * 4), cgctx.seen);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bpf_jit_enable > 1)
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Note that we output the base address of the code_base
 | 
				
			||||||
 | 
							 * rather than image, since opcodes are in code_base.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							bpf_jit_dump(flen, proglen, pass, code_base);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (image) {
 | 
				
			||||||
 | 
							bpf_flush_icache(bpf_hdr, image + alloclen);
 | 
				
			||||||
 | 
					#ifdef PPC64_ELF_ABI_v1
 | 
				
			||||||
 | 
							/* Function descriptor nastiness: Address + TOC */
 | 
				
			||||||
 | 
							((u64 *)image)[0] = (u64)code_base;
 | 
				
			||||||
 | 
							((u64 *)image)[1] = local_paca->kernel_toc;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							fp->bpf_func = (void *)image;
 | 
				
			||||||
 | 
							fp->jited = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						kfree(addrs);
 | 
				
			||||||
 | 
						return fp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bpf_jit_free(struct bpf_prog *fp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
 | 
				
			||||||
 | 
						struct bpf_binary_header *bpf_hdr = (void *)addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fp->jited)
 | 
				
			||||||
 | 
							bpf_jit_binary_free(bpf_hdr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bpf_prog_unlock_free(fp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in a new issue