|
| 1 | +#include "backends/tofino/bf-utils/include/dynamic_hash/bfn_hash_algorithm.h" |
| 2 | + |
| 3 | +#include <errno.h> |
| 4 | +#include <limits.h> |
| 5 | +#include <stdio.h> |
| 6 | +#include <stdlib.h> |
| 7 | +#include <string.h> |
| 8 | + |
| 9 | +/** |
| 10 | + * These values are based on the crc calculations provided at this url: |
| 11 | + * http://reveng.sourceforge.net/crc-catalogue/all.htm |
| 12 | + * |
| 13 | + * It is understood that the top bit of the the polynomial is known to be == 1, |
| 14 | + * but any polynomial over 64 bits would not fit. Thus the strings do not have |
| 15 | + * this value |
| 16 | + */ |
| 17 | +static crc_alg_info_t standard_crc_algs[CRC_INVALID] = { |
| 18 | + {"crc_8", 0x07, false, 0, 0}, |
| 19 | + {"crc_8_darc", 0x39, true, 0, 0}, |
| 20 | + {"crc_8_i_code", 0x1d, false, 0xfd, 0}, |
| 21 | + {"crc_8_itu", 0x07, false, 0x55, 0x55}, |
| 22 | + {"crc_8_maxim", 0x31, true, 0, 0}, |
| 23 | + {"crc_8_rohc", 0x07, true, 0xff, 0}, |
| 24 | + {"crc_8_wcdma", 0x9b, true, 0, 0}, |
| 25 | + {"crc_16", 0x8005, true, 0, 0}, |
| 26 | + {"crc_16_bypass", 0x8005, false, 0, 0}, |
| 27 | + {"crc_16_dds_110", 0x8005, false, 0x800d, 0}, |
| 28 | + // This one doesn't appear on the website, but kept to not break |
| 29 | + // regressions |
| 30 | + {"crc_16_dect", 0x0589, false, 1, 1}, |
| 31 | + {"crc_16_dect_r", 0x0589, false, 0, 1}, |
| 32 | + {"crc_16_dect_x", 0x0589, false, 0, 0}, |
| 33 | + {"crc_16_dnp", 0x3d65, true, 0, 0xffff}, |
| 34 | + {"crc_16_en_13757", 0x3d65, false, 0, 0xffff}, |
| 35 | + {"crc_16_genibus", 0x1021, false, 0xffff, 0xffff}, |
| 36 | + {"crc_16_maxim", 0x8005, true, 0, 0xffff}, |
| 37 | + {"crc_16_mcrf4xx", 0x1021, true, 0xffff, 0}, |
| 38 | + {"crc_16_riello", 0x1021, true, 0xb2aa, 0}, |
| 39 | + {"crc_16_t10_dif", 0x8bb7, false, 0, 0}, |
| 40 | + {"crc_16_teledisk", 0xa097, false, 0, 0}, |
| 41 | + {"crc_16_usb", 0x8005, true, 0xffff, 0xffff}, |
| 42 | + {"x_25", 0x1021, true, 0xffff, 0xffff}, |
| 43 | + {"xmodem", 0x1021, false, 0, 0}, |
| 44 | + {"modbus", 0x8005, true, 0xffff, 0}, |
| 45 | + {"kermit", 0x1021, true, 0, 0}, |
| 46 | + {"crc_ccitt_false", 0x1021, false, 0xffff, 0}, |
| 47 | + {"crc_aug_ccitt", 0x1021, false, 0x1d0f, 0}, |
| 48 | + {"crc_32", 0x04c11db7, true, 0xffffffff, 0xffffffff}, |
| 49 | + {"crc_32_bzip2", 0x04c11db7, false, 0xffffffff, 0xffffffff}, |
| 50 | + {"crc_32c", 0x1edc6f41, true, 0xffffffff, 0xffffffff}, |
| 51 | + {"crc_32d", 0xa833982b, true, 0xffffffff, 0xffffffff}, |
| 52 | + {"crc_32_mpeg", 0x04c11db7, false, 0xffffffff, 0}, |
| 53 | + {"posix", 0x04c11db7, false, 0, 0xffffffff}, |
| 54 | + {"crc_32q", 0x814141ab, false, 0, 0}, |
| 55 | + {"jamcrc", 0x04c11db7, true, 0xffffffff, 0}, |
| 56 | + {"xfer", 0x000000af, false, 0, 0}, |
| 57 | + {"crc_64", 0x42f0e1eba9ea3693, false, 0, 0}, |
| 58 | + {"crc_64_go_iso", 0x000000000000001b, true, 0xffffffffffffffff, 0xffffffffffffffff}, |
| 59 | + {"crc_64_we", 0x42f0e1eba9ea3693, false, 0xffffffffffffffff, 0xffffffffffffffff}, |
| 60 | + // This one doesn't appear on the website, but kept to not break |
| 61 | + // regressions |
| 62 | + {"crc_64_jones", 0xad93d23594c935a9, true, 0xffffffffffffffff, 0}}; |
| 63 | + |
| 64 | +int determine_msb(uint64_t value) { |
| 65 | + int rv = -1; |
| 66 | +#if defined(__GNUC__) || defined(__clang__) |
| 67 | + if (value) rv = 63 - __builtin_clzl(value); |
| 68 | +#else |
| 69 | + for (int i = 63; i >= 0; i--) { |
| 70 | + if (((1ULL << i) & value) != 0ULL) return i; |
| 71 | + } |
| 72 | +#endif |
| 73 | + return rv; |
| 74 | +} |
| 75 | + |
| 76 | +#define BUILD_CRC_ERRORS |
| 77 | + |
| 78 | +static const char *bfn_crc_errors[BUILD_CRC_ERRORS] = { |
| 79 | + "Polynomial is not odd", |
| 80 | + "Init value is larger than the polynomial", |
| 81 | + "Final xor value is larger than the polynomial", |
| 82 | +}; |
| 83 | + |
| 84 | +bool verify_algorithm(bfn_hash_algorithm_t *alg, const char **error_message) { |
| 85 | + if ((alg->poly & 1) == 0) { |
| 86 | + if (error_message) *error_message = bfn_crc_errors[0]; |
| 87 | + return false; |
| 88 | + } |
| 89 | + if (alg->init && determine_msb(alg->init) + 1 > alg->hash_bit_width) { |
| 90 | + if (error_message) *error_message = bfn_crc_errors[1]; |
| 91 | + return false; |
| 92 | + } |
| 93 | + if (alg->final_xor && determine_msb(alg->final_xor) + 1 > alg->hash_bit_width) { |
| 94 | + if (error_message) *error_message = bfn_crc_errors[2]; |
| 95 | + return false; |
| 96 | + } |
| 97 | + return true; |
| 98 | +} |
| 99 | + |
| 100 | +/** |
| 101 | + * Builds the algorithm from the standard crc algorithm position |
| 102 | + */ |
| 103 | +void build_standard_crc_algorithm(bfn_hash_algorithm_t *alg, bfn_crc_alg_t crc_alg) { |
| 104 | + if (crc_alg >= CRC_8 && crc_alg <= CRC_8_WCDMA) |
| 105 | + alg->hash_bit_width = 8; |
| 106 | + else if (crc_alg >= CRC_16 && crc_alg <= CRC_AUG_CCITT) |
| 107 | + alg->hash_bit_width = 16; |
| 108 | + else if (crc_alg >= CRC_32 && crc_alg <= XFER) |
| 109 | + alg->hash_bit_width = 32; |
| 110 | + else if (crc_alg >= CRC_64 && crc_alg <= CRC_64_JONES) |
| 111 | + alg->hash_bit_width = 64; |
| 112 | + |
| 113 | + crc_alg_info_t crc_alg_info = standard_crc_algs[(int)crc_alg]; |
| 114 | + alg->poly = crc_alg_info.poly; |
| 115 | + alg->reverse = crc_alg_info.reverse; |
| 116 | + alg->init = crc_alg_info.init; |
| 117 | + alg->final_xor = crc_alg_info.final_xor; |
| 118 | + alg->crc_type = crc_alg_str_to_type(crc_alg_info.crc_name); |
| 119 | +} |
| 120 | + |
| 121 | +void initialize_algorithm(bfn_hash_algorithm_t *alg, bfn_hash_alg_type_t hash_alg, bool msb, |
| 122 | + bool extend, bfn_crc_alg_t crc_alg) { |
| 123 | + alg->hash_alg = hash_alg; |
| 124 | + alg->msb = msb; |
| 125 | + alg->extend = extend; |
| 126 | + if (crc_alg != CRC_INVALID) build_standard_crc_algorithm(alg, crc_alg); |
| 127 | +} |
| 128 | + |
| 129 | +static uint64_t invert_poly(uint64_t poly, int bit_width) { |
| 130 | + uint64_t ret = 0, i = 0; |
| 131 | + while (poly > 0) { |
| 132 | + ret |= ((poly & 0x1) << (bit_width - i - 1)); |
| 133 | + i++; |
| 134 | + poly >>= 1; |
| 135 | + } |
| 136 | + return ret; |
| 137 | +} |
| 138 | + |
| 139 | +static void construct_stream(bfn_hash_algorithm_t *alg, uint64_t val, uint8_t *stream) { |
| 140 | + uint8_t width = (alg->hash_bit_width + 7) / 8; |
| 141 | + for (uint8_t i = 0; i < width; i++) { |
| 142 | + stream[width - i - 1] = (val >> (i * 8)); |
| 143 | + } |
| 144 | + return; |
| 145 | +} |
| 146 | + |
| 147 | +static void shift_right(uint8_t *stream, uint8_t bit_width) { |
| 148 | + uint8_t width = (bit_width + 7) / 8; |
| 149 | + for (int i = width - 1; i >= 0; i--) { |
| 150 | + stream[i] >>= 1; |
| 151 | + if (i > 0) { |
| 152 | + stream[i] |= (stream[i - 1] & 0x1) << 7; |
| 153 | + } |
| 154 | + } |
| 155 | +} |
| 156 | + |
| 157 | +static void shift_left(uint8_t *stream, uint8_t bit_width) { |
| 158 | + uint8_t width = (bit_width + 7) / 8; |
| 159 | + for (uint8_t i = 0; i < width; i++) { |
| 160 | + stream[i] <<= 1; |
| 161 | + if (i < width - 1) { |
| 162 | + stream[i] |= (stream[i + 1] >> 7) & 0x1; |
| 163 | + } |
| 164 | + } |
| 165 | +} |
| 166 | + |
| 167 | +void initialize_crc_matrix(bfn_hash_algorithm_t *alg) { |
| 168 | + if (alg->hash_alg != CRC_DYN) { |
| 169 | + return; |
| 170 | + } |
| 171 | + |
| 172 | + uint32_t width = (alg->hash_bit_width + 7) / 8, i = 0; |
| 173 | + std::vector<uint8_t> poly(width, 0), rem(width, 0); |
| 174 | + |
| 175 | + uint64_t poly_val = alg->poly; |
| 176 | + if (alg->reverse) { |
| 177 | + poly_val = invert_poly(poly_val, alg->hash_bit_width); |
| 178 | + } |
| 179 | + construct_stream(alg, poly_val, poly.data()); |
| 180 | + uint32_t byte = 0, bit = 0; |
| 181 | + bool need_xor = false; |
| 182 | + if (alg->reverse) { |
| 183 | + for (byte = 0; byte < 256; byte++) { |
| 184 | + memset(rem.data(), 0, width * sizeof(uint8_t)); |
| 185 | + rem[width - 1] = byte; |
| 186 | + for (bit = 0; bit < 8; bit++) { |
| 187 | + need_xor = rem[width - 1] & 0x1; |
| 188 | + shift_right(rem.data(), alg->hash_bit_width); |
| 189 | + if (need_xor) { |
| 190 | + for (i = 0; i < width; i++) { |
| 191 | + rem[i] ^= poly[i]; |
| 192 | + } |
| 193 | + } |
| 194 | + } |
| 195 | + memcpy(alg->crc_matrix[byte], rem.data(), width); |
| 196 | + } |
| 197 | + } else { |
| 198 | + uint8_t offset = alg->hash_bit_width % 8; |
| 199 | + uint8_t top_bit = offset == 0 ? 0x80 : 1 << ((offset - 1) % 8); |
| 200 | + uint8_t top_mask = top_bit == 0x80 ? 0xff : (top_bit << 1) - 1; |
| 201 | + for (byte = 0; byte < 256; byte++) { |
| 202 | + memset(rem.data(), 0, width * sizeof(uint8_t)); |
| 203 | + if (offset == 0) { |
| 204 | + rem[0] = byte; |
| 205 | + } else { |
| 206 | + rem[0] = byte >> (8 - offset); |
| 207 | + rem[1] = byte << offset; |
| 208 | + } |
| 209 | + for (bit = 0; bit < 8; bit++) { |
| 210 | + need_xor = rem[0] & top_bit; |
| 211 | + shift_left(rem.data(), alg->hash_bit_width); |
| 212 | + if (need_xor) { |
| 213 | + for (i = 0; i < width; i++) { |
| 214 | + rem[i] ^= poly[i]; |
| 215 | + } |
| 216 | + } |
| 217 | + rem[0] &= top_mask; |
| 218 | + } |
| 219 | + memcpy(alg->crc_matrix[byte], rem.data(), width); |
| 220 | + } |
| 221 | + } |
| 222 | + |
| 223 | + return; |
| 224 | +} |
| 225 | + |
| 226 | +static uint8_t get_byte(uint8_t *stream, uint8_t index, uint8_t offset) { |
| 227 | + if (offset == 0) { |
| 228 | + return stream[index]; |
| 229 | + } |
| 230 | + return ((stream[index] << (8 - offset)) | (stream[index + 1] >> offset)); |
| 231 | +} |
| 232 | + |
| 233 | +void calculate_crc(bfn_hash_algorithm_t *alg, uint32_t hash_output_bits, uint8_t *stream, |
| 234 | + uint32_t stream_len, uint8_t *crc) { |
| 235 | + uint32_t i = 0; |
| 236 | + uint8_t idx = 0; |
| 237 | + uint8_t width = (alg->hash_bit_width + 7) / 8; |
| 238 | + uint8_t hash_output_bytes = (hash_output_bits + 7) / 8; |
| 239 | + std::vector<uint8_t> final_xor(width, 0); |
| 240 | + memset(crc, 0, hash_output_bytes * sizeof(uint8_t)); |
| 241 | + construct_stream(alg, alg->init, crc); |
| 242 | + construct_stream(alg, alg->final_xor, final_xor.data()); |
| 243 | + uint8_t *crc_str = crc + hash_output_bytes - width; |
| 244 | + |
| 245 | + for (i = 0; i < stream_len; i++) { |
| 246 | + if (alg->reverse) { |
| 247 | + idx = crc_str[width - 1] ^ stream[i]; |
| 248 | + for (int j = width - 1; j > 0; j--) { |
| 249 | + crc_str[j] = crc_str[j - 1] ^ alg->crc_matrix[idx][j]; |
| 250 | + } |
| 251 | + crc_str[0] = alg->crc_matrix[idx][0]; |
| 252 | + } else { |
| 253 | + uint8_t offset = alg->hash_bit_width % 8; |
| 254 | + uint8_t mask = offset == 0 ? 0xff : (1 << offset) - 1; |
| 255 | + idx = get_byte(crc_str, 0, offset) ^ stream[i]; |
| 256 | + for (uint8_t j = 0; j < width - 1; j++) { |
| 257 | + crc_str[j] = (crc_str[j + 1] ^ alg->crc_matrix[idx][j]) & mask; |
| 258 | + mask = 0xff; |
| 259 | + } |
| 260 | + crc_str[width - 1] = alg->crc_matrix[idx][width - 1]; |
| 261 | + } |
| 262 | + } |
| 263 | + |
| 264 | + for (i = 0; i < width; i++) { |
| 265 | + crc_str[i] ^= final_xor[i]; |
| 266 | + } |
| 267 | + |
| 268 | + if (alg->extend) { |
| 269 | + for (i = 0; i < (uint32_t)(hash_output_bytes - width); i++) { |
| 270 | + *(crc_str - i - 1) = crc_str[width - i % width - 1]; |
| 271 | + } |
| 272 | + if (hash_output_bits % 8) { |
| 273 | + crc[0] &= (1 << (hash_output_bits % 8)) - 1; |
| 274 | + } |
| 275 | + } |
| 276 | + |
| 277 | + return; |
| 278 | +} |
0 commit comments