1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2f1174f77SEdward Cree /* tnum: tracked (or tristate) numbers 3f1174f77SEdward Cree * 4f1174f77SEdward Cree * A tnum tracks knowledge about the bits of a value. Each bit can be either 5f1174f77SEdward Cree * known (0 or 1), or unknown (x). Arithmetic operations on tnums will 6f1174f77SEdward Cree * propagate the unknown bits such that the tnum result represents all the 7f1174f77SEdward Cree * possible results for possible values of the operands. 8f1174f77SEdward Cree */ 9f1174f77SEdward Cree #include <linux/kernel.h> 10f1174f77SEdward Cree #include <linux/tnum.h> 11f1174f77SEdward Cree 12f1174f77SEdward Cree #define TNUM(_v, _m) (struct tnum){.value = _v, .mask = _m} 13f1174f77SEdward Cree /* A completely unknown value */ 14f1174f77SEdward Cree const struct tnum tnum_unknown = { .value = 0, .mask = -1 }; 15f1174f77SEdward Cree 16f1174f77SEdward Cree struct tnum tnum_const(u64 value) 17f1174f77SEdward Cree { 18f1174f77SEdward Cree return TNUM(value, 0); 19f1174f77SEdward Cree } 20f1174f77SEdward Cree 21b03c9f9fSEdward Cree struct tnum tnum_range(u64 min, u64 max) 22b03c9f9fSEdward Cree { 23b03c9f9fSEdward Cree u64 chi = min ^ max, delta; 24b03c9f9fSEdward Cree u8 bits = fls64(chi); 25b03c9f9fSEdward Cree 26b03c9f9fSEdward Cree /* special case, needed because 1ULL << 64 is undefined */ 27b03c9f9fSEdward Cree if (bits > 63) 28b03c9f9fSEdward Cree return tnum_unknown; 29b03c9f9fSEdward Cree /* e.g. if chi = 4, bits = 3, delta = (1<<3) - 1 = 7. 30b03c9f9fSEdward Cree * if chi = 0, bits = 0, delta = (1<<0) - 1 = 0, so we return 31b03c9f9fSEdward Cree * constant min (since min == max). 32b03c9f9fSEdward Cree */ 33b03c9f9fSEdward Cree delta = (1ULL << bits) - 1; 34b03c9f9fSEdward Cree return TNUM(min & ~delta, delta); 35b03c9f9fSEdward Cree } 36b03c9f9fSEdward Cree 37f1174f77SEdward Cree struct tnum tnum_lshift(struct tnum a, u8 shift) 38f1174f77SEdward Cree { 39f1174f77SEdward Cree return TNUM(a.value << shift, a.mask << shift); 40f1174f77SEdward Cree } 41f1174f77SEdward Cree 42f1174f77SEdward Cree struct tnum tnum_rshift(struct tnum a, u8 shift) 43f1174f77SEdward Cree { 44f1174f77SEdward Cree return TNUM(a.value >> shift, a.mask >> shift); 45f1174f77SEdward Cree } 46f1174f77SEdward Cree 470af2ffc9SDaniel Borkmann struct tnum tnum_arshift(struct tnum a, u8 min_shift, u8 insn_bitness) 489cbe1f5aSYonghong Song { 499cbe1f5aSYonghong Song /* if a.value is negative, arithmetic shifting by minimum shift 509cbe1f5aSYonghong Song * will have larger negative offset compared to more shifting. 519cbe1f5aSYonghong Song * If a.value is nonnegative, arithmetic shifting by minimum shift 529cbe1f5aSYonghong Song * will have larger positive offset compare to more shifting. 539cbe1f5aSYonghong Song */ 540af2ffc9SDaniel Borkmann if (insn_bitness == 32) 550af2ffc9SDaniel Borkmann return TNUM((u32)(((s32)a.value) >> min_shift), 560af2ffc9SDaniel Borkmann (u32)(((s32)a.mask) >> min_shift)); 570af2ffc9SDaniel Borkmann else 580af2ffc9SDaniel Borkmann return TNUM((s64)a.value >> min_shift, 590af2ffc9SDaniel Borkmann (s64)a.mask >> min_shift); 609cbe1f5aSYonghong Song } 619cbe1f5aSYonghong Song 62f1174f77SEdward Cree struct tnum tnum_add(struct tnum a, struct tnum b) 63f1174f77SEdward Cree { 64f1174f77SEdward Cree u64 sm, sv, sigma, chi, mu; 65f1174f77SEdward Cree 66f1174f77SEdward Cree sm = a.mask + b.mask; 67f1174f77SEdward Cree sv = a.value + b.value; 68f1174f77SEdward Cree sigma = sm + sv; 69f1174f77SEdward Cree chi = sigma ^ sv; 70f1174f77SEdward Cree mu = chi | a.mask | b.mask; 71f1174f77SEdward Cree return TNUM(sv & ~mu, mu); 72f1174f77SEdward Cree } 73f1174f77SEdward Cree 74f1174f77SEdward Cree struct tnum tnum_sub(struct tnum a, struct tnum b) 75f1174f77SEdward Cree { 76f1174f77SEdward Cree u64 dv, alpha, beta, chi, mu; 77f1174f77SEdward Cree 78f1174f77SEdward Cree dv = a.value - b.value; 79f1174f77SEdward Cree alpha = dv + a.mask; 80f1174f77SEdward Cree beta = dv - b.mask; 81f1174f77SEdward Cree chi = alpha ^ beta; 82f1174f77SEdward Cree mu = chi | a.mask | b.mask; 83f1174f77SEdward Cree return TNUM(dv & ~mu, mu); 84f1174f77SEdward Cree } 85f1174f77SEdward Cree 86f1174f77SEdward Cree struct tnum tnum_and(struct tnum a, struct tnum b) 87f1174f77SEdward Cree { 88f1174f77SEdward Cree u64 alpha, beta, v; 89f1174f77SEdward Cree 90f1174f77SEdward Cree alpha = a.value | a.mask; 91f1174f77SEdward Cree beta = b.value | b.mask; 92f1174f77SEdward Cree v = a.value & b.value; 93f1174f77SEdward Cree return TNUM(v, alpha & beta & ~v); 94f1174f77SEdward Cree } 95f1174f77SEdward Cree 96f1174f77SEdward Cree struct tnum tnum_or(struct tnum a, struct tnum b) 97f1174f77SEdward Cree { 98f1174f77SEdward Cree u64 v, mu; 99f1174f77SEdward Cree 100f1174f77SEdward Cree v = a.value | b.value; 101f1174f77SEdward Cree mu = a.mask | b.mask; 102f1174f77SEdward Cree return TNUM(v, mu & ~v); 103f1174f77SEdward Cree } 104f1174f77SEdward Cree 105f1174f77SEdward Cree struct tnum tnum_xor(struct tnum a, struct tnum b) 106f1174f77SEdward Cree { 107f1174f77SEdward Cree u64 v, mu; 108f1174f77SEdward Cree 109f1174f77SEdward Cree v = a.value ^ b.value; 110f1174f77SEdward Cree mu = a.mask | b.mask; 111f1174f77SEdward Cree return TNUM(v & ~mu, mu); 112f1174f77SEdward Cree } 113f1174f77SEdward Cree 114*05924717SHarishankar Vishwanathan /* Generate partial products by multiplying each bit in the multiplier (tnum a) 115*05924717SHarishankar Vishwanathan * with the multiplicand (tnum b), and add the partial products after 116*05924717SHarishankar Vishwanathan * appropriately bit-shifting them. Instead of directly performing tnum addition 117*05924717SHarishankar Vishwanathan * on the generated partial products, equivalenty, decompose each partial 118*05924717SHarishankar Vishwanathan * product into two tnums, consisting of the value-sum (acc_v) and the 119*05924717SHarishankar Vishwanathan * mask-sum (acc_m) and then perform tnum addition on them. The following paper 120*05924717SHarishankar Vishwanathan * explains the algorithm in more detail: https://arxiv.org/abs/2105.05398. 121f1174f77SEdward Cree */ 122f1174f77SEdward Cree struct tnum tnum_mul(struct tnum a, struct tnum b) 123f1174f77SEdward Cree { 124*05924717SHarishankar Vishwanathan u64 acc_v = a.value * b.value; 125*05924717SHarishankar Vishwanathan struct tnum acc_m = TNUM(0, 0); 126f1174f77SEdward Cree 127*05924717SHarishankar Vishwanathan while (a.value || a.mask) { 128*05924717SHarishankar Vishwanathan /* LSB of tnum a is a certain 1 */ 129*05924717SHarishankar Vishwanathan if (a.value & 1) 130*05924717SHarishankar Vishwanathan acc_m = tnum_add(acc_m, TNUM(0, b.mask)); 131*05924717SHarishankar Vishwanathan /* LSB of tnum a is uncertain */ 132*05924717SHarishankar Vishwanathan else if (a.mask & 1) 133*05924717SHarishankar Vishwanathan acc_m = tnum_add(acc_m, TNUM(0, b.value | b.mask)); 134*05924717SHarishankar Vishwanathan /* Note: no case for LSB is certain 0 */ 135*05924717SHarishankar Vishwanathan a = tnum_rshift(a, 1); 136*05924717SHarishankar Vishwanathan b = tnum_lshift(b, 1); 137*05924717SHarishankar Vishwanathan } 138*05924717SHarishankar Vishwanathan return tnum_add(TNUM(acc_v, 0), acc_m); 139f1174f77SEdward Cree } 140f1174f77SEdward Cree 141f1174f77SEdward Cree /* Note that if a and b disagree - i.e. one has a 'known 1' where the other has 142f1174f77SEdward Cree * a 'known 0' - this will return a 'known 1' for that bit. 143f1174f77SEdward Cree */ 144f1174f77SEdward Cree struct tnum tnum_intersect(struct tnum a, struct tnum b) 145f1174f77SEdward Cree { 146f1174f77SEdward Cree u64 v, mu; 147f1174f77SEdward Cree 148f1174f77SEdward Cree v = a.value | b.value; 149f1174f77SEdward Cree mu = a.mask & b.mask; 150f1174f77SEdward Cree return TNUM(v & ~mu, mu); 151f1174f77SEdward Cree } 152f1174f77SEdward Cree 153f1174f77SEdward Cree struct tnum tnum_cast(struct tnum a, u8 size) 154f1174f77SEdward Cree { 155f1174f77SEdward Cree a.value &= (1ULL << (size * 8)) - 1; 156f1174f77SEdward Cree a.mask &= (1ULL << (size * 8)) - 1; 157f1174f77SEdward Cree return a; 158f1174f77SEdward Cree } 159f1174f77SEdward Cree 160f1174f77SEdward Cree bool tnum_is_aligned(struct tnum a, u64 size) 161f1174f77SEdward Cree { 162f1174f77SEdward Cree if (!size) 163f1174f77SEdward Cree return true; 164f1174f77SEdward Cree return !((a.value | a.mask) & (size - 1)); 165f1174f77SEdward Cree } 166f1174f77SEdward Cree 167f1174f77SEdward Cree bool tnum_in(struct tnum a, struct tnum b) 168f1174f77SEdward Cree { 169f1174f77SEdward Cree if (b.mask & ~a.mask) 170f1174f77SEdward Cree return false; 171f1174f77SEdward Cree b.value &= ~a.mask; 172f1174f77SEdward Cree return a.value == b.value; 173f1174f77SEdward Cree } 174f1174f77SEdward Cree 175f1174f77SEdward Cree int tnum_strn(char *str, size_t size, struct tnum a) 176f1174f77SEdward Cree { 177f1174f77SEdward Cree return snprintf(str, size, "(%#llx; %#llx)", a.value, a.mask); 178f1174f77SEdward Cree } 179f1174f77SEdward Cree EXPORT_SYMBOL_GPL(tnum_strn); 180f1174f77SEdward Cree 181f1174f77SEdward Cree int tnum_sbin(char *str, size_t size, struct tnum a) 182f1174f77SEdward Cree { 183f1174f77SEdward Cree size_t n; 184f1174f77SEdward Cree 185f1174f77SEdward Cree for (n = 64; n; n--) { 186f1174f77SEdward Cree if (n < size) { 187f1174f77SEdward Cree if (a.mask & 1) 188f1174f77SEdward Cree str[n - 1] = 'x'; 189f1174f77SEdward Cree else if (a.value & 1) 190f1174f77SEdward Cree str[n - 1] = '1'; 191f1174f77SEdward Cree else 192f1174f77SEdward Cree str[n - 1] = '0'; 193f1174f77SEdward Cree } 194f1174f77SEdward Cree a.mask >>= 1; 195f1174f77SEdward Cree a.value >>= 1; 196f1174f77SEdward Cree } 197f1174f77SEdward Cree str[min(size - 1, (size_t)64)] = 0; 198f1174f77SEdward Cree return 64; 199f1174f77SEdward Cree } 2003f50f132SJohn Fastabend 2013f50f132SJohn Fastabend struct tnum tnum_subreg(struct tnum a) 2023f50f132SJohn Fastabend { 2033f50f132SJohn Fastabend return tnum_cast(a, 4); 2043f50f132SJohn Fastabend } 2053f50f132SJohn Fastabend 2063f50f132SJohn Fastabend struct tnum tnum_clear_subreg(struct tnum a) 2073f50f132SJohn Fastabend { 2083f50f132SJohn Fastabend return tnum_lshift(tnum_rshift(a, 32), 32); 2093f50f132SJohn Fastabend } 2103f50f132SJohn Fastabend 2113f50f132SJohn Fastabend struct tnum tnum_const_subreg(struct tnum a, u32 value) 2123f50f132SJohn Fastabend { 2133f50f132SJohn Fastabend return tnum_or(tnum_clear_subreg(a), tnum_const(value)); 2143f50f132SJohn Fastabend } 215