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