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