Release 4.12 lib/bch.c
/*
* Generic binary BCH encoding/decoding library
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright © 2011 Parrot S.A.
*
* Author: Ivan Djelic <ivan.djelic@parrot.com>
*
* Description:
*
* This library provides runtime configurable encoding/decoding of binary
* Bose-Chaudhuri-Hocquenghem (BCH) codes.
*
* Call init_bch to get a pointer to a newly allocated bch_control structure for
* the given m (Galois field order), t (error correction capability) and
* (optional) primitive polynomial parameters.
*
* Call encode_bch to compute and store ecc parity bytes to a given buffer.
* Call decode_bch to detect and locate errors in received data.
*
* On systems supporting hw BCH features, intermediate results may be provided
* to decode_bch in order to skip certain steps. See decode_bch() documentation
* for details.
*
* Option CONFIG_BCH_CONST_PARAMS can be used to force fixed values of
* parameters m and t; thus allowing extra compiler optimizations and providing
* better (up to 2x) encoding performance. Using this option makes sense when
* (m,t) are fixed and known in advance, e.g. when using BCH error correction
* on a particular NAND flash device.
*
* Algorithmic details:
*
* Encoding is performed by processing 32 input bits in parallel, using 4
* remainder lookup tables.
*
* The final stage of decoding involves the following internal steps:
* a. Syndrome computation
* b. Error locator polynomial computation using Berlekamp-Massey algorithm
* c. Error locator root finding (by far the most expensive step)
*
* In this implementation, step c is not performed using the usual Chien search.
* Instead, an alternative approach described in [1] is used. It consists in
* factoring the error locator polynomial using the Berlekamp Trace algorithm
* (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial
* solving techniques [2] are used. The resulting algorithm, called BTZ, yields
* much better performance than Chien search for usual (m,t) values (typically
* m >= 13, t < 32, see [1]).
*
* [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields
* of characteristic 2, in: Western European Workshop on Research in Cryptology
* - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear.
* [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over
* finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <asm/byteorder.h>
#include <linux/bch.h>
#if defined(CONFIG_BCH_CONST_PARAMS)
#define GF_M(_p) (CONFIG_BCH_CONST_M)
#define GF_T(_p) (CONFIG_BCH_CONST_T)
#define GF_N(_p) ((1 << (CONFIG_BCH_CONST_M))-1)
#else
#define GF_M(_p) ((_p)->m)
#define GF_T(_p) ((_p)->t)
#define GF_N(_p) ((_p)->n)
#endif
#define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32)
#define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8)
#ifndef dbg
#define dbg(_fmt, args...) do {} while (0)
#endif
/*
* represent a polynomial over GF(2^m)
*/
struct gf_poly {
unsigned int deg; /* polynomial degree */
unsigned int c[0]; /* polynomial terms */
};
/* given its degree, compute a polynomial size in bytes */
#define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int))
/* polynomial of degree 1 */
struct gf_poly_deg1 {
struct gf_poly poly;
unsigned int c[2];
};
/*
* same as encode_bch(), but process input data one byte at a time
*/
static void encode_bch_unaligned(struct bch_control *bch,
const unsigned char *data, unsigned int len,
uint32_t *ecc)
{
int i;
const uint32_t *p;
const int l = BCH_ECC_WORDS(bch)-1;
while (len--) {
p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff);
for (i = 0; i < l; i++)
ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++);
ecc[l] = (ecc[l] << 8)^(*p);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 149 | 100.00% | 1 | 100.00% |
Total | 149 | 100.00% | 1 | 100.00% |
/*
* convert ecc bytes to aligned, zero-padded 32-bit ecc words
*/
static void load_ecc8(struct bch_control *bch, uint32_t *dst,
const uint8_t *src)
{
uint8_t pad[4] = {0, 0, 0, 0};
unsigned int i, nwords = BCH_ECC_WORDS(bch)-1;
for (i = 0; i < nwords; i++, src += 4)
dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3];
memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords);
dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 156 | 100.00% | 1 | 100.00% |
Total | 156 | 100.00% | 1 | 100.00% |
/*
* convert 32-bit ecc words to ecc bytes
*/
static void store_ecc8(struct bch_control *bch, uint8_t *dst,
const uint32_t *src)
{
uint8_t pad[4];
unsigned int i, nwords = BCH_ECC_WORDS(bch)-1;
for (i = 0; i < nwords; i++) {
*dst++ = (src[i] >> 24);
*dst++ = (src[i] >> 16) & 0xff;
*dst++ = (src[i] >> 8) & 0xff;
*dst++ = (src[i] >> 0) & 0xff;
}
pad[0] = (src[nwords] >> 24);
pad[1] = (src[nwords] >> 16) & 0xff;
pad[2] = (src[nwords] >> 8) & 0xff;
pad[3] = (src[nwords] >> 0) & 0xff;
memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 190 | 100.00% | 1 | 100.00% |
Total | 190 | 100.00% | 1 | 100.00% |
/**
* encode_bch - calculate BCH ecc parity of data
* @bch: BCH control structure
* @data: data to encode
* @len: data length in bytes
* @ecc: ecc parity data, must be initialized by caller
*
* The @ecc parity array is used both as input and output parameter, in order to
* allow incremental computations. It should be of the size indicated by member
* @ecc_bytes of @bch, and should be initialized to 0 before the first call.
*
* The exact number of computed ecc parity bits is given by member @ecc_bits of
* @bch; it may be less than m*t for large values of t.
*/
void encode_bch(struct bch_control *bch, const uint8_t *data,
unsigned int len, uint8_t *ecc)
{
const unsigned int l = BCH_ECC_WORDS(bch)-1;
unsigned int i, mlen;
unsigned long m;
uint32_t w, r[l+1];
const uint32_t * const tab0 = bch->mod8_tab;
const uint32_t * const tab1 = tab0 + 256*(l+1);
const uint32_t * const tab2 = tab1 + 256*(l+1);
const uint32_t * const tab3 = tab2 + 256*(l+1);
const uint32_t *pdata, *p0, *p1, *p2, *p3;
if (ecc) {
/* load ecc parity bytes into internal 32-bit buffer */
load_ecc8(bch, bch->ecc_buf, ecc);
} else {
memset(bch->ecc_buf, 0, sizeof(r));
}
/* process first unaligned data bytes */
m = ((unsigned long)data) & 3;
if (m) {
mlen = (len < (4-m)) ? len : 4-m;
encode_bch_unaligned(bch, data, mlen, bch->ecc_buf);
data += mlen;
len -= mlen;
}
/* process 32-bit aligned data words */
pdata = (uint32_t *)data;
mlen = len/4;
data += 4*mlen;
len -= 4*mlen;
memcpy(r, bch->ecc_buf, sizeof(r));
/*
* split each 32-bit word into 4 polynomials of weight 8 as follows:
*
* 31 ...24 23 ...16 15 ... 8 7 ... 0
* xxxxxxxx yyyyyyyy zzzzzzzz tttttttt
* tttttttt mod g = r0 (precomputed)
* zzzzzzzz 00000000 mod g = r1 (precomputed)
* yyyyyyyy 00000000 00000000 mod g = r2 (precomputed)
* xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed)
* xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3
*/
while (mlen--) {
/* input data is read in big-endian format */
w = r[0]^cpu_to_be32(*pdata++);
p0 = tab0 + (l+1)*((w >> 0) & 0xff);
p1 = tab1 + (l+1)*((w >> 8) & 0xff);
p2 = tab2 + (l+1)*((w >> 16) & 0xff);
p3 = tab3 + (l+1)*((w >> 24) & 0xff);
for (i = 0; i < l; i++)
r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i];
r[l] = p0[l]^p1[l]^p2[l]^p3[l];
}
memcpy(bch->ecc_buf, r, sizeof(r));
/* process last unaligned bytes */
if (len)
encode_bch_unaligned(bch, data, len, bch->ecc_buf);
/* store ecc parity bytes into original parity buffer */
if (ecc)
store_ecc8(bch, ecc, bch->ecc_buf);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 485 | 100.00% | 1 | 100.00% |
Total | 485 | 100.00% | 1 | 100.00% |
EXPORT_SYMBOL_GPL(encode_bch);
static inline int modulo(struct bch_control *bch, unsigned int v)
{
const unsigned int n = GF_N(bch);
while (v >= n) {
v -= n;
v = (v & n) + (v >> GF_M(bch));
}
return v;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 58 | 100.00% | 1 | 100.00% |
Total | 58 | 100.00% | 1 | 100.00% |
/*
* shorter and faster modulo function, only works when v < 2N.
*/
static inline int mod_s(struct bch_control *bch, unsigned int v)
{
const unsigned int n = GF_N(bch);
return (v < n) ? v : v-n;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 39 | 100.00% | 1 | 100.00% |
Total | 39 | 100.00% | 1 | 100.00% |
static inline int deg(unsigned int poly)
{
/* polynomial degree is the most-significant bit index */
return fls(poly)-1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 20 | 100.00% | 1 | 100.00% |
Total | 20 | 100.00% | 1 | 100.00% |
static inline int parity(unsigned int x)
{
/*
* public domain code snippet, lifted from
* http://www-graphics.stanford.edu/~seander/bithacks.html
*/
x ^= x >> 1;
x ^= x >> 2;
x = (x & 0x11111111U) * 0x11111111U;
return (x >> 28) & 1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 43 | 100.00% | 1 | 100.00% |
Total | 43 | 100.00% | 1 | 100.00% |
/* Galois field basic operations: multiply, divide, inverse, etc. */
static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a,
unsigned int b)
{
return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+
bch->a_log_tab[b])] : 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 54 | 100.00% | 1 | 100.00% |
Total | 54 | 100.00% | 1 | 100.00% |
static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a)
{
return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 41 | 100.00% | 1 | 100.00% |
Total | 41 | 100.00% | 1 | 100.00% |
static inline unsigned int gf_div(struct bch_control *bch, unsigned int a,
unsigned int b)
{
return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+
GF_N(bch)-bch->a_log_tab[b])] : 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 55 | 100.00% | 1 | 100.00% |
Total | 55 | 100.00% | 1 | 100.00% |
static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a)
{
return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 35 | 100.00% | 1 | 100.00% |
Total | 35 | 100.00% | 1 | 100.00% |
static inline unsigned int a_pow(struct bch_control *bch, int i)
{
return bch->a_pow_tab[modulo(bch, i)];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 29 | 100.00% | 1 | 100.00% |
Total | 29 | 100.00% | 1 | 100.00% |
static inline int a_log(struct bch_control *bch, unsigned int x)
{
return bch->a_log_tab[x];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
static inline int a_ilog(struct bch_control *bch, unsigned int x)
{
return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 34 | 100.00% | 1 | 100.00% |
Total | 34 | 100.00% | 1 | 100.00% |
/*
* compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t
*/
static void compute_syndromes(struct bch_control *bch, uint32_t *ecc,
unsigned int *syn)
{
int i, j, s;
unsigned int m;
uint32_t poly;
const int t = GF_T(bch);
s = bch->ecc_bits;
/* make sure extra bits in last ecc word are cleared */
m = ((unsigned int)s) & 31;
if (m)
ecc[s/32] &= ~((1u << (32-m))-1);
memset(syn, 0, 2*t*sizeof(*syn));
/* compute v(a^j) for j=1 .. 2t-1 */
do {
poly = *ecc++;
s -= 32;
while (poly) {
i = deg(poly);
for (j = 0; j < 2*t; j += 2)
syn[j] ^= a_pow(bch, (j+1)*(i+s));
poly ^= (1 << i);
}
} while (s > 0);
/* v(a^(2j)) = v(a^j)^2 */
for (j = 0; j < t; j++)
syn[2*j+1] = gf_sqr(bch, syn[j]);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 218 | 100.00% | 1 | 100.00% |
Total | 218 | 100.00% | 1 | 100.00% |
static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src)
{
memcpy(dst, src, GF_POLY_SZ(src->deg));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 30 | 100.00% | 1 | 100.00% |
Total | 30 | 100.00% | 1 | 100.00% |
static int compute_error_locator_polynomial(struct bch_control *bch,
const unsigned int *syn)
{
const unsigned int t = GF_T(bch);
const unsigned int n = GF_N(bch);
unsigned int i, j, tmp, l, pd = 1, d = syn[0];
struct gf_poly *elp = bch->elp;
struct gf_poly *pelp = bch->poly_2t[0];
struct gf_poly *elp_copy = bch->poly_2t[1];
int k, pp = -1;
memset(pelp, 0, GF_POLY_SZ(2*t));
memset(elp, 0, GF_POLY_SZ(2*t));
pelp->deg = 0;
pelp->c[0] = 1;
elp->deg = 0;
elp->c[0] = 1;
/* use simplified binary Berlekamp-Massey algorithm */
for (i = 0; (i < t) && (elp->deg <= t); i++) {
if (d) {
k = 2*i-pp;
gf_poly_copy(elp_copy, elp);
/* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */
tmp = a_log(bch, d)+n-a_log(bch, pd);
for (j = 0; j <= pelp->deg; j++) {
if (pelp->c[j]) {
l = a_log(bch, pelp->c[j]);
elp->c[j+k] ^= a_pow(bch, tmp+l);
}
}
/* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */
tmp = pelp->deg+k;
if (tmp > elp->deg) {
elp->deg = tmp;
gf_poly_copy(pelp, elp_copy);
pd = d;
pp = 2*i;
}
}
/* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */
if (i < t-1) {
d = syn[2*i+2];
for (j = 1; j <= elp->deg; j++)
d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]);
}
}
dbg("elp=%s\n", gf_poly_str(elp));
return (elp->deg > t) ? -1 : (int)elp->deg;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 416 | 100.00% | 1 | 100.00% |
Total | 416 | 100.00% | 1 | 100.00% |
/*
* solve a m x m linear system in GF(2) with an expected number of solutions,
* and return the number of found solutions
*/
static int solve_linear_system(struct bch_control *bch, unsigned int *rows,
unsigned int *sol, int nsol)
{
const int m = GF_M(bch);
unsigned int tmp, mask;
int rem, c, r, p, k, param[m];
k = 0;
mask = 1 << m;
/* Gaussian elimination */
for (c = 0; c < m; c++) {
rem = 0;
p = c-k;
/* find suitable row for elimination */
for (r = p; r < m; r++) {
if (rows[r] & mask) {
if (r != p) {
tmp = rows[r];
rows[r] = rows[p];
rows[p] = tmp;
}
rem = r+1;
break;
}
}
if (rem) {
/* perform elimination on remaining rows */
tmp = rows[p];
for (r = rem; r < m; r++) {
if (rows[r] & mask)
rows[r] ^= tmp;
}
} else {
/* elimination not needed, store defective row index */
param[k++] = c;
}
mask >>= 1;
}
/* rewrite system, inserting fake parameter rows */
if (k > 0) {
p = k;
for (r = m-1; r >= 0; r--) {
if ((r > m-1-k) && rows[r])
/* system has no solution */
return 0;
rows[r] = (p && (r == param[p-1])) ?
p--, 1u << (m-r) : rows[r-p];
}
}
if (nsol != (1 << k))
/* unexpected number of solutions */
return 0;
for (p = 0; p < nsol; p++) {
/* set parameters for p-th solution */
for (c = 0; c < k; c++)
rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1);
/* compute unique solution */
tmp = 0;
for (r = m-1; r >= 0; r--) {
mask = rows[r] & (tmp|1);
tmp |= parity(mask) << (m-r);
}
sol[p] = tmp >> 1;
}
return nsol;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 441 | 100.00% | 1 | 100.00% |
Total | 441 | 100.00% | 1 | 100.00% |
/*
* this function builds and solves a linear system for finding roots of a degree
* 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m).
*/
static int find_affine4_roots(struct bch_control *bch, unsigned int a,
unsigned int b, unsigned int c,
unsigned int *roots)
{
int i, j, k;
const int m = GF_M(bch);
unsigned int mask = 0xff, t, rows[16] = {0,};
j = a_log(bch, b);
k = a_log(bch, a);
rows[0] = c;
/* buid linear system to solve X^4+aX^2+bX+c = 0 */
for (i = 0; i < m; i++) {
rows[i+1] = bch->a_pow_tab[4*i]^
(a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^
(b ? bch->a_pow_tab[mod_s(bch, j)] : 0);
j++;
k += 2;
}
/*
* transpose 16x16 matrix before passing it to linear solver
* warning: this code assumes m < 16
*/
for (j = 8; j != 0; j >>= 1, mask ^= (mask << j)) {
for (k = 0; k < 16; k = (k+j+1) & ~j) {
t = ((rows[k] >> j)^rows[k+j]) & mask;
rows[k] ^= (t << j);
rows[k+j] ^= t;
}
}
return solve_linear_system(bch, rows, roots, 4);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ivan Djelic | 265 | 100.00% | 1 | 100.00% |
Total | 265 | 100.00% | 1 | 100.00% |
/*
* compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r))
*/
static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly,
unsigned int *roots)
{
int n = 0;
if (poly->c[0])
/* poly[X] = bX+c with c!=0, root=c/b */
roots[n++