14c6de856SChristian Hitz /* 24c6de856SChristian Hitz * Generic binary BCH encoding/decoding library 34c6de856SChristian Hitz * 45b8031ccSTom Rini * SPDX-License-Identifier: GPL-2.0 54c6de856SChristian Hitz * 64c6de856SChristian Hitz * Copyright © 2011 Parrot S.A. 74c6de856SChristian Hitz * 84c6de856SChristian Hitz * Author: Ivan Djelic <ivan.djelic@parrot.com> 94c6de856SChristian Hitz * 104c6de856SChristian Hitz * Description: 114c6de856SChristian Hitz * 124c6de856SChristian Hitz * This library provides runtime configurable encoding/decoding of binary 134c6de856SChristian Hitz * Bose-Chaudhuri-Hocquenghem (BCH) codes. 144c6de856SChristian Hitz * 154c6de856SChristian Hitz * Call init_bch to get a pointer to a newly allocated bch_control structure for 164c6de856SChristian Hitz * the given m (Galois field order), t (error correction capability) and 174c6de856SChristian Hitz * (optional) primitive polynomial parameters. 184c6de856SChristian Hitz * 194c6de856SChristian Hitz * Call encode_bch to compute and store ecc parity bytes to a given buffer. 204c6de856SChristian Hitz * Call decode_bch to detect and locate errors in received data. 214c6de856SChristian Hitz * 224c6de856SChristian Hitz * On systems supporting hw BCH features, intermediate results may be provided 234c6de856SChristian Hitz * to decode_bch in order to skip certain steps. See decode_bch() documentation 244c6de856SChristian Hitz * for details. 254c6de856SChristian Hitz * 264c6de856SChristian Hitz * Option CONFIG_BCH_CONST_PARAMS can be used to force fixed values of 274c6de856SChristian Hitz * parameters m and t; thus allowing extra compiler optimizations and providing 284c6de856SChristian Hitz * better (up to 2x) encoding performance. Using this option makes sense when 294c6de856SChristian Hitz * (m,t) are fixed and known in advance, e.g. when using BCH error correction 304c6de856SChristian Hitz * on a particular NAND flash device. 314c6de856SChristian Hitz * 324c6de856SChristian Hitz * Algorithmic details: 334c6de856SChristian Hitz * 344c6de856SChristian Hitz * Encoding is performed by processing 32 input bits in parallel, using 4 354c6de856SChristian Hitz * remainder lookup tables. 364c6de856SChristian Hitz * 374c6de856SChristian Hitz * The final stage of decoding involves the following internal steps: 384c6de856SChristian Hitz * a. Syndrome computation 394c6de856SChristian Hitz * b. Error locator polynomial computation using Berlekamp-Massey algorithm 404c6de856SChristian Hitz * c. Error locator root finding (by far the most expensive step) 414c6de856SChristian Hitz * 424c6de856SChristian Hitz * In this implementation, step c is not performed using the usual Chien search. 434c6de856SChristian Hitz * Instead, an alternative approach described in [1] is used. It consists in 444c6de856SChristian Hitz * factoring the error locator polynomial using the Berlekamp Trace algorithm 454c6de856SChristian Hitz * (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial 464c6de856SChristian Hitz * solving techniques [2] are used. The resulting algorithm, called BTZ, yields 474c6de856SChristian Hitz * much better performance than Chien search for usual (m,t) values (typically 484c6de856SChristian Hitz * m >= 13, t < 32, see [1]). 494c6de856SChristian Hitz * 504c6de856SChristian Hitz * [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields 514c6de856SChristian Hitz * of characteristic 2, in: Western European Workshop on Research in Cryptology 524c6de856SChristian Hitz * - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear. 534c6de856SChristian Hitz * [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over 544c6de856SChristian Hitz * finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996. 554c6de856SChristian Hitz */ 564c6de856SChristian Hitz 57*71d2c070SMaxime Ripard #ifndef USE_HOSTCC 584c6de856SChristian Hitz #include <common.h> 594c6de856SChristian Hitz #include <ubi_uboot.h> 604c6de856SChristian Hitz 614c6de856SChristian Hitz #include <linux/bitops.h> 62*71d2c070SMaxime Ripard #else 63*71d2c070SMaxime Ripard #include <errno.h> 64*71d2c070SMaxime Ripard #include <endian.h> 65*71d2c070SMaxime Ripard #include <stdint.h> 66*71d2c070SMaxime Ripard #include <stdlib.h> 67*71d2c070SMaxime Ripard #include <string.h> 68*71d2c070SMaxime Ripard 69*71d2c070SMaxime Ripard #undef cpu_to_be32 70*71d2c070SMaxime Ripard #define cpu_to_be32 htobe32 71*71d2c070SMaxime Ripard #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) 72*71d2c070SMaxime Ripard #define kmalloc(size, flags) malloc(size) 73*71d2c070SMaxime Ripard #define kzalloc(size, flags) calloc(1, size) 74*71d2c070SMaxime Ripard #define kfree free 75*71d2c070SMaxime Ripard #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 76*71d2c070SMaxime Ripard #endif 77*71d2c070SMaxime Ripard 784c6de856SChristian Hitz #include <asm/byteorder.h> 794c6de856SChristian Hitz #include <linux/bch.h> 804c6de856SChristian Hitz 814c6de856SChristian Hitz #if defined(CONFIG_BCH_CONST_PARAMS) 824c6de856SChristian Hitz #define GF_M(_p) (CONFIG_BCH_CONST_M) 834c6de856SChristian Hitz #define GF_T(_p) (CONFIG_BCH_CONST_T) 844c6de856SChristian Hitz #define GF_N(_p) ((1 << (CONFIG_BCH_CONST_M))-1) 854c6de856SChristian Hitz #else 864c6de856SChristian Hitz #define GF_M(_p) ((_p)->m) 874c6de856SChristian Hitz #define GF_T(_p) ((_p)->t) 884c6de856SChristian Hitz #define GF_N(_p) ((_p)->n) 894c6de856SChristian Hitz #endif 904c6de856SChristian Hitz 914c6de856SChristian Hitz #define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) 924c6de856SChristian Hitz #define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) 934c6de856SChristian Hitz 944c6de856SChristian Hitz #ifndef dbg 954c6de856SChristian Hitz #define dbg(_fmt, args...) do {} while (0) 964c6de856SChristian Hitz #endif 974c6de856SChristian Hitz 984c6de856SChristian Hitz /* 994c6de856SChristian Hitz * represent a polynomial over GF(2^m) 1004c6de856SChristian Hitz */ 1014c6de856SChristian Hitz struct gf_poly { 1024c6de856SChristian Hitz unsigned int deg; /* polynomial degree */ 1034c6de856SChristian Hitz unsigned int c[0]; /* polynomial terms */ 1044c6de856SChristian Hitz }; 1054c6de856SChristian Hitz 1064c6de856SChristian Hitz /* given its degree, compute a polynomial size in bytes */ 1074c6de856SChristian Hitz #define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) 1084c6de856SChristian Hitz 1094c6de856SChristian Hitz /* polynomial of degree 1 */ 1104c6de856SChristian Hitz struct gf_poly_deg1 { 1114c6de856SChristian Hitz struct gf_poly poly; 1124c6de856SChristian Hitz unsigned int c[2]; 1134c6de856SChristian Hitz }; 1144c6de856SChristian Hitz 115*71d2c070SMaxime Ripard #ifdef USE_HOSTCC 116*71d2c070SMaxime Ripard static int fls(int x) 117*71d2c070SMaxime Ripard { 118*71d2c070SMaxime Ripard int r = 32; 119*71d2c070SMaxime Ripard 120*71d2c070SMaxime Ripard if (!x) 121*71d2c070SMaxime Ripard return 0; 122*71d2c070SMaxime Ripard if (!(x & 0xffff0000u)) { 123*71d2c070SMaxime Ripard x <<= 16; 124*71d2c070SMaxime Ripard r -= 16; 125*71d2c070SMaxime Ripard } 126*71d2c070SMaxime Ripard if (!(x & 0xff000000u)) { 127*71d2c070SMaxime Ripard x <<= 8; 128*71d2c070SMaxime Ripard r -= 8; 129*71d2c070SMaxime Ripard } 130*71d2c070SMaxime Ripard if (!(x & 0xf0000000u)) { 131*71d2c070SMaxime Ripard x <<= 4; 132*71d2c070SMaxime Ripard r -= 4; 133*71d2c070SMaxime Ripard } 134*71d2c070SMaxime Ripard if (!(x & 0xc0000000u)) { 135*71d2c070SMaxime Ripard x <<= 2; 136*71d2c070SMaxime Ripard r -= 2; 137*71d2c070SMaxime Ripard } 138*71d2c070SMaxime Ripard if (!(x & 0x80000000u)) { 139*71d2c070SMaxime Ripard x <<= 1; 140*71d2c070SMaxime Ripard r -= 1; 141*71d2c070SMaxime Ripard } 142*71d2c070SMaxime Ripard return r; 143*71d2c070SMaxime Ripard } 144*71d2c070SMaxime Ripard #endif 145*71d2c070SMaxime Ripard 1464c6de856SChristian Hitz /* 1474c6de856SChristian Hitz * same as encode_bch(), but process input data one byte at a time 1484c6de856SChristian Hitz */ 1494c6de856SChristian Hitz static void encode_bch_unaligned(struct bch_control *bch, 1504c6de856SChristian Hitz const unsigned char *data, unsigned int len, 1514c6de856SChristian Hitz uint32_t *ecc) 1524c6de856SChristian Hitz { 1534c6de856SChristian Hitz int i; 1544c6de856SChristian Hitz const uint32_t *p; 1554c6de856SChristian Hitz const int l = BCH_ECC_WORDS(bch)-1; 1564c6de856SChristian Hitz 1574c6de856SChristian Hitz while (len--) { 1584c6de856SChristian Hitz p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff); 1594c6de856SChristian Hitz 1604c6de856SChristian Hitz for (i = 0; i < l; i++) 1614c6de856SChristian Hitz ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); 1624c6de856SChristian Hitz 1634c6de856SChristian Hitz ecc[l] = (ecc[l] << 8)^(*p); 1644c6de856SChristian Hitz } 1654c6de856SChristian Hitz } 1664c6de856SChristian Hitz 1674c6de856SChristian Hitz /* 1684c6de856SChristian Hitz * convert ecc bytes to aligned, zero-padded 32-bit ecc words 1694c6de856SChristian Hitz */ 1704c6de856SChristian Hitz static void load_ecc8(struct bch_control *bch, uint32_t *dst, 1714c6de856SChristian Hitz const uint8_t *src) 1724c6de856SChristian Hitz { 1734c6de856SChristian Hitz uint8_t pad[4] = {0, 0, 0, 0}; 1744c6de856SChristian Hitz unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; 1754c6de856SChristian Hitz 1764c6de856SChristian Hitz for (i = 0; i < nwords; i++, src += 4) 1774c6de856SChristian Hitz dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3]; 1784c6de856SChristian Hitz 1794c6de856SChristian Hitz memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); 1804c6de856SChristian Hitz dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3]; 1814c6de856SChristian Hitz } 1824c6de856SChristian Hitz 1834c6de856SChristian Hitz /* 1844c6de856SChristian Hitz * convert 32-bit ecc words to ecc bytes 1854c6de856SChristian Hitz */ 1864c6de856SChristian Hitz static void store_ecc8(struct bch_control *bch, uint8_t *dst, 1874c6de856SChristian Hitz const uint32_t *src) 1884c6de856SChristian Hitz { 1894c6de856SChristian Hitz uint8_t pad[4]; 1904c6de856SChristian Hitz unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; 1914c6de856SChristian Hitz 1924c6de856SChristian Hitz for (i = 0; i < nwords; i++) { 1934c6de856SChristian Hitz *dst++ = (src[i] >> 24); 1944c6de856SChristian Hitz *dst++ = (src[i] >> 16) & 0xff; 1954c6de856SChristian Hitz *dst++ = (src[i] >> 8) & 0xff; 1964c6de856SChristian Hitz *dst++ = (src[i] >> 0) & 0xff; 1974c6de856SChristian Hitz } 1984c6de856SChristian Hitz pad[0] = (src[nwords] >> 24); 1994c6de856SChristian Hitz pad[1] = (src[nwords] >> 16) & 0xff; 2004c6de856SChristian Hitz pad[2] = (src[nwords] >> 8) & 0xff; 2014c6de856SChristian Hitz pad[3] = (src[nwords] >> 0) & 0xff; 2024c6de856SChristian Hitz memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); 2034c6de856SChristian Hitz } 2044c6de856SChristian Hitz 2054c6de856SChristian Hitz /** 2064c6de856SChristian Hitz * encode_bch - calculate BCH ecc parity of data 2074c6de856SChristian Hitz * @bch: BCH control structure 2084c6de856SChristian Hitz * @data: data to encode 2094c6de856SChristian Hitz * @len: data length in bytes 2104c6de856SChristian Hitz * @ecc: ecc parity data, must be initialized by caller 2114c6de856SChristian Hitz * 2124c6de856SChristian Hitz * The @ecc parity array is used both as input and output parameter, in order to 2134c6de856SChristian Hitz * allow incremental computations. It should be of the size indicated by member 2144c6de856SChristian Hitz * @ecc_bytes of @bch, and should be initialized to 0 before the first call. 2154c6de856SChristian Hitz * 2164c6de856SChristian Hitz * The exact number of computed ecc parity bits is given by member @ecc_bits of 2174c6de856SChristian Hitz * @bch; it may be less than m*t for large values of t. 2184c6de856SChristian Hitz */ 2194c6de856SChristian Hitz void encode_bch(struct bch_control *bch, const uint8_t *data, 2204c6de856SChristian Hitz unsigned int len, uint8_t *ecc) 2214c6de856SChristian Hitz { 2224c6de856SChristian Hitz const unsigned int l = BCH_ECC_WORDS(bch)-1; 2234c6de856SChristian Hitz unsigned int i, mlen; 2244c6de856SChristian Hitz unsigned long m; 2254c6de856SChristian Hitz uint32_t w, r[l+1]; 2264c6de856SChristian Hitz const uint32_t * const tab0 = bch->mod8_tab; 2274c6de856SChristian Hitz const uint32_t * const tab1 = tab0 + 256*(l+1); 2284c6de856SChristian Hitz const uint32_t * const tab2 = tab1 + 256*(l+1); 2294c6de856SChristian Hitz const uint32_t * const tab3 = tab2 + 256*(l+1); 2304c6de856SChristian Hitz const uint32_t *pdata, *p0, *p1, *p2, *p3; 2314c6de856SChristian Hitz 2324c6de856SChristian Hitz if (ecc) { 2334c6de856SChristian Hitz /* load ecc parity bytes into internal 32-bit buffer */ 2344c6de856SChristian Hitz load_ecc8(bch, bch->ecc_buf, ecc); 2354c6de856SChristian Hitz } else { 2364c6de856SChristian Hitz memset(bch->ecc_buf, 0, sizeof(r)); 2374c6de856SChristian Hitz } 2384c6de856SChristian Hitz 2394c6de856SChristian Hitz /* process first unaligned data bytes */ 2404c6de856SChristian Hitz m = ((unsigned long)data) & 3; 2414c6de856SChristian Hitz if (m) { 2424c6de856SChristian Hitz mlen = (len < (4-m)) ? len : 4-m; 2434c6de856SChristian Hitz encode_bch_unaligned(bch, data, mlen, bch->ecc_buf); 2444c6de856SChristian Hitz data += mlen; 2454c6de856SChristian Hitz len -= mlen; 2464c6de856SChristian Hitz } 2474c6de856SChristian Hitz 2484c6de856SChristian Hitz /* process 32-bit aligned data words */ 2494c6de856SChristian Hitz pdata = (uint32_t *)data; 2504c6de856SChristian Hitz mlen = len/4; 2514c6de856SChristian Hitz data += 4*mlen; 2524c6de856SChristian Hitz len -= 4*mlen; 2534c6de856SChristian Hitz memcpy(r, bch->ecc_buf, sizeof(r)); 2544c6de856SChristian Hitz 2554c6de856SChristian Hitz /* 2564c6de856SChristian Hitz * split each 32-bit word into 4 polynomials of weight 8 as follows: 2574c6de856SChristian Hitz * 2584c6de856SChristian Hitz * 31 ...24 23 ...16 15 ... 8 7 ... 0 2594c6de856SChristian Hitz * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt 2604c6de856SChristian Hitz * tttttttt mod g = r0 (precomputed) 2614c6de856SChristian Hitz * zzzzzzzz 00000000 mod g = r1 (precomputed) 2624c6de856SChristian Hitz * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) 2634c6de856SChristian Hitz * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) 2644c6de856SChristian Hitz * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 2654c6de856SChristian Hitz */ 2664c6de856SChristian Hitz while (mlen--) { 2674c6de856SChristian Hitz /* input data is read in big-endian format */ 2684c6de856SChristian Hitz w = r[0]^cpu_to_be32(*pdata++); 2694c6de856SChristian Hitz p0 = tab0 + (l+1)*((w >> 0) & 0xff); 2704c6de856SChristian Hitz p1 = tab1 + (l+1)*((w >> 8) & 0xff); 2714c6de856SChristian Hitz p2 = tab2 + (l+1)*((w >> 16) & 0xff); 2724c6de856SChristian Hitz p3 = tab3 + (l+1)*((w >> 24) & 0xff); 2734c6de856SChristian Hitz 2744c6de856SChristian Hitz for (i = 0; i < l; i++) 2754c6de856SChristian Hitz r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; 2764c6de856SChristian Hitz 2774c6de856SChristian Hitz r[l] = p0[l]^p1[l]^p2[l]^p3[l]; 2784c6de856SChristian Hitz } 2794c6de856SChristian Hitz memcpy(bch->ecc_buf, r, sizeof(r)); 2804c6de856SChristian Hitz 2814c6de856SChristian Hitz /* process last unaligned bytes */ 2824c6de856SChristian Hitz if (len) 2834c6de856SChristian Hitz encode_bch_unaligned(bch, data, len, bch->ecc_buf); 2844c6de856SChristian Hitz 2854c6de856SChristian Hitz /* store ecc parity bytes into original parity buffer */ 2864c6de856SChristian Hitz if (ecc) 2874c6de856SChristian Hitz store_ecc8(bch, ecc, bch->ecc_buf); 2884c6de856SChristian Hitz } 2894c6de856SChristian Hitz 2904c6de856SChristian Hitz static inline int modulo(struct bch_control *bch, unsigned int v) 2914c6de856SChristian Hitz { 2924c6de856SChristian Hitz const unsigned int n = GF_N(bch); 2934c6de856SChristian Hitz while (v >= n) { 2944c6de856SChristian Hitz v -= n; 2954c6de856SChristian Hitz v = (v & n) + (v >> GF_M(bch)); 2964c6de856SChristian Hitz } 2974c6de856SChristian Hitz return v; 2984c6de856SChristian Hitz } 2994c6de856SChristian Hitz 3004c6de856SChristian Hitz /* 3014c6de856SChristian Hitz * shorter and faster modulo function, only works when v < 2N. 3024c6de856SChristian Hitz */ 3034c6de856SChristian Hitz static inline int mod_s(struct bch_control *bch, unsigned int v) 3044c6de856SChristian Hitz { 3054c6de856SChristian Hitz const unsigned int n = GF_N(bch); 3064c6de856SChristian Hitz return (v < n) ? v : v-n; 3074c6de856SChristian Hitz } 3084c6de856SChristian Hitz 3094c6de856SChristian Hitz static inline int deg(unsigned int poly) 3104c6de856SChristian Hitz { 3114c6de856SChristian Hitz /* polynomial degree is the most-significant bit index */ 3124c6de856SChristian Hitz return fls(poly)-1; 3134c6de856SChristian Hitz } 3144c6de856SChristian Hitz 3154c6de856SChristian Hitz static inline int parity(unsigned int x) 3164c6de856SChristian Hitz { 3174c6de856SChristian Hitz /* 3184c6de856SChristian Hitz * public domain code snippet, lifted from 3194c6de856SChristian Hitz * http://www-graphics.stanford.edu/~seander/bithacks.html 3204c6de856SChristian Hitz */ 3214c6de856SChristian Hitz x ^= x >> 1; 3224c6de856SChristian Hitz x ^= x >> 2; 3234c6de856SChristian Hitz x = (x & 0x11111111U) * 0x11111111U; 3244c6de856SChristian Hitz return (x >> 28) & 1; 3254c6de856SChristian Hitz } 3264c6de856SChristian Hitz 3274c6de856SChristian Hitz /* Galois field basic operations: multiply, divide, inverse, etc. */ 3284c6de856SChristian Hitz 3294c6de856SChristian Hitz static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, 3304c6de856SChristian Hitz unsigned int b) 3314c6de856SChristian Hitz { 3324c6de856SChristian Hitz return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ 3334c6de856SChristian Hitz bch->a_log_tab[b])] : 0; 3344c6de856SChristian Hitz } 3354c6de856SChristian Hitz 3364c6de856SChristian Hitz static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) 3374c6de856SChristian Hitz { 3384c6de856SChristian Hitz return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; 3394c6de856SChristian Hitz } 3404c6de856SChristian Hitz 3414c6de856SChristian Hitz static inline unsigned int gf_div(struct bch_control *bch, unsigned int a, 3424c6de856SChristian Hitz unsigned int b) 3434c6de856SChristian Hitz { 3444c6de856SChristian Hitz return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ 3454c6de856SChristian Hitz GF_N(bch)-bch->a_log_tab[b])] : 0; 3464c6de856SChristian Hitz } 3474c6de856SChristian Hitz 3484c6de856SChristian Hitz static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a) 3494c6de856SChristian Hitz { 3504c6de856SChristian Hitz return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]]; 3514c6de856SChristian Hitz } 3524c6de856SChristian Hitz 3534c6de856SChristian Hitz static inline unsigned int a_pow(struct bch_control *bch, int i) 3544c6de856SChristian Hitz { 3554c6de856SChristian Hitz return bch->a_pow_tab[modulo(bch, i)]; 3564c6de856SChristian Hitz } 3574c6de856SChristian Hitz 3584c6de856SChristian Hitz static inline int a_log(struct bch_control *bch, unsigned int x) 3594c6de856SChristian Hitz { 3604c6de856SChristian Hitz return bch->a_log_tab[x]; 3614c6de856SChristian Hitz } 3624c6de856SChristian Hitz 3634c6de856SChristian Hitz static inline int a_ilog(struct bch_control *bch, unsigned int x) 3644c6de856SChristian Hitz { 3654c6de856SChristian Hitz return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]); 3664c6de856SChristian Hitz } 3674c6de856SChristian Hitz 3684c6de856SChristian Hitz /* 3694c6de856SChristian Hitz * compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t 3704c6de856SChristian Hitz */ 3714c6de856SChristian Hitz static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, 3724c6de856SChristian Hitz unsigned int *syn) 3734c6de856SChristian Hitz { 3744c6de856SChristian Hitz int i, j, s; 3754c6de856SChristian Hitz unsigned int m; 3764c6de856SChristian Hitz uint32_t poly; 3774c6de856SChristian Hitz const int t = GF_T(bch); 3784c6de856SChristian Hitz 3794c6de856SChristian Hitz s = bch->ecc_bits; 3804c6de856SChristian Hitz 3814c6de856SChristian Hitz /* make sure extra bits in last ecc word are cleared */ 3824c6de856SChristian Hitz m = ((unsigned int)s) & 31; 3834c6de856SChristian Hitz if (m) 3844c6de856SChristian Hitz ecc[s/32] &= ~((1u << (32-m))-1); 3854c6de856SChristian Hitz memset(syn, 0, 2*t*sizeof(*syn)); 3864c6de856SChristian Hitz 3874c6de856SChristian Hitz /* compute v(a^j) for j=1 .. 2t-1 */ 3884c6de856SChristian Hitz do { 3894c6de856SChristian Hitz poly = *ecc++; 3904c6de856SChristian Hitz s -= 32; 3914c6de856SChristian Hitz while (poly) { 3924c6de856SChristian Hitz i = deg(poly); 3934c6de856SChristian Hitz for (j = 0; j < 2*t; j += 2) 3944c6de856SChristian Hitz syn[j] ^= a_pow(bch, (j+1)*(i+s)); 3954c6de856SChristian Hitz 3964c6de856SChristian Hitz poly ^= (1 << i); 3974c6de856SChristian Hitz } 3984c6de856SChristian Hitz } while (s > 0); 3994c6de856SChristian Hitz 4004c6de856SChristian Hitz /* v(a^(2j)) = v(a^j)^2 */ 4014c6de856SChristian Hitz for (j = 0; j < t; j++) 4024c6de856SChristian Hitz syn[2*j+1] = gf_sqr(bch, syn[j]); 4034c6de856SChristian Hitz } 4044c6de856SChristian Hitz 4054c6de856SChristian Hitz static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src) 4064c6de856SChristian Hitz { 4074c6de856SChristian Hitz memcpy(dst, src, GF_POLY_SZ(src->deg)); 4084c6de856SChristian Hitz } 4094c6de856SChristian Hitz 4104c6de856SChristian Hitz static int compute_error_locator_polynomial(struct bch_control *bch, 4114c6de856SChristian Hitz const unsigned int *syn) 4124c6de856SChristian Hitz { 4134c6de856SChristian Hitz const unsigned int t = GF_T(bch); 4144c6de856SChristian Hitz const unsigned int n = GF_N(bch); 4154c6de856SChristian Hitz unsigned int i, j, tmp, l, pd = 1, d = syn[0]; 4164c6de856SChristian Hitz struct gf_poly *elp = bch->elp; 4174c6de856SChristian Hitz struct gf_poly *pelp = bch->poly_2t[0]; 4184c6de856SChristian Hitz struct gf_poly *elp_copy = bch->poly_2t[1]; 4194c6de856SChristian Hitz int k, pp = -1; 4204c6de856SChristian Hitz 4214c6de856SChristian Hitz memset(pelp, 0, GF_POLY_SZ(2*t)); 4224c6de856SChristian Hitz memset(elp, 0, GF_POLY_SZ(2*t)); 4234c6de856SChristian Hitz 4244c6de856SChristian Hitz pelp->deg = 0; 4254c6de856SChristian Hitz pelp->c[0] = 1; 4264c6de856SChristian Hitz elp->deg = 0; 4274c6de856SChristian Hitz elp->c[0] = 1; 4284c6de856SChristian Hitz 4294c6de856SChristian Hitz /* use simplified binary Berlekamp-Massey algorithm */ 4304c6de856SChristian Hitz for (i = 0; (i < t) && (elp->deg <= t); i++) { 4314c6de856SChristian Hitz if (d) { 4324c6de856SChristian Hitz k = 2*i-pp; 4334c6de856SChristian Hitz gf_poly_copy(elp_copy, elp); 4344c6de856SChristian Hitz /* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */ 4354c6de856SChristian Hitz tmp = a_log(bch, d)+n-a_log(bch, pd); 4364c6de856SChristian Hitz for (j = 0; j <= pelp->deg; j++) { 4374c6de856SChristian Hitz if (pelp->c[j]) { 4384c6de856SChristian Hitz l = a_log(bch, pelp->c[j]); 4394c6de856SChristian Hitz elp->c[j+k] ^= a_pow(bch, tmp+l); 4404c6de856SChristian Hitz } 4414c6de856SChristian Hitz } 4424c6de856SChristian Hitz /* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */ 4434c6de856SChristian Hitz tmp = pelp->deg+k; 4444c6de856SChristian Hitz if (tmp > elp->deg) { 4454c6de856SChristian Hitz elp->deg = tmp; 4464c6de856SChristian Hitz gf_poly_copy(pelp, elp_copy); 4474c6de856SChristian Hitz pd = d; 4484c6de856SChristian Hitz pp = 2*i; 4494c6de856SChristian Hitz } 4504c6de856SChristian Hitz } 4514c6de856SChristian Hitz /* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */ 4524c6de856SChristian Hitz if (i < t-1) { 4534c6de856SChristian Hitz d = syn[2*i+2]; 4544c6de856SChristian Hitz for (j = 1; j <= elp->deg; j++) 4554c6de856SChristian Hitz d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]); 4564c6de856SChristian Hitz } 4574c6de856SChristian Hitz } 4584c6de856SChristian Hitz dbg("elp=%s\n", gf_poly_str(elp)); 4594c6de856SChristian Hitz return (elp->deg > t) ? -1 : (int)elp->deg; 4604c6de856SChristian Hitz } 4614c6de856SChristian Hitz 4624c6de856SChristian Hitz /* 4634c6de856SChristian Hitz * solve a m x m linear system in GF(2) with an expected number of solutions, 4644c6de856SChristian Hitz * and return the number of found solutions 4654c6de856SChristian Hitz */ 4664c6de856SChristian Hitz static int solve_linear_system(struct bch_control *bch, unsigned int *rows, 4674c6de856SChristian Hitz unsigned int *sol, int nsol) 4684c6de856SChristian Hitz { 4694c6de856SChristian Hitz const int m = GF_M(bch); 4704c6de856SChristian Hitz unsigned int tmp, mask; 4714c6de856SChristian Hitz int rem, c, r, p, k, param[m]; 4724c6de856SChristian Hitz 4734c6de856SChristian Hitz k = 0; 4744c6de856SChristian Hitz mask = 1 << m; 4754c6de856SChristian Hitz 4764c6de856SChristian Hitz /* Gaussian elimination */ 4774c6de856SChristian Hitz for (c = 0; c < m; c++) { 4784c6de856SChristian Hitz rem = 0; 4794c6de856SChristian Hitz p = c-k; 4804c6de856SChristian Hitz /* find suitable row for elimination */ 4814c6de856SChristian Hitz for (r = p; r < m; r++) { 4824c6de856SChristian Hitz if (rows[r] & mask) { 4834c6de856SChristian Hitz if (r != p) { 4844c6de856SChristian Hitz tmp = rows[r]; 4854c6de856SChristian Hitz rows[r] = rows[p]; 4864c6de856SChristian Hitz rows[p] = tmp; 4874c6de856SChristian Hitz } 4884c6de856SChristian Hitz rem = r+1; 4894c6de856SChristian Hitz break; 4904c6de856SChristian Hitz } 4914c6de856SChristian Hitz } 4924c6de856SChristian Hitz if (rem) { 4934c6de856SChristian Hitz /* perform elimination on remaining rows */ 4944c6de856SChristian Hitz tmp = rows[p]; 4954c6de856SChristian Hitz for (r = rem; r < m; r++) { 4964c6de856SChristian Hitz if (rows[r] & mask) 4974c6de856SChristian Hitz rows[r] ^= tmp; 4984c6de856SChristian Hitz } 4994c6de856SChristian Hitz } else { 5004c6de856SChristian Hitz /* elimination not needed, store defective row index */ 5014c6de856SChristian Hitz param[k++] = c; 5024c6de856SChristian Hitz } 5034c6de856SChristian Hitz mask >>= 1; 5044c6de856SChristian Hitz } 5054c6de856SChristian Hitz /* rewrite system, inserting fake parameter rows */ 5064c6de856SChristian Hitz if (k > 0) { 5074c6de856SChristian Hitz p = k; 5084c6de856SChristian Hitz for (r = m-1; r >= 0; r--) { 5094c6de856SChristian Hitz if ((r > m-1-k) && rows[r]) 5104c6de856SChristian Hitz /* system has no solution */ 5114c6de856SChristian Hitz return 0; 5124c6de856SChristian Hitz 5134c6de856SChristian Hitz rows[r] = (p && (r == param[p-1])) ? 5144c6de856SChristian Hitz p--, 1u << (m-r) : rows[r-p]; 5154c6de856SChristian Hitz } 5164c6de856SChristian Hitz } 5174c6de856SChristian Hitz 5184c6de856SChristian Hitz if (nsol != (1 << k)) 5194c6de856SChristian Hitz /* unexpected number of solutions */ 5204c6de856SChristian Hitz return 0; 5214c6de856SChristian Hitz 5224c6de856SChristian Hitz for (p = 0; p < nsol; p++) { 5234c6de856SChristian Hitz /* set parameters for p-th solution */ 5244c6de856SChristian Hitz for (c = 0; c < k; c++) 5254c6de856SChristian Hitz rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1); 5264c6de856SChristian Hitz 5274c6de856SChristian Hitz /* compute unique solution */ 5284c6de856SChristian Hitz tmp = 0; 5294c6de856SChristian Hitz for (r = m-1; r >= 0; r--) { 5304c6de856SChristian Hitz mask = rows[r] & (tmp|1); 5314c6de856SChristian Hitz tmp |= parity(mask) << (m-r); 5324c6de856SChristian Hitz } 5334c6de856SChristian Hitz sol[p] = tmp >> 1; 5344c6de856SChristian Hitz } 5354c6de856SChristian Hitz return nsol; 5364c6de856SChristian Hitz } 5374c6de856SChristian Hitz 5384c6de856SChristian Hitz /* 5394c6de856SChristian Hitz * this function builds and solves a linear system for finding roots of a degree 5404c6de856SChristian Hitz * 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m). 5414c6de856SChristian Hitz */ 5424c6de856SChristian Hitz static int find_affine4_roots(struct bch_control *bch, unsigned int a, 5434c6de856SChristian Hitz unsigned int b, unsigned int c, 5444c6de856SChristian Hitz unsigned int *roots) 5454c6de856SChristian Hitz { 5464c6de856SChristian Hitz int i, j, k; 5474c6de856SChristian Hitz const int m = GF_M(bch); 5484c6de856SChristian Hitz unsigned int mask = 0xff, t, rows[16] = {0,}; 5494c6de856SChristian Hitz 5504c6de856SChristian Hitz j = a_log(bch, b); 5514c6de856SChristian Hitz k = a_log(bch, a); 5524c6de856SChristian Hitz rows[0] = c; 5534c6de856SChristian Hitz 5544c6de856SChristian Hitz /* buid linear system to solve X^4+aX^2+bX+c = 0 */ 5554c6de856SChristian Hitz for (i = 0; i < m; i++) { 5564c6de856SChristian Hitz rows[i+1] = bch->a_pow_tab[4*i]^ 5574c6de856SChristian Hitz (a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^ 5584c6de856SChristian Hitz (b ? bch->a_pow_tab[mod_s(bch, j)] : 0); 5594c6de856SChristian Hitz j++; 5604c6de856SChristian Hitz k += 2; 5614c6de856SChristian Hitz } 5624c6de856SChristian Hitz /* 5634c6de856SChristian Hitz * transpose 16x16 matrix before passing it to linear solver 5644c6de856SChristian Hitz * warning: this code assumes m < 16 5654c6de856SChristian Hitz */ 5664c6de856SChristian Hitz for (j = 8; j != 0; j >>= 1, mask ^= (mask << j)) { 5674c6de856SChristian Hitz for (k = 0; k < 16; k = (k+j+1) & ~j) { 5684c6de856SChristian Hitz t = ((rows[k] >> j)^rows[k+j]) & mask; 5694c6de856SChristian Hitz rows[k] ^= (t << j); 5704c6de856SChristian Hitz rows[k+j] ^= t; 5714c6de856SChristian Hitz } 5724c6de856SChristian Hitz } 5734c6de856SChristian Hitz return solve_linear_system(bch, rows, roots, 4); 5744c6de856SChristian Hitz } 5754c6de856SChristian Hitz 5764c6de856SChristian Hitz /* 5774c6de856SChristian Hitz * compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r)) 5784c6de856SChristian Hitz */ 5794c6de856SChristian Hitz static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly, 5804c6de856SChristian Hitz unsigned int *roots) 5814c6de856SChristian Hitz { 5824c6de856SChristian Hitz int n = 0; 5834c6de856SChristian Hitz 5844c6de856SChristian Hitz if (poly->c[0]) 5854c6de856SChristian Hitz /* poly[X] = bX+c with c!=0, root=c/b */ 5864c6de856SChristian Hitz roots[n++] = mod_s(bch, GF_N(bch)-bch->a_log_tab[poly->c[0]]+ 5874c6de856SChristian Hitz bch->a_log_tab[poly->c[1]]); 5884c6de856SChristian Hitz return n; 5894c6de856SChristian Hitz } 5904c6de856SChristian Hitz 5914c6de856SChristian Hitz /* 5924c6de856SChristian Hitz * compute roots of a degree 2 polynomial over GF(2^m) 5934c6de856SChristian Hitz */ 5944c6de856SChristian Hitz static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, 5954c6de856SChristian Hitz unsigned int *roots) 5964c6de856SChristian Hitz { 5974c6de856SChristian Hitz int n = 0, i, l0, l1, l2; 5984c6de856SChristian Hitz unsigned int u, v, r; 5994c6de856SChristian Hitz 6004c6de856SChristian Hitz if (poly->c[0] && poly->c[1]) { 6014c6de856SChristian Hitz 6024c6de856SChristian Hitz l0 = bch->a_log_tab[poly->c[0]]; 6034c6de856SChristian Hitz l1 = bch->a_log_tab[poly->c[1]]; 6044c6de856SChristian Hitz l2 = bch->a_log_tab[poly->c[2]]; 6054c6de856SChristian Hitz 6064c6de856SChristian Hitz /* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */ 6074c6de856SChristian Hitz u = a_pow(bch, l0+l2+2*(GF_N(bch)-l1)); 6084c6de856SChristian Hitz /* 6094c6de856SChristian Hitz * let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi): 6104c6de856SChristian Hitz * r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) = 6114c6de856SChristian Hitz * u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u) 6124c6de856SChristian Hitz * i.e. r and r+1 are roots iff Tr(u)=0 6134c6de856SChristian Hitz */ 6144c6de856SChristian Hitz r = 0; 6154c6de856SChristian Hitz v = u; 6164c6de856SChristian Hitz while (v) { 6174c6de856SChristian Hitz i = deg(v); 6184c6de856SChristian Hitz r ^= bch->xi_tab[i]; 6194c6de856SChristian Hitz v ^= (1 << i); 6204c6de856SChristian Hitz } 6214c6de856SChristian Hitz /* verify root */ 6224c6de856SChristian Hitz if ((gf_sqr(bch, r)^r) == u) { 6234c6de856SChristian Hitz /* reverse z=a/bX transformation and compute log(1/r) */ 6244c6de856SChristian Hitz roots[n++] = modulo(bch, 2*GF_N(bch)-l1- 6254c6de856SChristian Hitz bch->a_log_tab[r]+l2); 6264c6de856SChristian Hitz roots[n++] = modulo(bch, 2*GF_N(bch)-l1- 6274c6de856SChristian Hitz bch->a_log_tab[r^1]+l2); 6284c6de856SChristian Hitz } 6294c6de856SChristian Hitz } 6304c6de856SChristian Hitz return n; 6314c6de856SChristian Hitz } 6324c6de856SChristian Hitz 6334c6de856SChristian Hitz /* 6344c6de856SChristian Hitz * compute roots of a degree 3 polynomial over GF(2^m) 6354c6de856SChristian Hitz */ 6364c6de856SChristian Hitz static int find_poly_deg3_roots(struct bch_control *bch, struct gf_poly *poly, 6374c6de856SChristian Hitz unsigned int *roots) 6384c6de856SChristian Hitz { 6394c6de856SChristian Hitz int i, n = 0; 6404c6de856SChristian Hitz unsigned int a, b, c, a2, b2, c2, e3, tmp[4]; 6414c6de856SChristian Hitz 6424c6de856SChristian Hitz if (poly->c[0]) { 6434c6de856SChristian Hitz /* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */ 6444c6de856SChristian Hitz e3 = poly->c[3]; 6454c6de856SChristian Hitz c2 = gf_div(bch, poly->c[0], e3); 6464c6de856SChristian Hitz b2 = gf_div(bch, poly->c[1], e3); 6474c6de856SChristian Hitz a2 = gf_div(bch, poly->c[2], e3); 6484c6de856SChristian Hitz 6494c6de856SChristian Hitz /* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */ 6504c6de856SChristian Hitz c = gf_mul(bch, a2, c2); /* c = a2c2 */ 6514c6de856SChristian Hitz b = gf_mul(bch, a2, b2)^c2; /* b = a2b2 + c2 */ 6524c6de856SChristian Hitz a = gf_sqr(bch, a2)^b2; /* a = a2^2 + b2 */ 6534c6de856SChristian Hitz 6544c6de856SChristian Hitz /* find the 4 roots of this affine polynomial */ 6554c6de856SChristian Hitz if (find_affine4_roots(bch, a, b, c, tmp) == 4) { 6564c6de856SChristian Hitz /* remove a2 from final list of roots */ 6574c6de856SChristian Hitz for (i = 0; i < 4; i++) { 6584c6de856SChristian Hitz if (tmp[i] != a2) 6594c6de856SChristian Hitz roots[n++] = a_ilog(bch, tmp[i]); 6604c6de856SChristian Hitz } 6614c6de856SChristian Hitz } 6624c6de856SChristian Hitz } 6634c6de856SChristian Hitz return n; 6644c6de856SChristian Hitz } 6654c6de856SChristian Hitz 6664c6de856SChristian Hitz /* 6674c6de856SChristian Hitz * compute roots of a degree 4 polynomial over GF(2^m) 6684c6de856SChristian Hitz */ 6694c6de856SChristian Hitz static int find_poly_deg4_roots(struct bch_control *bch, struct gf_poly *poly, 6704c6de856SChristian Hitz unsigned int *roots) 6714c6de856SChristian Hitz { 6724c6de856SChristian Hitz int i, l, n = 0; 6734c6de856SChristian Hitz unsigned int a, b, c, d, e = 0, f, a2, b2, c2, e4; 6744c6de856SChristian Hitz 6754c6de856SChristian Hitz if (poly->c[0] == 0) 6764c6de856SChristian Hitz return 0; 6774c6de856SChristian Hitz 6784c6de856SChristian Hitz /* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */ 6794c6de856SChristian Hitz e4 = poly->c[4]; 6804c6de856SChristian Hitz d = gf_div(bch, poly->c[0], e4); 6814c6de856SChristian Hitz c = gf_div(bch, poly->c[1], e4); 6824c6de856SChristian Hitz b = gf_div(bch, poly->c[2], e4); 6834c6de856SChristian Hitz a = gf_div(bch, poly->c[3], e4); 6844c6de856SChristian Hitz 6854c6de856SChristian Hitz /* use Y=1/X transformation to get an affine polynomial */ 6864c6de856SChristian Hitz if (a) { 6874c6de856SChristian Hitz /* first, eliminate cX by using z=X+e with ae^2+c=0 */ 6884c6de856SChristian Hitz if (c) { 6894c6de856SChristian Hitz /* compute e such that e^2 = c/a */ 6904c6de856SChristian Hitz f = gf_div(bch, c, a); 6914c6de856SChristian Hitz l = a_log(bch, f); 6924c6de856SChristian Hitz l += (l & 1) ? GF_N(bch) : 0; 6934c6de856SChristian Hitz e = a_pow(bch, l/2); 6944c6de856SChristian Hitz /* 6954c6de856SChristian Hitz * use transformation z=X+e: 6964c6de856SChristian Hitz * z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d 6974c6de856SChristian Hitz * z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d 6984c6de856SChristian Hitz * z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d 6994c6de856SChristian Hitz * z^4 + az^3 + b'z^2 + d' 7004c6de856SChristian Hitz */ 7014c6de856SChristian Hitz d = a_pow(bch, 2*l)^gf_mul(bch, b, f)^d; 7024c6de856SChristian Hitz b = gf_mul(bch, a, e)^b; 7034c6de856SChristian Hitz } 7044c6de856SChristian Hitz /* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */ 7054c6de856SChristian Hitz if (d == 0) 7064c6de856SChristian Hitz /* assume all roots have multiplicity 1 */ 7074c6de856SChristian Hitz return 0; 7084c6de856SChristian Hitz 7094c6de856SChristian Hitz c2 = gf_inv(bch, d); 7104c6de856SChristian Hitz b2 = gf_div(bch, a, d); 7114c6de856SChristian Hitz a2 = gf_div(bch, b, d); 7124c6de856SChristian Hitz } else { 7134c6de856SChristian Hitz /* polynomial is already affine */ 7144c6de856SChristian Hitz c2 = d; 7154c6de856SChristian Hitz b2 = c; 7164c6de856SChristian Hitz a2 = b; 7174c6de856SChristian Hitz } 7184c6de856SChristian Hitz /* find the 4 roots of this affine polynomial */ 7194c6de856SChristian Hitz if (find_affine4_roots(bch, a2, b2, c2, roots) == 4) { 7204c6de856SChristian Hitz for (i = 0; i < 4; i++) { 7214c6de856SChristian Hitz /* post-process roots (reverse transformations) */ 7224c6de856SChristian Hitz f = a ? gf_inv(bch, roots[i]) : roots[i]; 7234c6de856SChristian Hitz roots[i] = a_ilog(bch, f^e); 7244c6de856SChristian Hitz } 7254c6de856SChristian Hitz n = 4; 7264c6de856SChristian Hitz } 7274c6de856SChristian Hitz return n; 7284c6de856SChristian Hitz } 7294c6de856SChristian Hitz 7304c6de856SChristian Hitz /* 7314c6de856SChristian Hitz * build monic, log-based representation of a polynomial 7324c6de856SChristian Hitz */ 7334c6de856SChristian Hitz static void gf_poly_logrep(struct bch_control *bch, 7344c6de856SChristian Hitz const struct gf_poly *a, int *rep) 7354c6de856SChristian Hitz { 7364c6de856SChristian Hitz int i, d = a->deg, l = GF_N(bch)-a_log(bch, a->c[a->deg]); 7374c6de856SChristian Hitz 7384c6de856SChristian Hitz /* represent 0 values with -1; warning, rep[d] is not set to 1 */ 7394c6de856SChristian Hitz for (i = 0; i < d; i++) 7404c6de856SChristian Hitz rep[i] = a->c[i] ? mod_s(bch, a_log(bch, a->c[i])+l) : -1; 7414c6de856SChristian Hitz } 7424c6de856SChristian Hitz 7434c6de856SChristian Hitz /* 7444c6de856SChristian Hitz * compute polynomial Euclidean division remainder in GF(2^m)[X] 7454c6de856SChristian Hitz */ 7464c6de856SChristian Hitz static void gf_poly_mod(struct bch_control *bch, struct gf_poly *a, 7474c6de856SChristian Hitz const struct gf_poly *b, int *rep) 7484c6de856SChristian Hitz { 7494c6de856SChristian Hitz int la, p, m; 7504c6de856SChristian Hitz unsigned int i, j, *c = a->c; 7514c6de856SChristian Hitz const unsigned int d = b->deg; 7524c6de856SChristian Hitz 7534c6de856SChristian Hitz if (a->deg < d) 7544c6de856SChristian Hitz return; 7554c6de856SChristian Hitz 7564c6de856SChristian Hitz /* reuse or compute log representation of denominator */ 7574c6de856SChristian Hitz if (!rep) { 7584c6de856SChristian Hitz rep = bch->cache; 7594c6de856SChristian Hitz gf_poly_logrep(bch, b, rep); 7604c6de856SChristian Hitz } 7614c6de856SChristian Hitz 7624c6de856SChristian Hitz for (j = a->deg; j >= d; j--) { 7634c6de856SChristian Hitz if (c[j]) { 7644c6de856SChristian Hitz la = a_log(bch, c[j]); 7654c6de856SChristian Hitz p = j-d; 7664c6de856SChristian Hitz for (i = 0; i < d; i++, p++) { 7674c6de856SChristian Hitz m = rep[i]; 7684c6de856SChristian Hitz if (m >= 0) 7694c6de856SChristian Hitz c[p] ^= bch->a_pow_tab[mod_s(bch, 7704c6de856SChristian Hitz m+la)]; 7714c6de856SChristian Hitz } 7724c6de856SChristian Hitz } 7734c6de856SChristian Hitz } 7744c6de856SChristian Hitz a->deg = d-1; 7754c6de856SChristian Hitz while (!c[a->deg] && a->deg) 7764c6de856SChristian Hitz a->deg--; 7774c6de856SChristian Hitz } 7784c6de856SChristian Hitz 7794c6de856SChristian Hitz /* 7804c6de856SChristian Hitz * compute polynomial Euclidean division quotient in GF(2^m)[X] 7814c6de856SChristian Hitz */ 7824c6de856SChristian Hitz static void gf_poly_div(struct bch_control *bch, struct gf_poly *a, 7834c6de856SChristian Hitz const struct gf_poly *b, struct gf_poly *q) 7844c6de856SChristian Hitz { 7854c6de856SChristian Hitz if (a->deg >= b->deg) { 7864c6de856SChristian Hitz q->deg = a->deg-b->deg; 7874c6de856SChristian Hitz /* compute a mod b (modifies a) */ 7884c6de856SChristian Hitz gf_poly_mod(bch, a, b, NULL); 7894c6de856SChristian Hitz /* quotient is stored in upper part of polynomial a */ 7904c6de856SChristian Hitz memcpy(q->c, &a->c[b->deg], (1+q->deg)*sizeof(unsigned int)); 7914c6de856SChristian Hitz } else { 7924c6de856SChristian Hitz q->deg = 0; 7934c6de856SChristian Hitz q->c[0] = 0; 7944c6de856SChristian Hitz } 7954c6de856SChristian Hitz } 7964c6de856SChristian Hitz 7974c6de856SChristian Hitz /* 7984c6de856SChristian Hitz * compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X] 7994c6de856SChristian Hitz */ 8004c6de856SChristian Hitz static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a, 8014c6de856SChristian Hitz struct gf_poly *b) 8024c6de856SChristian Hitz { 8034c6de856SChristian Hitz struct gf_poly *tmp; 8044c6de856SChristian Hitz 8054c6de856SChristian Hitz dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b)); 8064c6de856SChristian Hitz 8074c6de856SChristian Hitz if (a->deg < b->deg) { 8084c6de856SChristian Hitz tmp = b; 8094c6de856SChristian Hitz b = a; 8104c6de856SChristian Hitz a = tmp; 8114c6de856SChristian Hitz } 8124c6de856SChristian Hitz 8134c6de856SChristian Hitz while (b->deg > 0) { 8144c6de856SChristian Hitz gf_poly_mod(bch, a, b, NULL); 8154c6de856SChristian Hitz tmp = b; 8164c6de856SChristian Hitz b = a; 8174c6de856SChristian Hitz a = tmp; 8184c6de856SChristian Hitz } 8194c6de856SChristian Hitz 8204c6de856SChristian Hitz dbg("%s\n", gf_poly_str(a)); 8214c6de856SChristian Hitz 8224c6de856SChristian Hitz return a; 8234c6de856SChristian Hitz } 8244c6de856SChristian Hitz 8254c6de856SChristian Hitz /* 8264c6de856SChristian Hitz * Given a polynomial f and an integer k, compute Tr(a^kX) mod f 8274c6de856SChristian Hitz * This is used in Berlekamp Trace algorithm for splitting polynomials 8284c6de856SChristian Hitz */ 8294c6de856SChristian Hitz static void compute_trace_bk_mod(struct bch_control *bch, int k, 8304c6de856SChristian Hitz const struct gf_poly *f, struct gf_poly *z, 8314c6de856SChristian Hitz struct gf_poly *out) 8324c6de856SChristian Hitz { 8334c6de856SChristian Hitz const int m = GF_M(bch); 8344c6de856SChristian Hitz int i, j; 8354c6de856SChristian Hitz 8364c6de856SChristian Hitz /* z contains z^2j mod f */ 8374c6de856SChristian Hitz z->deg = 1; 8384c6de856SChristian Hitz z->c[0] = 0; 8394c6de856SChristian Hitz z->c[1] = bch->a_pow_tab[k]; 8404c6de856SChristian Hitz 8414c6de856SChristian Hitz out->deg = 0; 8424c6de856SChristian Hitz memset(out, 0, GF_POLY_SZ(f->deg)); 8434c6de856SChristian Hitz 8444c6de856SChristian Hitz /* compute f log representation only once */ 8454c6de856SChristian Hitz gf_poly_logrep(bch, f, bch->cache); 8464c6de856SChristian Hitz 8474c6de856SChristian Hitz for (i = 0; i < m; i++) { 8484c6de856SChristian Hitz /* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */ 8494c6de856SChristian Hitz for (j = z->deg; j >= 0; j--) { 8504c6de856SChristian Hitz out->c[j] ^= z->c[j]; 8514c6de856SChristian Hitz z->c[2*j] = gf_sqr(bch, z->c[j]); 8524c6de856SChristian Hitz z->c[2*j+1] = 0; 8534c6de856SChristian Hitz } 8544c6de856SChristian Hitz if (z->deg > out->deg) 8554c6de856SChristian Hitz out->deg = z->deg; 8564c6de856SChristian Hitz 8574c6de856SChristian Hitz if (i < m-1) { 8584c6de856SChristian Hitz z->deg *= 2; 8594c6de856SChristian Hitz /* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */ 8604c6de856SChristian Hitz gf_poly_mod(bch, z, f, bch->cache); 8614c6de856SChristian Hitz } 8624c6de856SChristian Hitz } 8634c6de856SChristian Hitz while (!out->c[out->deg] && out->deg) 8644c6de856SChristian Hitz out->deg--; 8654c6de856SChristian Hitz 8664c6de856SChristian Hitz dbg("Tr(a^%d.X) mod f = %s\n", k, gf_poly_str(out)); 8674c6de856SChristian Hitz } 8684c6de856SChristian Hitz 8694c6de856SChristian Hitz /* 8704c6de856SChristian Hitz * factor a polynomial using Berlekamp Trace algorithm (BTA) 8714c6de856SChristian Hitz */ 8724c6de856SChristian Hitz static void factor_polynomial(struct bch_control *bch, int k, struct gf_poly *f, 8734c6de856SChristian Hitz struct gf_poly **g, struct gf_poly **h) 8744c6de856SChristian Hitz { 8754c6de856SChristian Hitz struct gf_poly *f2 = bch->poly_2t[0]; 8764c6de856SChristian Hitz struct gf_poly *q = bch->poly_2t[1]; 8774c6de856SChristian Hitz struct gf_poly *tk = bch->poly_2t[2]; 8784c6de856SChristian Hitz struct gf_poly *z = bch->poly_2t[3]; 8794c6de856SChristian Hitz struct gf_poly *gcd; 8804c6de856SChristian Hitz 8814c6de856SChristian Hitz dbg("factoring %s...\n", gf_poly_str(f)); 8824c6de856SChristian Hitz 8834c6de856SChristian Hitz *g = f; 8844c6de856SChristian Hitz *h = NULL; 8854c6de856SChristian Hitz 8864c6de856SChristian Hitz /* tk = Tr(a^k.X) mod f */ 8874c6de856SChristian Hitz compute_trace_bk_mod(bch, k, f, z, tk); 8884c6de856SChristian Hitz 8894c6de856SChristian Hitz if (tk->deg > 0) { 8904c6de856SChristian Hitz /* compute g = gcd(f, tk) (destructive operation) */ 8914c6de856SChristian Hitz gf_poly_copy(f2, f); 8924c6de856SChristian Hitz gcd = gf_poly_gcd(bch, f2, tk); 8934c6de856SChristian Hitz if (gcd->deg < f->deg) { 8944c6de856SChristian Hitz /* compute h=f/gcd(f,tk); this will modify f and q */ 8954c6de856SChristian Hitz gf_poly_div(bch, f, gcd, q); 8964c6de856SChristian Hitz /* store g and h in-place (clobbering f) */ 8974c6de856SChristian Hitz *h = &((struct gf_poly_deg1 *)f)[gcd->deg].poly; 8984c6de856SChristian Hitz gf_poly_copy(*g, gcd); 8994c6de856SChristian Hitz gf_poly_copy(*h, q); 9004c6de856SChristian Hitz } 9014c6de856SChristian Hitz } 9024c6de856SChristian Hitz } 9034c6de856SChristian Hitz 9044c6de856SChristian Hitz /* 9054c6de856SChristian Hitz * find roots of a polynomial, using BTZ algorithm; see the beginning of this 9064c6de856SChristian Hitz * file for details 9074c6de856SChristian Hitz */ 9084c6de856SChristian Hitz static int find_poly_roots(struct bch_control *bch, unsigned int k, 9094c6de856SChristian Hitz struct gf_poly *poly, unsigned int *roots) 9104c6de856SChristian Hitz { 9114c6de856SChristian Hitz int cnt; 9124c6de856SChristian Hitz struct gf_poly *f1, *f2; 9134c6de856SChristian Hitz 9144c6de856SChristian Hitz switch (poly->deg) { 9154c6de856SChristian Hitz /* handle low degree polynomials with ad hoc techniques */ 9164c6de856SChristian Hitz case 1: 9174c6de856SChristian Hitz cnt = find_poly_deg1_roots(bch, poly, roots); 9184c6de856SChristian Hitz break; 9194c6de856SChristian Hitz case 2: 9204c6de856SChristian Hitz cnt = find_poly_deg2_roots(bch, poly, roots); 9214c6de856SChristian Hitz break; 9224c6de856SChristian Hitz case 3: 9234c6de856SChristian Hitz cnt = find_poly_deg3_roots(bch, poly, roots); 9244c6de856SChristian Hitz break; 9254c6de856SChristian Hitz case 4: 9264c6de856SChristian Hitz cnt = find_poly_deg4_roots(bch, poly, roots); 9274c6de856SChristian Hitz break; 9284c6de856SChristian Hitz default: 9294c6de856SChristian Hitz /* factor polynomial using Berlekamp Trace Algorithm (BTA) */ 9304c6de856SChristian Hitz cnt = 0; 9314c6de856SChristian Hitz if (poly->deg && (k <= GF_M(bch))) { 9324c6de856SChristian Hitz factor_polynomial(bch, k, poly, &f1, &f2); 9334c6de856SChristian Hitz if (f1) 9344c6de856SChristian Hitz cnt += find_poly_roots(bch, k+1, f1, roots); 9354c6de856SChristian Hitz if (f2) 9364c6de856SChristian Hitz cnt += find_poly_roots(bch, k+1, f2, roots+cnt); 9374c6de856SChristian Hitz } 9384c6de856SChristian Hitz break; 9394c6de856SChristian Hitz } 9404c6de856SChristian Hitz return cnt; 9414c6de856SChristian Hitz } 9424c6de856SChristian Hitz 9434c6de856SChristian Hitz #if defined(USE_CHIEN_SEARCH) 9444c6de856SChristian Hitz /* 9454c6de856SChristian Hitz * exhaustive root search (Chien) implementation - not used, included only for 9464c6de856SChristian Hitz * reference/comparison tests 9474c6de856SChristian Hitz */ 9484c6de856SChristian Hitz static int chien_search(struct bch_control *bch, unsigned int len, 9494c6de856SChristian Hitz struct gf_poly *p, unsigned int *roots) 9504c6de856SChristian Hitz { 9514c6de856SChristian Hitz int m; 9524c6de856SChristian Hitz unsigned int i, j, syn, syn0, count = 0; 9534c6de856SChristian Hitz const unsigned int k = 8*len+bch->ecc_bits; 9544c6de856SChristian Hitz 9554c6de856SChristian Hitz /* use a log-based representation of polynomial */ 9564c6de856SChristian Hitz gf_poly_logrep(bch, p, bch->cache); 9574c6de856SChristian Hitz bch->cache[p->deg] = 0; 9584c6de856SChristian Hitz syn0 = gf_div(bch, p->c[0], p->c[p->deg]); 9594c6de856SChristian Hitz 9604c6de856SChristian Hitz for (i = GF_N(bch)-k+1; i <= GF_N(bch); i++) { 9614c6de856SChristian Hitz /* compute elp(a^i) */ 9624c6de856SChristian Hitz for (j = 1, syn = syn0; j <= p->deg; j++) { 9634c6de856SChristian Hitz m = bch->cache[j]; 9644c6de856SChristian Hitz if (m >= 0) 9654c6de856SChristian Hitz syn ^= a_pow(bch, m+j*i); 9664c6de856SChristian Hitz } 9674c6de856SChristian Hitz if (syn == 0) { 9684c6de856SChristian Hitz roots[count++] = GF_N(bch)-i; 9694c6de856SChristian Hitz if (count == p->deg) 9704c6de856SChristian Hitz break; 9714c6de856SChristian Hitz } 9724c6de856SChristian Hitz } 9734c6de856SChristian Hitz return (count == p->deg) ? count : 0; 9744c6de856SChristian Hitz } 9754c6de856SChristian Hitz #define find_poly_roots(_p, _k, _elp, _loc) chien_search(_p, len, _elp, _loc) 9764c6de856SChristian Hitz #endif /* USE_CHIEN_SEARCH */ 9774c6de856SChristian Hitz 9784c6de856SChristian Hitz /** 9794c6de856SChristian Hitz * decode_bch - decode received codeword and find bit error locations 9804c6de856SChristian Hitz * @bch: BCH control structure 9814c6de856SChristian Hitz * @data: received data, ignored if @calc_ecc is provided 9824c6de856SChristian Hitz * @len: data length in bytes, must always be provided 9834c6de856SChristian Hitz * @recv_ecc: received ecc, if NULL then assume it was XORed in @calc_ecc 9844c6de856SChristian Hitz * @calc_ecc: calculated ecc, if NULL then calc_ecc is computed from @data 9854c6de856SChristian Hitz * @syn: hw computed syndrome data (if NULL, syndrome is calculated) 9864c6de856SChristian Hitz * @errloc: output array of error locations 9874c6de856SChristian Hitz * 9884c6de856SChristian Hitz * Returns: 9894c6de856SChristian Hitz * The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if 9904c6de856SChristian Hitz * invalid parameters were provided 9914c6de856SChristian Hitz * 9924c6de856SChristian Hitz * Depending on the available hw BCH support and the need to compute @calc_ecc 9934c6de856SChristian Hitz * separately (using encode_bch()), this function should be called with one of 9944c6de856SChristian Hitz * the following parameter configurations - 9954c6de856SChristian Hitz * 9964c6de856SChristian Hitz * by providing @data and @recv_ecc only: 9974c6de856SChristian Hitz * decode_bch(@bch, @data, @len, @recv_ecc, NULL, NULL, @errloc) 9984c6de856SChristian Hitz * 9994c6de856SChristian Hitz * by providing @recv_ecc and @calc_ecc: 10004c6de856SChristian Hitz * decode_bch(@bch, NULL, @len, @recv_ecc, @calc_ecc, NULL, @errloc) 10014c6de856SChristian Hitz * 10024c6de856SChristian Hitz * by providing ecc = recv_ecc XOR calc_ecc: 10034c6de856SChristian Hitz * decode_bch(@bch, NULL, @len, NULL, ecc, NULL, @errloc) 10044c6de856SChristian Hitz * 10054c6de856SChristian Hitz * by providing syndrome results @syn: 10064c6de856SChristian Hitz * decode_bch(@bch, NULL, @len, NULL, NULL, @syn, @errloc) 10074c6de856SChristian Hitz * 10084c6de856SChristian Hitz * Once decode_bch() has successfully returned with a positive value, error 10094c6de856SChristian Hitz * locations returned in array @errloc should be interpreted as follows - 10104c6de856SChristian Hitz * 10114c6de856SChristian Hitz * if (errloc[n] >= 8*len), then n-th error is located in ecc (no need for 10124c6de856SChristian Hitz * data correction) 10134c6de856SChristian Hitz * 10144c6de856SChristian Hitz * if (errloc[n] < 8*len), then n-th error is located in data and can be 10154c6de856SChristian Hitz * corrected with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8); 10164c6de856SChristian Hitz * 10174c6de856SChristian Hitz * Note that this function does not perform any data correction by itself, it 10184c6de856SChristian Hitz * merely indicates error locations. 10194c6de856SChristian Hitz */ 10204c6de856SChristian Hitz int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, 10214c6de856SChristian Hitz const uint8_t *recv_ecc, const uint8_t *calc_ecc, 10224c6de856SChristian Hitz const unsigned int *syn, unsigned int *errloc) 10234c6de856SChristian Hitz { 10244c6de856SChristian Hitz const unsigned int ecc_words = BCH_ECC_WORDS(bch); 10254c6de856SChristian Hitz unsigned int nbits; 10264c6de856SChristian Hitz int i, err, nroots; 10274c6de856SChristian Hitz uint32_t sum; 10284c6de856SChristian Hitz 10294c6de856SChristian Hitz /* sanity check: make sure data length can be handled */ 10304c6de856SChristian Hitz if (8*len > (bch->n-bch->ecc_bits)) 10314c6de856SChristian Hitz return -EINVAL; 10324c6de856SChristian Hitz 10334c6de856SChristian Hitz /* if caller does not provide syndromes, compute them */ 10344c6de856SChristian Hitz if (!syn) { 10354c6de856SChristian Hitz if (!calc_ecc) { 10364c6de856SChristian Hitz /* compute received data ecc into an internal buffer */ 10374c6de856SChristian Hitz if (!data || !recv_ecc) 10384c6de856SChristian Hitz return -EINVAL; 10394c6de856SChristian Hitz encode_bch(bch, data, len, NULL); 10404c6de856SChristian Hitz } else { 10414c6de856SChristian Hitz /* load provided calculated ecc */ 10424c6de856SChristian Hitz load_ecc8(bch, bch->ecc_buf, calc_ecc); 10434c6de856SChristian Hitz } 10444c6de856SChristian Hitz /* load received ecc or assume it was XORed in calc_ecc */ 10454c6de856SChristian Hitz if (recv_ecc) { 10464c6de856SChristian Hitz load_ecc8(bch, bch->ecc_buf2, recv_ecc); 10474c6de856SChristian Hitz /* XOR received and calculated ecc */ 10484c6de856SChristian Hitz for (i = 0, sum = 0; i < (int)ecc_words; i++) { 10494c6de856SChristian Hitz bch->ecc_buf[i] ^= bch->ecc_buf2[i]; 10504c6de856SChristian Hitz sum |= bch->ecc_buf[i]; 10514c6de856SChristian Hitz } 10524c6de856SChristian Hitz if (!sum) 10534c6de856SChristian Hitz /* no error found */ 10544c6de856SChristian Hitz return 0; 10554c6de856SChristian Hitz } 10564c6de856SChristian Hitz compute_syndromes(bch, bch->ecc_buf, bch->syn); 10574c6de856SChristian Hitz syn = bch->syn; 10584c6de856SChristian Hitz } 10594c6de856SChristian Hitz 10604c6de856SChristian Hitz err = compute_error_locator_polynomial(bch, syn); 10614c6de856SChristian Hitz if (err > 0) { 10624c6de856SChristian Hitz nroots = find_poly_roots(bch, 1, bch->elp, errloc); 10634c6de856SChristian Hitz if (err != nroots) 10644c6de856SChristian Hitz err = -1; 10654c6de856SChristian Hitz } 10664c6de856SChristian Hitz if (err > 0) { 10674c6de856SChristian Hitz /* post-process raw error locations for easier correction */ 10684c6de856SChristian Hitz nbits = (len*8)+bch->ecc_bits; 10694c6de856SChristian Hitz for (i = 0; i < err; i++) { 10704c6de856SChristian Hitz if (errloc[i] >= nbits) { 10714c6de856SChristian Hitz err = -1; 10724c6de856SChristian Hitz break; 10734c6de856SChristian Hitz } 10744c6de856SChristian Hitz errloc[i] = nbits-1-errloc[i]; 10754c6de856SChristian Hitz errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7)); 10764c6de856SChristian Hitz } 10774c6de856SChristian Hitz } 10784c6de856SChristian Hitz return (err >= 0) ? err : -EBADMSG; 10794c6de856SChristian Hitz } 10804c6de856SChristian Hitz 10814c6de856SChristian Hitz /* 10824c6de856SChristian Hitz * generate Galois field lookup tables 10834c6de856SChristian Hitz */ 10844c6de856SChristian Hitz static int build_gf_tables(struct bch_control *bch, unsigned int poly) 10854c6de856SChristian Hitz { 10864c6de856SChristian Hitz unsigned int i, x = 1; 10874c6de856SChristian Hitz const unsigned int k = 1 << deg(poly); 10884c6de856SChristian Hitz 10894c6de856SChristian Hitz /* primitive polynomial must be of degree m */ 10904c6de856SChristian Hitz if (k != (1u << GF_M(bch))) 10914c6de856SChristian Hitz return -1; 10924c6de856SChristian Hitz 10934c6de856SChristian Hitz for (i = 0; i < GF_N(bch); i++) { 10944c6de856SChristian Hitz bch->a_pow_tab[i] = x; 10954c6de856SChristian Hitz bch->a_log_tab[x] = i; 10964c6de856SChristian Hitz if (i && (x == 1)) 10974c6de856SChristian Hitz /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */ 10984c6de856SChristian Hitz return -1; 10994c6de856SChristian Hitz x <<= 1; 11004c6de856SChristian Hitz if (x & k) 11014c6de856SChristian Hitz x ^= poly; 11024c6de856SChristian Hitz } 11034c6de856SChristian Hitz bch->a_pow_tab[GF_N(bch)] = 1; 11044c6de856SChristian Hitz bch->a_log_tab[0] = 0; 11054c6de856SChristian Hitz 11064c6de856SChristian Hitz return 0; 11074c6de856SChristian Hitz } 11084c6de856SChristian Hitz 11094c6de856SChristian Hitz /* 11104c6de856SChristian Hitz * compute generator polynomial remainder tables for fast encoding 11114c6de856SChristian Hitz */ 11124c6de856SChristian Hitz static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) 11134c6de856SChristian Hitz { 11144c6de856SChristian Hitz int i, j, b, d; 11154c6de856SChristian Hitz uint32_t data, hi, lo, *tab; 11164c6de856SChristian Hitz const int l = BCH_ECC_WORDS(bch); 11174c6de856SChristian Hitz const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); 11184c6de856SChristian Hitz const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); 11194c6de856SChristian Hitz 11204c6de856SChristian Hitz memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); 11214c6de856SChristian Hitz 11224c6de856SChristian Hitz for (i = 0; i < 256; i++) { 11234c6de856SChristian Hitz /* p(X)=i is a small polynomial of weight <= 8 */ 11244c6de856SChristian Hitz for (b = 0; b < 4; b++) { 11254c6de856SChristian Hitz /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ 11264c6de856SChristian Hitz tab = bch->mod8_tab + (b*256+i)*l; 11274c6de856SChristian Hitz data = i << (8*b); 11284c6de856SChristian Hitz while (data) { 11294c6de856SChristian Hitz d = deg(data); 11304c6de856SChristian Hitz /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ 11314c6de856SChristian Hitz data ^= g[0] >> (31-d); 11324c6de856SChristian Hitz for (j = 0; j < ecclen; j++) { 11334c6de856SChristian Hitz hi = (d < 31) ? g[j] << (d+1) : 0; 11344c6de856SChristian Hitz lo = (j+1 < plen) ? 11354c6de856SChristian Hitz g[j+1] >> (31-d) : 0; 11364c6de856SChristian Hitz tab[j] ^= hi|lo; 11374c6de856SChristian Hitz } 11384c6de856SChristian Hitz } 11394c6de856SChristian Hitz } 11404c6de856SChristian Hitz } 11414c6de856SChristian Hitz } 11424c6de856SChristian Hitz 11434c6de856SChristian Hitz /* 11444c6de856SChristian Hitz * build a base for factoring degree 2 polynomials 11454c6de856SChristian Hitz */ 11464c6de856SChristian Hitz static int build_deg2_base(struct bch_control *bch) 11474c6de856SChristian Hitz { 11484c6de856SChristian Hitz const int m = GF_M(bch); 11494c6de856SChristian Hitz int i, j, r; 11504c6de856SChristian Hitz unsigned int sum, x, y, remaining, ak = 0, xi[m]; 11514c6de856SChristian Hitz 11524c6de856SChristian Hitz /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ 11534c6de856SChristian Hitz for (i = 0; i < m; i++) { 11544c6de856SChristian Hitz for (j = 0, sum = 0; j < m; j++) 11554c6de856SChristian Hitz sum ^= a_pow(bch, i*(1 << j)); 11564c6de856SChristian Hitz 11574c6de856SChristian Hitz if (sum) { 11584c6de856SChristian Hitz ak = bch->a_pow_tab[i]; 11594c6de856SChristian Hitz break; 11604c6de856SChristian Hitz } 11614c6de856SChristian Hitz } 11624c6de856SChristian Hitz /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ 11634c6de856SChristian Hitz remaining = m; 11644c6de856SChristian Hitz memset(xi, 0, sizeof(xi)); 11654c6de856SChristian Hitz 11664c6de856SChristian Hitz for (x = 0; (x <= GF_N(bch)) && remaining; x++) { 11674c6de856SChristian Hitz y = gf_sqr(bch, x)^x; 11684c6de856SChristian Hitz for (i = 0; i < 2; i++) { 11694c6de856SChristian Hitz r = a_log(bch, y); 11704c6de856SChristian Hitz if (y && (r < m) && !xi[r]) { 11714c6de856SChristian Hitz bch->xi_tab[r] = x; 11724c6de856SChristian Hitz xi[r] = 1; 11734c6de856SChristian Hitz remaining--; 11744c6de856SChristian Hitz dbg("x%d = %x\n", r, x); 11754c6de856SChristian Hitz break; 11764c6de856SChristian Hitz } 11774c6de856SChristian Hitz y ^= ak; 11784c6de856SChristian Hitz } 11794c6de856SChristian Hitz } 11804c6de856SChristian Hitz /* should not happen but check anyway */ 11814c6de856SChristian Hitz return remaining ? -1 : 0; 11824c6de856SChristian Hitz } 11834c6de856SChristian Hitz 11844c6de856SChristian Hitz static void *bch_alloc(size_t size, int *err) 11854c6de856SChristian Hitz { 11864c6de856SChristian Hitz void *ptr; 11874c6de856SChristian Hitz 11884c6de856SChristian Hitz ptr = kmalloc(size, GFP_KERNEL); 11894c6de856SChristian Hitz if (ptr == NULL) 11904c6de856SChristian Hitz *err = 1; 11914c6de856SChristian Hitz return ptr; 11924c6de856SChristian Hitz } 11934c6de856SChristian Hitz 11944c6de856SChristian Hitz /* 11954c6de856SChristian Hitz * compute generator polynomial for given (m,t) parameters. 11964c6de856SChristian Hitz */ 11974c6de856SChristian Hitz static uint32_t *compute_generator_polynomial(struct bch_control *bch) 11984c6de856SChristian Hitz { 11994c6de856SChristian Hitz const unsigned int m = GF_M(bch); 12004c6de856SChristian Hitz const unsigned int t = GF_T(bch); 12014c6de856SChristian Hitz int n, err = 0; 12024c6de856SChristian Hitz unsigned int i, j, nbits, r, word, *roots; 12034c6de856SChristian Hitz struct gf_poly *g; 12044c6de856SChristian Hitz uint32_t *genpoly; 12054c6de856SChristian Hitz 12064c6de856SChristian Hitz g = bch_alloc(GF_POLY_SZ(m*t), &err); 12074c6de856SChristian Hitz roots = bch_alloc((bch->n+1)*sizeof(*roots), &err); 12084c6de856SChristian Hitz genpoly = bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); 12094c6de856SChristian Hitz 12104c6de856SChristian Hitz if (err) { 12114c6de856SChristian Hitz kfree(genpoly); 12124c6de856SChristian Hitz genpoly = NULL; 12134c6de856SChristian Hitz goto finish; 12144c6de856SChristian Hitz } 12154c6de856SChristian Hitz 12164c6de856SChristian Hitz /* enumerate all roots of g(X) */ 12174c6de856SChristian Hitz memset(roots , 0, (bch->n+1)*sizeof(*roots)); 12184c6de856SChristian Hitz for (i = 0; i < t; i++) { 12194c6de856SChristian Hitz for (j = 0, r = 2*i+1; j < m; j++) { 12204c6de856SChristian Hitz roots[r] = 1; 12214c6de856SChristian Hitz r = mod_s(bch, 2*r); 12224c6de856SChristian Hitz } 12234c6de856SChristian Hitz } 12244c6de856SChristian Hitz /* build generator polynomial g(X) */ 12254c6de856SChristian Hitz g->deg = 0; 12264c6de856SChristian Hitz g->c[0] = 1; 12274c6de856SChristian Hitz for (i = 0; i < GF_N(bch); i++) { 12284c6de856SChristian Hitz if (roots[i]) { 12294c6de856SChristian Hitz /* multiply g(X) by (X+root) */ 12304c6de856SChristian Hitz r = bch->a_pow_tab[i]; 12314c6de856SChristian Hitz g->c[g->deg+1] = 1; 12324c6de856SChristian Hitz for (j = g->deg; j > 0; j--) 12334c6de856SChristian Hitz g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; 12344c6de856SChristian Hitz 12354c6de856SChristian Hitz g->c[0] = gf_mul(bch, g->c[0], r); 12364c6de856SChristian Hitz g->deg++; 12374c6de856SChristian Hitz } 12384c6de856SChristian Hitz } 12394c6de856SChristian Hitz /* store left-justified binary representation of g(X) */ 12404c6de856SChristian Hitz n = g->deg+1; 12414c6de856SChristian Hitz i = 0; 12424c6de856SChristian Hitz 12434c6de856SChristian Hitz while (n > 0) { 12444c6de856SChristian Hitz nbits = (n > 32) ? 32 : n; 12454c6de856SChristian Hitz for (j = 0, word = 0; j < nbits; j++) { 12464c6de856SChristian Hitz if (g->c[n-1-j]) 12474c6de856SChristian Hitz word |= 1u << (31-j); 12484c6de856SChristian Hitz } 12494c6de856SChristian Hitz genpoly[i++] = word; 12504c6de856SChristian Hitz n -= nbits; 12514c6de856SChristian Hitz } 12524c6de856SChristian Hitz bch->ecc_bits = g->deg; 12534c6de856SChristian Hitz 12544c6de856SChristian Hitz finish: 12554c6de856SChristian Hitz kfree(g); 12564c6de856SChristian Hitz kfree(roots); 12574c6de856SChristian Hitz 12584c6de856SChristian Hitz return genpoly; 12594c6de856SChristian Hitz } 12604c6de856SChristian Hitz 12614c6de856SChristian Hitz /** 12624c6de856SChristian Hitz * init_bch - initialize a BCH encoder/decoder 12634c6de856SChristian Hitz * @m: Galois field order, should be in the range 5-15 12644c6de856SChristian Hitz * @t: maximum error correction capability, in bits 12654c6de856SChristian Hitz * @prim_poly: user-provided primitive polynomial (or 0 to use default) 12664c6de856SChristian Hitz * 12674c6de856SChristian Hitz * Returns: 12684c6de856SChristian Hitz * a newly allocated BCH control structure if successful, NULL otherwise 12694c6de856SChristian Hitz * 12704c6de856SChristian Hitz * This initialization can take some time, as lookup tables are built for fast 12714c6de856SChristian Hitz * encoding/decoding; make sure not to call this function from a time critical 12724c6de856SChristian Hitz * path. Usually, init_bch() should be called on module/driver init and 12734c6de856SChristian Hitz * free_bch() should be called to release memory on exit. 12744c6de856SChristian Hitz * 12754c6de856SChristian Hitz * You may provide your own primitive polynomial of degree @m in argument 12764c6de856SChristian Hitz * @prim_poly, or let init_bch() use its default polynomial. 12774c6de856SChristian Hitz * 12784c6de856SChristian Hitz * Once init_bch() has successfully returned a pointer to a newly allocated 12794c6de856SChristian Hitz * BCH control structure, ecc length in bytes is given by member @ecc_bytes of 12804c6de856SChristian Hitz * the structure. 12814c6de856SChristian Hitz */ 12824c6de856SChristian Hitz struct bch_control *init_bch(int m, int t, unsigned int prim_poly) 12834c6de856SChristian Hitz { 12844c6de856SChristian Hitz int err = 0; 12854c6de856SChristian Hitz unsigned int i, words; 12864c6de856SChristian Hitz uint32_t *genpoly; 12874c6de856SChristian Hitz struct bch_control *bch = NULL; 12884c6de856SChristian Hitz 12894c6de856SChristian Hitz const int min_m = 5; 12904c6de856SChristian Hitz const int max_m = 15; 12914c6de856SChristian Hitz 12924c6de856SChristian Hitz /* default primitive polynomials */ 12934c6de856SChristian Hitz static const unsigned int prim_poly_tab[] = { 12944c6de856SChristian Hitz 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, 12954c6de856SChristian Hitz 0x402b, 0x8003, 12964c6de856SChristian Hitz }; 12974c6de856SChristian Hitz 12984c6de856SChristian Hitz #if defined(CONFIG_BCH_CONST_PARAMS) 12994c6de856SChristian Hitz if ((m != (CONFIG_BCH_CONST_M)) || (t != (CONFIG_BCH_CONST_T))) { 13004c6de856SChristian Hitz printk(KERN_ERR "bch encoder/decoder was configured to support " 13014c6de856SChristian Hitz "parameters m=%d, t=%d only!\n", 13024c6de856SChristian Hitz CONFIG_BCH_CONST_M, CONFIG_BCH_CONST_T); 13034c6de856SChristian Hitz goto fail; 13044c6de856SChristian Hitz } 13054c6de856SChristian Hitz #endif 13064c6de856SChristian Hitz if ((m < min_m) || (m > max_m)) 13074c6de856SChristian Hitz /* 13084c6de856SChristian Hitz * values of m greater than 15 are not currently supported; 13094c6de856SChristian Hitz * supporting m > 15 would require changing table base type 13104c6de856SChristian Hitz * (uint16_t) and a small patch in matrix transposition 13114c6de856SChristian Hitz */ 13124c6de856SChristian Hitz goto fail; 13134c6de856SChristian Hitz 13144c6de856SChristian Hitz /* sanity checks */ 13154c6de856SChristian Hitz if ((t < 1) || (m*t >= ((1 << m)-1))) 13164c6de856SChristian Hitz /* invalid t value */ 13174c6de856SChristian Hitz goto fail; 13184c6de856SChristian Hitz 13194c6de856SChristian Hitz /* select a primitive polynomial for generating GF(2^m) */ 13204c6de856SChristian Hitz if (prim_poly == 0) 13214c6de856SChristian Hitz prim_poly = prim_poly_tab[m-min_m]; 13224c6de856SChristian Hitz 13234c6de856SChristian Hitz bch = kzalloc(sizeof(*bch), GFP_KERNEL); 13244c6de856SChristian Hitz if (bch == NULL) 13254c6de856SChristian Hitz goto fail; 13264c6de856SChristian Hitz 13274c6de856SChristian Hitz bch->m = m; 13284c6de856SChristian Hitz bch->t = t; 13294c6de856SChristian Hitz bch->n = (1 << m)-1; 13304c6de856SChristian Hitz words = DIV_ROUND_UP(m*t, 32); 13314c6de856SChristian Hitz bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); 13324c6de856SChristian Hitz bch->a_pow_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); 13334c6de856SChristian Hitz bch->a_log_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); 13344c6de856SChristian Hitz bch->mod8_tab = bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); 13354c6de856SChristian Hitz bch->ecc_buf = bch_alloc(words*sizeof(*bch->ecc_buf), &err); 13364c6de856SChristian Hitz bch->ecc_buf2 = bch_alloc(words*sizeof(*bch->ecc_buf2), &err); 13374c6de856SChristian Hitz bch->xi_tab = bch_alloc(m*sizeof(*bch->xi_tab), &err); 13384c6de856SChristian Hitz bch->syn = bch_alloc(2*t*sizeof(*bch->syn), &err); 13394c6de856SChristian Hitz bch->cache = bch_alloc(2*t*sizeof(*bch->cache), &err); 13404c6de856SChristian Hitz bch->elp = bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); 13414c6de856SChristian Hitz 13424c6de856SChristian Hitz for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) 13434c6de856SChristian Hitz bch->poly_2t[i] = bch_alloc(GF_POLY_SZ(2*t), &err); 13444c6de856SChristian Hitz 13454c6de856SChristian Hitz if (err) 13464c6de856SChristian Hitz goto fail; 13474c6de856SChristian Hitz 13484c6de856SChristian Hitz err = build_gf_tables(bch, prim_poly); 13494c6de856SChristian Hitz if (err) 13504c6de856SChristian Hitz goto fail; 13514c6de856SChristian Hitz 13524c6de856SChristian Hitz /* use generator polynomial for computing encoding tables */ 13534c6de856SChristian Hitz genpoly = compute_generator_polynomial(bch); 13544c6de856SChristian Hitz if (genpoly == NULL) 13554c6de856SChristian Hitz goto fail; 13564c6de856SChristian Hitz 13574c6de856SChristian Hitz build_mod8_tables(bch, genpoly); 13584c6de856SChristian Hitz kfree(genpoly); 13594c6de856SChristian Hitz 13604c6de856SChristian Hitz err = build_deg2_base(bch); 13614c6de856SChristian Hitz if (err) 13624c6de856SChristian Hitz goto fail; 13634c6de856SChristian Hitz 13644c6de856SChristian Hitz return bch; 13654c6de856SChristian Hitz 13664c6de856SChristian Hitz fail: 13674c6de856SChristian Hitz free_bch(bch); 13684c6de856SChristian Hitz return NULL; 13694c6de856SChristian Hitz } 13704c6de856SChristian Hitz 13714c6de856SChristian Hitz /** 13724c6de856SChristian Hitz * free_bch - free the BCH control structure 13734c6de856SChristian Hitz * @bch: BCH control structure to release 13744c6de856SChristian Hitz */ 13754c6de856SChristian Hitz void free_bch(struct bch_control *bch) 13764c6de856SChristian Hitz { 13774c6de856SChristian Hitz unsigned int i; 13784c6de856SChristian Hitz 13794c6de856SChristian Hitz if (bch) { 13804c6de856SChristian Hitz kfree(bch->a_pow_tab); 13814c6de856SChristian Hitz kfree(bch->a_log_tab); 13824c6de856SChristian Hitz kfree(bch->mod8_tab); 13834c6de856SChristian Hitz kfree(bch->ecc_buf); 13844c6de856SChristian Hitz kfree(bch->ecc_buf2); 13854c6de856SChristian Hitz kfree(bch->xi_tab); 13864c6de856SChristian Hitz kfree(bch->syn); 13874c6de856SChristian Hitz kfree(bch->cache); 13884c6de856SChristian Hitz kfree(bch->elp); 13894c6de856SChristian Hitz 13904c6de856SChristian Hitz for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) 13914c6de856SChristian Hitz kfree(bch->poly_2t[i]); 13924c6de856SChristian Hitz 13934c6de856SChristian Hitz kfree(bch); 13944c6de856SChristian Hitz } 13954c6de856SChristian Hitz } 1396