forked from mirrors/linux
		
	bpf/verifier: rework value tracking
Unifies adjusted and unadjusted register value types (e.g. FRAME_POINTER is now just a PTR_TO_STACK with zero offset). Tracks value alignment by means of tracking known & unknown bits. This also replaces the 'reg->imm' (leading zero bits) calculations for (what were) UNKNOWN_VALUEs. If pointer leaks are allowed, and adjust_ptr_min_max_vals returns -EACCES, treat the pointer as an unknown scalar and try again, because we might be able to conclude something about the result (e.g. pointer & 0x40 is either 0 or 0x40). Verifier hooks in the netronome/nfp driver were changed to match the new data structures. Signed-off-by: Edward Cree <ecree@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									e1cb90f2b8
								
							
						
					
					
						commit
						f1174f77b5
					
				
					 7 changed files with 1277 additions and 864 deletions
				
			
		| 
						 | 
					@ -79,28 +79,32 @@ nfp_bpf_check_exit(struct nfp_prog *nfp_prog,
 | 
				
			||||||
		   const struct bpf_verifier_env *env)
 | 
							   const struct bpf_verifier_env *env)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const struct bpf_reg_state *reg0 = &env->cur_state.regs[0];
 | 
						const struct bpf_reg_state *reg0 = &env->cur_state.regs[0];
 | 
				
			||||||
 | 
						u64 imm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nfp_prog->act == NN_ACT_XDP)
 | 
						if (nfp_prog->act == NN_ACT_XDP)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (reg0->type != CONST_IMM) {
 | 
						if (!(reg0->type == SCALAR_VALUE && tnum_is_const(reg0->var_off))) {
 | 
				
			||||||
		pr_info("unsupported exit state: %d, imm: %llx\n",
 | 
							char tn_buf[48];
 | 
				
			||||||
			reg0->type, reg0->imm);
 | 
					
 | 
				
			||||||
 | 
							tnum_strn(tn_buf, sizeof(tn_buf), reg0->var_off);
 | 
				
			||||||
 | 
							pr_info("unsupported exit state: %d, var_off: %s\n",
 | 
				
			||||||
 | 
								reg0->type, tn_buf);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nfp_prog->act != NN_ACT_DIRECT &&
 | 
						imm = reg0->var_off.value;
 | 
				
			||||||
	    reg0->imm != 0 && (reg0->imm & ~0U) != ~0U) {
 | 
						if (nfp_prog->act != NN_ACT_DIRECT && imm != 0 && (imm & ~0U) != ~0U) {
 | 
				
			||||||
		pr_info("unsupported exit state: %d, imm: %llx\n",
 | 
							pr_info("unsupported exit state: %d, imm: %llx\n",
 | 
				
			||||||
			reg0->type, reg0->imm);
 | 
								reg0->type, imm);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nfp_prog->act == NN_ACT_DIRECT && reg0->imm <= TC_ACT_REDIRECT &&
 | 
						if (nfp_prog->act == NN_ACT_DIRECT && imm <= TC_ACT_REDIRECT &&
 | 
				
			||||||
	    reg0->imm != TC_ACT_SHOT && reg0->imm != TC_ACT_STOLEN &&
 | 
						    imm != TC_ACT_SHOT && imm != TC_ACT_STOLEN &&
 | 
				
			||||||
	    reg0->imm != TC_ACT_QUEUED) {
 | 
						    imm != TC_ACT_QUEUED) {
 | 
				
			||||||
		pr_info("unsupported exit state: %d, imm: %llx\n",
 | 
							pr_info("unsupported exit state: %d, imm: %llx\n",
 | 
				
			||||||
			reg0->type, reg0->imm);
 | 
								reg0->type, imm);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -117,35 +117,25 @@ enum bpf_access_type {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* types of values stored in eBPF registers */
 | 
					/* types of values stored in eBPF registers */
 | 
				
			||||||
 | 
					/* Pointer types represent:
 | 
				
			||||||
 | 
					 * pointer
 | 
				
			||||||
 | 
					 * pointer + imm
 | 
				
			||||||
 | 
					 * pointer + (u16) var
 | 
				
			||||||
 | 
					 * pointer + (u16) var + imm
 | 
				
			||||||
 | 
					 * if (range > 0) then [ptr, ptr + range - off) is safe to access
 | 
				
			||||||
 | 
					 * if (id > 0) means that some 'var' was added
 | 
				
			||||||
 | 
					 * if (off > 0) means that 'imm' was added
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
enum bpf_reg_type {
 | 
					enum bpf_reg_type {
 | 
				
			||||||
	NOT_INIT = 0,		 /* nothing was written into register */
 | 
						NOT_INIT = 0,		 /* nothing was written into register */
 | 
				
			||||||
	UNKNOWN_VALUE,		 /* reg doesn't contain a valid pointer */
 | 
						SCALAR_VALUE,		 /* reg doesn't contain a valid pointer */
 | 
				
			||||||
	PTR_TO_CTX,		 /* reg points to bpf_context */
 | 
						PTR_TO_CTX,		 /* reg points to bpf_context */
 | 
				
			||||||
	CONST_PTR_TO_MAP,	 /* reg points to struct bpf_map */
 | 
						CONST_PTR_TO_MAP,	 /* reg points to struct bpf_map */
 | 
				
			||||||
	PTR_TO_MAP_VALUE,	 /* reg points to map element value */
 | 
						PTR_TO_MAP_VALUE,	 /* reg points to map element value */
 | 
				
			||||||
	PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */
 | 
						PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */
 | 
				
			||||||
	FRAME_PTR,		 /* reg == frame_pointer */
 | 
						PTR_TO_STACK,		 /* reg == frame_pointer + offset */
 | 
				
			||||||
	PTR_TO_STACK,		 /* reg == frame_pointer + imm */
 | 
						PTR_TO_PACKET,		 /* reg points to skb->data */
 | 
				
			||||||
	CONST_IMM,		 /* constant integer value */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* PTR_TO_PACKET represents:
 | 
					 | 
				
			||||||
	 * skb->data
 | 
					 | 
				
			||||||
	 * skb->data + imm
 | 
					 | 
				
			||||||
	 * skb->data + (u16) var
 | 
					 | 
				
			||||||
	 * skb->data + (u16) var + imm
 | 
					 | 
				
			||||||
	 * if (range > 0) then [ptr, ptr + range - off) is safe to access
 | 
					 | 
				
			||||||
	 * if (id > 0) means that some 'var' was added
 | 
					 | 
				
			||||||
	 * if (off > 0) menas that 'imm' was added
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	PTR_TO_PACKET,
 | 
					 | 
				
			||||||
	PTR_TO_PACKET_END,	 /* skb->data + headlen */
 | 
						PTR_TO_PACKET_END,	 /* skb->data + headlen */
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* PTR_TO_MAP_VALUE_ADJ is used for doing pointer math inside of a map
 | 
					 | 
				
			||||||
	 * elem value.  We only allow this if we can statically verify that
 | 
					 | 
				
			||||||
	 * access from this register are going to fall within the size of the
 | 
					 | 
				
			||||||
	 * map element.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	PTR_TO_MAP_VALUE_ADJ,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct bpf_prog;
 | 
					struct bpf_prog;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/bpf.h> /* for enum bpf_reg_type */
 | 
					#include <linux/bpf.h> /* for enum bpf_reg_type */
 | 
				
			||||||
#include <linux/filter.h> /* for MAX_BPF_STACK */
 | 
					#include <linux/filter.h> /* for MAX_BPF_STACK */
 | 
				
			||||||
 | 
					#include <linux/tnum.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 /* Just some arbitrary values so we can safely do math without overflowing and
 | 
					 /* Just some arbitrary values so we can safely do math without overflowing and
 | 
				
			||||||
  * are obviously wrong for any sort of memory access.
 | 
					  * are obviously wrong for any sort of memory access.
 | 
				
			||||||
| 
						 | 
					@ -19,30 +20,37 @@
 | 
				
			||||||
struct bpf_reg_state {
 | 
					struct bpf_reg_state {
 | 
				
			||||||
	enum bpf_reg_type type;
 | 
						enum bpf_reg_type type;
 | 
				
			||||||
	union {
 | 
						union {
 | 
				
			||||||
		/* valid when type == CONST_IMM | PTR_TO_STACK | UNKNOWN_VALUE */
 | 
							/* valid when type == PTR_TO_PACKET */
 | 
				
			||||||
		s64 imm;
 | 
							u16 range;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* valid when type == PTR_TO_PACKET* */
 | 
					 | 
				
			||||||
		struct {
 | 
					 | 
				
			||||||
			u16 off;
 | 
					 | 
				
			||||||
			u16 range;
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* valid when type == CONST_PTR_TO_MAP | PTR_TO_MAP_VALUE |
 | 
							/* valid when type == CONST_PTR_TO_MAP | PTR_TO_MAP_VALUE |
 | 
				
			||||||
		 *   PTR_TO_MAP_VALUE_OR_NULL
 | 
							 *   PTR_TO_MAP_VALUE_OR_NULL
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		struct bpf_map *map_ptr;
 | 
							struct bpf_map *map_ptr;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
						/* Fixed part of pointer offset, pointer types only */
 | 
				
			||||||
 | 
						s32 off;
 | 
				
			||||||
 | 
						/* For PTR_TO_PACKET, used to find other pointers with the same variable
 | 
				
			||||||
 | 
						 * offset, so they can share range knowledge.
 | 
				
			||||||
 | 
						 * For PTR_TO_MAP_VALUE_OR_NULL this is used to share which map value we
 | 
				
			||||||
 | 
						 * came from, when one is tested for != NULL.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	u32 id;
 | 
						u32 id;
 | 
				
			||||||
 | 
						/* These three fields must be last.  See states_equal() */
 | 
				
			||||||
 | 
						/* For scalar types (SCALAR_VALUE), this represents our knowledge of
 | 
				
			||||||
 | 
						 * the actual value.
 | 
				
			||||||
 | 
						 * For pointer types, this represents the variable part of the offset
 | 
				
			||||||
 | 
						 * from the pointed-to object, and is shared with all bpf_reg_states
 | 
				
			||||||
 | 
						 * with the same id as us.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						struct tnum var_off;
 | 
				
			||||||
	/* Used to determine if any memory access using this register will
 | 
						/* Used to determine if any memory access using this register will
 | 
				
			||||||
	 * result in a bad access. These two fields must be last.
 | 
						 * result in a bad access.
 | 
				
			||||||
	 * See states_equal()
 | 
						 * These refer to the same value as var_off, not necessarily the actual
 | 
				
			||||||
 | 
						 * contents of the register.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	s64 min_value;
 | 
						s64 min_value;
 | 
				
			||||||
	u64 max_value;
 | 
						u64 max_value;
 | 
				
			||||||
	u32 min_align;
 | 
					 | 
				
			||||||
	u32 aux_off;
 | 
					 | 
				
			||||||
	u32 aux_off_align;
 | 
					 | 
				
			||||||
	bool value_from_signed;
 | 
						bool value_from_signed;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										79
									
								
								include/linux/tnum.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								include/linux/tnum.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,79 @@
 | 
				
			||||||
 | 
					/* tnum: tracked (or tristate) numbers
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * A tnum tracks knowledge about the bits of a value.  Each bit can be either
 | 
				
			||||||
 | 
					 * known (0 or 1), or unknown (x).  Arithmetic operations on tnums will
 | 
				
			||||||
 | 
					 * propagate the unknown bits such that the tnum result represents all the
 | 
				
			||||||
 | 
					 * possible results for possible values of the operands.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <linux/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum {
 | 
				
			||||||
 | 
						u64 value;
 | 
				
			||||||
 | 
						u64 mask;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Constructors */
 | 
				
			||||||
 | 
					/* Represent a known constant as a tnum. */
 | 
				
			||||||
 | 
					struct tnum tnum_const(u64 value);
 | 
				
			||||||
 | 
					/* A completely unknown value */
 | 
				
			||||||
 | 
					extern const struct tnum tnum_unknown;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Arithmetic and logical ops */
 | 
				
			||||||
 | 
					/* Shift a tnum left (by a fixed shift) */
 | 
				
			||||||
 | 
					struct tnum tnum_lshift(struct tnum a, u8 shift);
 | 
				
			||||||
 | 
					/* Shift a tnum right (by a fixed shift) */
 | 
				
			||||||
 | 
					struct tnum tnum_rshift(struct tnum a, u8 shift);
 | 
				
			||||||
 | 
					/* Add two tnums, return @a + @b */
 | 
				
			||||||
 | 
					struct tnum tnum_add(struct tnum a, struct tnum b);
 | 
				
			||||||
 | 
					/* Subtract two tnums, return @a - @b */
 | 
				
			||||||
 | 
					struct tnum tnum_sub(struct tnum a, struct tnum b);
 | 
				
			||||||
 | 
					/* Bitwise-AND, return @a & @b */
 | 
				
			||||||
 | 
					struct tnum tnum_and(struct tnum a, struct tnum b);
 | 
				
			||||||
 | 
					/* Bitwise-OR, return @a | @b */
 | 
				
			||||||
 | 
					struct tnum tnum_or(struct tnum a, struct tnum b);
 | 
				
			||||||
 | 
					/* Bitwise-XOR, return @a ^ @b */
 | 
				
			||||||
 | 
					struct tnum tnum_xor(struct tnum a, struct tnum b);
 | 
				
			||||||
 | 
					/* Multiply two tnums, return @a * @b */
 | 
				
			||||||
 | 
					struct tnum tnum_mul(struct tnum a, struct tnum b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Return a tnum representing numbers satisfying both @a and @b */
 | 
				
			||||||
 | 
					struct tnum tnum_intersect(struct tnum a, struct tnum b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Return @a with all but the lowest @size bytes cleared */
 | 
				
			||||||
 | 
					struct tnum tnum_cast(struct tnum a, u8 size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Returns true if @a is a known constant */
 | 
				
			||||||
 | 
					static inline bool tnum_is_const(struct tnum a)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return !a.mask;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Returns true if @a == tnum_const(@b) */
 | 
				
			||||||
 | 
					static inline bool tnum_equals_const(struct tnum a, u64 b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return tnum_is_const(a) && a.value == b;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Returns true if @a is completely unknown */
 | 
				
			||||||
 | 
					static inline bool tnum_is_unknown(struct tnum a)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return !~a.mask;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Returns true if @a is known to be a multiple of @size.
 | 
				
			||||||
 | 
					 * @size must be a power of two.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool tnum_is_aligned(struct tnum a, u64 size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Returns true if @b represents a subset of @a. */
 | 
				
			||||||
 | 
					bool tnum_in(struct tnum a, struct tnum b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Formatting functions.  These have snprintf-like semantics: they will write
 | 
				
			||||||
 | 
					 * up to @size bytes (including the terminating NUL byte), and return the number
 | 
				
			||||||
 | 
					 * of bytes (excluding the terminating NUL) which would have been written had
 | 
				
			||||||
 | 
					 * sufficient space been available.  (Thus tnum_sbin always returns 64.)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					/* Format a tnum as a pair of hex numbers (value; mask) */
 | 
				
			||||||
 | 
					int tnum_strn(char *str, size_t size, struct tnum a);
 | 
				
			||||||
 | 
					/* Format a tnum as tristate binary expansion */
 | 
				
			||||||
 | 
					int tnum_sbin(char *str, size_t size, struct tnum a);
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
obj-y := core.o
 | 
					obj-y := core.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o
 | 
					obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o
 | 
				
			||||||
obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
 | 
					obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
 | 
				
			||||||
ifeq ($(CONFIG_NET),y)
 | 
					ifeq ($(CONFIG_NET),y)
 | 
				
			||||||
obj-$(CONFIG_BPF_SYSCALL) += devmap.o
 | 
					obj-$(CONFIG_BPF_SYSCALL) += devmap.o
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										164
									
								
								kernel/bpf/tnum.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								kernel/bpf/tnum.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,164 @@
 | 
				
			||||||
 | 
					/* tnum: tracked (or tristate) numbers
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * A tnum tracks knowledge about the bits of a value.  Each bit can be either
 | 
				
			||||||
 | 
					 * known (0 or 1), or unknown (x).  Arithmetic operations on tnums will
 | 
				
			||||||
 | 
					 * propagate the unknown bits such that the tnum result represents all the
 | 
				
			||||||
 | 
					 * possible results for possible values of the operands.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
 | 
					#include <linux/tnum.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TNUM(_v, _m)	(struct tnum){.value = _v, .mask = _m}
 | 
				
			||||||
 | 
					/* A completely unknown value */
 | 
				
			||||||
 | 
					const struct tnum tnum_unknown = { .value = 0, .mask = -1 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum tnum_const(u64 value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return TNUM(value, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum tnum_lshift(struct tnum a, u8 shift)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return TNUM(a.value << shift, a.mask << shift);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum tnum_rshift(struct tnum a, u8 shift)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return TNUM(a.value >> shift, a.mask >> shift);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum tnum_add(struct tnum a, struct tnum b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u64 sm, sv, sigma, chi, mu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sm = a.mask + b.mask;
 | 
				
			||||||
 | 
						sv = a.value + b.value;
 | 
				
			||||||
 | 
						sigma = sm + sv;
 | 
				
			||||||
 | 
						chi = sigma ^ sv;
 | 
				
			||||||
 | 
						mu = chi | a.mask | b.mask;
 | 
				
			||||||
 | 
						return TNUM(sv & ~mu, mu);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum tnum_sub(struct tnum a, struct tnum b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u64 dv, alpha, beta, chi, mu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dv = a.value - b.value;
 | 
				
			||||||
 | 
						alpha = dv + a.mask;
 | 
				
			||||||
 | 
						beta = dv - b.mask;
 | 
				
			||||||
 | 
						chi = alpha ^ beta;
 | 
				
			||||||
 | 
						mu = chi | a.mask | b.mask;
 | 
				
			||||||
 | 
						return TNUM(dv & ~mu, mu);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum tnum_and(struct tnum a, struct tnum b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u64 alpha, beta, v;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						alpha = a.value | a.mask;
 | 
				
			||||||
 | 
						beta = b.value | b.mask;
 | 
				
			||||||
 | 
						v = a.value & b.value;
 | 
				
			||||||
 | 
						return TNUM(v, alpha & beta & ~v);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum tnum_or(struct tnum a, struct tnum b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u64 v, mu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v = a.value | b.value;
 | 
				
			||||||
 | 
						mu = a.mask | b.mask;
 | 
				
			||||||
 | 
						return TNUM(v, mu & ~v);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum tnum_xor(struct tnum a, struct tnum b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u64 v, mu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v = a.value ^ b.value;
 | 
				
			||||||
 | 
						mu = a.mask | b.mask;
 | 
				
			||||||
 | 
						return TNUM(v & ~mu, mu);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* half-multiply add: acc += (unknown * mask * value).
 | 
				
			||||||
 | 
					 * An intermediate step in the multiply algorithm.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static struct tnum hma(struct tnum acc, u64 value, u64 mask)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						while (mask) {
 | 
				
			||||||
 | 
							if (mask & 1)
 | 
				
			||||||
 | 
								acc = tnum_add(acc, TNUM(0, value));
 | 
				
			||||||
 | 
							mask >>= 1;
 | 
				
			||||||
 | 
							value <<= 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return acc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum tnum_mul(struct tnum a, struct tnum b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct tnum acc;
 | 
				
			||||||
 | 
						u64 pi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pi = a.value * b.value;
 | 
				
			||||||
 | 
						acc = hma(TNUM(pi, 0), a.mask, b.mask | b.value);
 | 
				
			||||||
 | 
						return hma(acc, b.mask, a.value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Note that if a and b disagree - i.e. one has a 'known 1' where the other has
 | 
				
			||||||
 | 
					 * a 'known 0' - this will return a 'known 1' for that bit.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct tnum tnum_intersect(struct tnum a, struct tnum b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u64 v, mu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						v = a.value | b.value;
 | 
				
			||||||
 | 
						mu = a.mask & b.mask;
 | 
				
			||||||
 | 
						return TNUM(v & ~mu, mu);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct tnum tnum_cast(struct tnum a, u8 size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						a.value &= (1ULL << (size * 8)) - 1;
 | 
				
			||||||
 | 
						a.mask &= (1ULL << (size * 8)) - 1;
 | 
				
			||||||
 | 
						return a;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool tnum_is_aligned(struct tnum a, u64 size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!size)
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						return !((a.value | a.mask) & (size - 1));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool tnum_in(struct tnum a, struct tnum b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (b.mask & ~a.mask)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						b.value &= ~a.mask;
 | 
				
			||||||
 | 
						return a.value == b.value;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int tnum_strn(char *str, size_t size, struct tnum a)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return snprintf(str, size, "(%#llx; %#llx)", a.value, a.mask);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(tnum_strn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int tnum_sbin(char *str, size_t size, struct tnum a)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (n = 64; n; n--) {
 | 
				
			||||||
 | 
							if (n < size) {
 | 
				
			||||||
 | 
								if (a.mask & 1)
 | 
				
			||||||
 | 
									str[n - 1] = 'x';
 | 
				
			||||||
 | 
								else if (a.value & 1)
 | 
				
			||||||
 | 
									str[n - 1] = '1';
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									str[n - 1] = '0';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							a.mask >>= 1;
 | 
				
			||||||
 | 
							a.value >>= 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						str[min(size - 1, (size_t)64)] = 0;
 | 
				
			||||||
 | 
						return 64;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
		Reference in a new issue