Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
Sunxi Tools
Commits
9febb550
Commit
9febb550
authored
May 31, 2016
by
Olliver Schinagl
Browse files
Merge pull request #54 from bbrezillon/nand-image-builder
Add a tool to generate raw NAND images
parents
d93a631d
e25f6e90
Changes
3
Show whitespace changes
Inline
Side-by-side
Makefile
View file @
9febb550
...
@@ -41,7 +41,7 @@ FEXC_LINKS = bin2fex fex2bin
...
@@ -41,7 +41,7 @@ FEXC_LINKS = bin2fex fex2bin
# Tools which are only useful on the target
# Tools which are only useful on the target
TARGET_TOOLS
=
sunxi-pio
TARGET_TOOLS
=
sunxi-pio
MISC_TOOLS
=
phoenix_info
MISC_TOOLS
=
phoenix_info
sunxi-nand-image-builder
# ARM binaries and images
# ARM binaries and images
# Note: To use this target, set/adjust CROSS_COMPILE and MKSUNXIBOOT if needed
# Note: To use this target, set/adjust CROSS_COMPILE and MKSUNXIBOOT if needed
...
...
README.md
View file @
9febb550
...
@@ -46,6 +46,9 @@ Manipulate PIO register dumps
...
@@ -46,6 +46,9 @@ Manipulate PIO register dumps
### sunxi-nand-part
### sunxi-nand-part
Tool for manipulating Allwinner NAND partition tables
Tool for manipulating Allwinner NAND partition tables
### sunxi-nand-image-builder
Tool used to create raw NAND images (including boot0 images)
### jtag-loop.sunxi
### jtag-loop.sunxi
ARM native boot helper to force the SD port into JTAG and then stop,
ARM native boot helper to force the SD port into JTAG and then stop,
to ease debugging of bootloaders.
to ease debugging of bootloaders.
...
...
nand-image-builder.c
0 → 100644
View file @
9febb550
/*
* 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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#include "portable_endian.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 DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#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
#define cpu_to_be32 htobe32
#define kfree free
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define BCH_PRIMITIVE_POLY 0x5803
struct
image_info
{
int
ecc_strength
;
int
ecc_step_size
;
int
page_size
;
int
oob_size
;
int
usable_page_size
;
int
eraseblock_size
;
int
scramble
;
int
boot0
;
off_t
offset
;
const
char
*
source
;
const
char
*
dest
;
};
/**
* struct bch_control - BCH control structure
* @m: Galois field order
* @n: maximum codeword size in bits (= 2^m-1)
* @t: error correction capability in bits
* @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t)
* @ecc_bytes: ecc max size (m*t bits) in bytes
* @a_pow_tab: Galois field GF(2^m) exponentiation lookup table
* @a_log_tab: Galois field GF(2^m) log lookup table
* @mod8_tab: remainder generator polynomial lookup tables
* @ecc_buf: ecc parity words buffer
* @ecc_buf2: ecc parity words buffer
* @xi_tab: GF(2^m) base for solving degree 2 polynomial roots
* @syn: syndrome buffer
* @cache: log-based polynomial representation buffer
* @elp: error locator polynomial
* @poly_2t: temporary polynomials of degree 2t
*/
struct
bch_control
{
unsigned
int
m
;
unsigned
int
n
;
unsigned
int
t
;
unsigned
int
ecc_bits
;
unsigned
int
ecc_bytes
;
/* private: */
uint16_t
*
a_pow_tab
;
uint16_t
*
a_log_tab
;
uint32_t
*
mod8_tab
;
uint32_t
*
ecc_buf
;
uint32_t
*
ecc_buf2
;
unsigned
int
*
xi_tab
;
unsigned
int
*
syn
;
int
*
cache
;
struct
gf_poly
*
elp
;
struct
gf_poly
*
poly_2t
[
4
];
};
static
int
fls
(
int
x
)
{
int
r
=
32
;
if
(
!
x
)
return
0
;
if
(
!
(
x
&
0xffff0000u
))
{
x
<<=
16
;
r
-=
16
;
}
if
(
!
(
x
&
0xff000000u
))
{
x
<<=
8
;
r
-=
8
;
}
if
(
!
(
x
&
0xf0000000u
))
{
x
<<=
4
;
r
-=
4
;
}
if
(
!
(
x
&
0xc0000000u
))
{
x
<<=
2
;
r
-=
2
;
}
if
(
!
(
x
&
0x80000000u
))
{
x
<<=
1
;
r
-=
1
;
}
return
r
;
}
/*
* 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
);
}
}
/*
* 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
];
}
/*
* 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
);
}
/**
* 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.
*/
static
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
);
}
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
;
}
/*
* 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
;
}
static
inline
int
deg
(
unsigned
int
poly
)
{
/* polynomial degree is the most-significant bit index */
return
fls
(
poly
)
-
1
;
}
/* 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
;
}
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
;
}
static
inline
unsigned
int
a_pow
(
struct
bch_control
*
bch
,
int
i
)
{
return
bch
->
a_pow_tab
[
modulo
(
bch
,
i
)];
}
static
inline
int
a_log
(
struct
bch_control
*
bch
,
unsigned
int
x
)
{
return
bch
->
a_log_tab
[
x
];
}
/*
* generate Galois field lookup tables
*/
static
int
build_gf_tables
(
struct
bch_control
*
bch
,
unsigned
int
poly
)
{
unsigned
int
i
,
x
=
1
;
const
unsigned
int
k
=
1
<<
deg
(
poly
);
/* primitive polynomial must be of degree m */
if
(
k
!=
(
1u
<<
GF_M
(
bch
)))
return
-
1
;
for
(
i
=
0
;
i
<
GF_N
(
bch
);
i
++
)
{
bch
->
a_pow_tab
[
i
]
=
x
;
bch
->
a_log_tab
[
x
]
=
i
;
if
(
i
&&
(
x
==
1
))
/* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
return
-
1
;
x
<<=
1
;
if
(
x
&
k
)
x
^=
poly
;
}
bch
->
a_pow_tab
[
GF_N
(
bch
)]
=
1
;
bch
->
a_log_tab
[
0
]
=
0
;
return
0
;
}
/*
* compute generator polynomial remainder tables for fast encoding
*/
static
void
build_mod8_tables
(
struct
bch_control
*
bch
,
const
uint32_t
*
g
)
{
int
i
,
j
,
b
,
d
;
uint32_t
data
,
hi
,
lo
,
*
tab
;
const
int
l
=
BCH_ECC_WORDS
(
bch
);
const
int
plen
=
DIV_ROUND_UP
(
bch
->
ecc_bits
+
1
,
32
);
const
int
ecclen
=
DIV_ROUND_UP
(
bch
->
ecc_bits
,
32
);
memset
(
bch
->
mod8_tab
,
0
,
4
*
256
*
l
*
sizeof
(
*
bch
->
mod8_tab
));
for
(
i
=
0
;
i
<
256
;
i
++
)
{
/* p(X)=i is a small polynomial of weight <= 8 */
for
(
b
=
0
;
b
<
4
;
b
++
)
{
/* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */
tab
=
bch
->
mod8_tab
+
(
b
*
256
+
i
)
*
l
;
data
=
i
<<
(
8
*
b
);
while
(
data
)
{
d
=
deg
(
data
);
/* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */
data
^=
g
[
0
]
>>
(
31
-
d
);
for
(
j
=
0
;
j
<
ecclen
;
j
++
)
{
hi
=
(
d
<
31
)
?
g
[
j
]
<<
(
d
+
1
)
:
0
;
lo
=
(
j
+
1
<
plen
)
?
g
[
j
+
1
]
>>
(
31
-
d
)
:
0
;
tab
[
j
]
^=
hi
|
lo
;
}
}
}
}
}
/*
* build a base for factoring degree 2 polynomials
*/
static
int
build_deg2_base
(
struct
bch_control
*
bch
)
{
const
int
m
=
GF_M
(
bch
);
int
i
,
j
,
r
;
unsigned
int
sum
,
x
,
y
,
remaining
,
ak
=
0
,
xi
[
m
];
/* find k s.t. Tr(a^k) = 1 and 0 <= k < m */
for
(
i
=
0
;
i
<
m
;
i
++
)
{
for
(
j
=
0
,
sum
=
0
;
j
<
m
;
j
++
)
sum
^=
a_pow
(
bch
,
i
*
(
1
<<
j
));
if
(
sum
)
{
ak
=
bch
->
a_pow_tab
[
i
];
break
;
}
}
/* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */
remaining
=
m
;
memset
(
xi
,
0
,
sizeof
(
xi
));
for
(
x
=
0
;
(
x
<=
GF_N
(
bch
))
&&
remaining
;
x
++
)
{
y
=
gf_sqr
(
bch
,
x
)
^
x
;
for
(
i
=
0
;
i
<
2
;
i
++
)
{
r
=
a_log
(
bch
,
y
);
if
(
y
&&
(
r
<
m
)
&&
!
xi
[
r
])
{
bch
->
xi_tab
[
r
]
=
x
;
xi
[
r
]
=
1
;
remaining
--
;
dbg
(
"x%d = %x
\n
"
,
r
,
x
);
break
;
}
y
^=
ak
;
}
}
/* should not happen but check anyway */
return
remaining
?
-
1
:
0
;
}
static
void
*
bch_alloc
(
size_t
size
,
int
*
err
)
{
void
*
ptr
;
ptr
=
malloc
(
size
);
if
(
ptr
==
NULL
)
*
err
=
1
;
return
ptr
;
}
/*
* compute generator polynomial for given (m,t) parameters.
*/
static
uint32_t
*
compute_generator_polynomial
(
struct
bch_control
*
bch
)
{
const
unsigned
int
m
=
GF_M
(
bch
);
const
unsigned
int
t
=
GF_T
(
bch
);
int
n
,
err
=
0
;
unsigned
int
i
,
j
,
nbits
,
r
,
word
,
*
roots
;
struct
gf_poly
*
g
;
uint32_t
*
genpoly
;
g
=
bch_alloc
(
GF_POLY_SZ
(
m
*
t
),
&
err
);
roots
=
bch_alloc
((
bch
->
n
+
1
)
*
sizeof
(
*
roots
),
&
err
);
genpoly
=
bch_alloc
(
DIV_ROUND_UP
(
m
*
t
+
1
,
32
)
*
sizeof
(
*
genpoly
),
&
err
);
if
(
err
)
{
kfree
(
genpoly
);
genpoly
=
NULL
;
goto
finish
;
}
/* enumerate all roots of g(X) */
memset
(
roots
,
0
,
(
bch
->
n
+
1
)
*
sizeof
(
*
roots
));
for
(
i
=
0
;
i
<
t
;
i
++
)
{
for
(
j
=
0
,
r
=
2
*
i
+
1
;
j
<
m
;
j
++
)
{
roots
[
r
]
=
1
;
r
=
mod_s
(
bch
,
2
*
r
);
}
}
/* build generator polynomial g(X) */
g
->
deg
=
0
;
g
->
c
[
0
]
=
1
;
for
(
i
=
0
;
i
<
GF_N
(
bch
);
i
++
)
{
if
(
roots
[
i
])
{
/* multiply g(X) by (X+root) */
r
=
bch
->
a_pow_tab
[
i
];
g
->
c
[
g
->
deg
+
1
]
=
1
;
for
(
j
=
g
->
deg
;
j
>
0
;
j
--
)
g
->
c
[
j
]
=
gf_mul
(
bch
,
g
->
c
[
j
],
r
)
^
g
->
c
[
j
-
1
];
g
->
c
[
0
]
=
gf_mul
(
bch
,
g
->
c
[
0
],
r
);
g
->
deg
++
;
}
}
/* store left-justified binary representation of g(X) */
n
=
g
->
deg
+
1
;
i
=
0
;
while
(
n
>
0
)
{
nbits
=
(
n
>
32
)
?
32
:
n
;
for
(
j
=
0
,
word
=
0
;
j
<
nbits
;
j
++
)
{
if
(
g
->
c
[
n
-
1
-
j
])
word
|=
1u
<<
(
31
-
j
);
}
genpoly
[
i
++
]
=
word
;
n
-=
nbits
;
}
bch
->
ecc_bits
=
g
->
deg
;
finish:
kfree
(
g
);
kfree
(
roots
);
return
genpoly
;
}
/**
* free_bch - free the BCH control structure
* @bch: BCH control structure to release
*/
static
void
free_bch
(
struct
bch_control
*
bch
)
{
unsigned
int
i
;
if
(
bch
)
{
kfree
(
bch
->
a_pow_tab
);
kfree
(
bch
->
a_log_tab
);
kfree
(
bch
->
mod8_tab
);
kfree
(
bch
->
ecc_buf
);
kfree
(
bch
->
ecc_buf2
);
kfree
(
bch
->
xi_tab
);
kfree
(
bch
->
syn
);
kfree
(
bch
->
cache
);
kfree
(
bch
->
elp
);
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
bch
->
poly_2t
);
i
++
)
kfree
(
bch
->
poly_2t
[
i
]);
kfree
(
bch
);
}
}
/**
* init_bch - initialize a BCH encoder/decoder
* @m: Galois field order, should be in the range 5-15
* @t: maximum error correction capability, in bits
* @prim_poly: user-provided primitive polynomial (or 0 to use default)
*
* Returns:
* a newly allocated BCH control structure if successful, NULL otherwise
*
* This initialization can take some time, as lookup tables are built for fast
* encoding/decoding; make sure not to call this function from a time critical
* path. Usually, init_bch() should be called on module/driver init and
* free_bch() should be called to release memory on exit.
*
* You may provide your own primitive polynomial of degree @m in argument
* @prim_poly, or let init_bch() use its default polynomial.
*
* Once init_bch() has successfully returned a pointer to a newly allocated
* BCH control structure, ecc length in bytes is given by member @ecc_bytes of
* the structure.
*/
static
struct
bch_control
*
init_bch
(
int
m
,
int
t
,
unsigned
int
prim_poly
)
{
int
err
=
0
;
unsigned
int
i
,
words
;
uint32_t
*
genpoly
;
struct
bch_control
*
bch
=
NULL
;
const
int
min_m
=
5
;
const
int
max_m
=
15
;
/* default primitive polynomials */
static
const
unsigned
int
prim_poly_tab
[]
=
{
0x25
,
0x43
,
0x83
,
0x11d
,
0x211
,
0x409
,
0x805
,
0x1053
,
0x201b
,
0x402b
,
0x8003
,
};
#if defined(CONFIG_BCH_CONST_PARAMS)
if
((
m
!=
(
CONFIG_BCH_CONST_M
))
||
(
t
!=
(
CONFIG_BCH_CONST_T
)))
{
printk
(
KERN_ERR
"bch encoder/decoder was configured to support "
"parameters m=%d, t=%d only!
\n
"
,
CONFIG_BCH_CONST_M
,
CONFIG_BCH_CONST_T
);
goto
fail
;
}
#endif
if
((
m
<
min_m
)
||
(
m
>
max_m
))
/*
* values of m greater than 15 are not currently supported;
* supporting m > 15 would require changing table base type
* (uint16_t) and a small patch in matrix transposition
*/
goto
fail
;
/* sanity checks */
if
((
t
<
1
)
||
(
m
*
t
>=
((
1
<<
m
)
-
1
)))
/* invalid t value */
goto
fail
;
/* select a primitive polynomial for generating GF(2^m) */
if
(
prim_poly
==
0
)
prim_poly
=
prim_poly_tab
[
m
-
min_m
];
bch
=
malloc
(
sizeof
(
*
bch
));
if
(
bch
==
NULL
)
goto
fail
;
memset
(
bch
,
0
,
sizeof
(
*
bch
));
bch
->
m
=
m
;
bch
->
t
=
t
;
bch
->
n
=
(
1
<<
m
)
-
1
;
words
=
DIV_ROUND_UP
(
m
*
t
,
32
);
bch
->
ecc_bytes
=
DIV_ROUND_UP
(
m
*
t
,
8
);
bch
->
a_pow_tab
=
bch_alloc
((
1
+
bch
->
n
)
*
sizeof
(
*
bch
->
a_pow_tab
),
&
err
);
bch
->
a_log_tab
=
bch_alloc
((
1
+
bch
->
n
)
*
sizeof
(
*
bch
->
a_log_tab
),
&
err
);
bch
->
mod8_tab
=
bch_alloc
(
words
*
1024
*
sizeof
(
*
bch
->
mod8_tab
),
&
err
);
bch
->
ecc_buf
=
bch_alloc
(
words
*
sizeof
(
*
bch
->
ecc_buf
),
&
err
);
bch
->
ecc_buf2
=
bch_alloc
(
words
*
sizeof
(
*
bch
->
ecc_buf2
),
&
err
);
bch
->
xi_tab
=
bch_alloc
(
m
*
sizeof
(
*
bch
->
xi_tab
),
&
err
);
bch
->
syn
=
bch_alloc
(
2
*
t
*
sizeof
(
*
bch
->
syn
),
&
err
);
bch
->
cache
=
bch_alloc
(
2
*
t
*
sizeof
(
*
bch
->
cache
),
&
err
);
bch
->
elp
=
bch_alloc
((
t
+
1
)
*
sizeof
(
struct
gf_poly_deg1
),
&
err
);
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
bch
->
poly_2t
);
i
++
)
bch
->
poly_2t
[
i
]
=
bch_alloc
(
GF_POLY_SZ
(
2
*
t
),
&
err
);
if
(
err
)
goto
fail
;
err
=
build_gf_tables
(
bch
,
prim_poly
);
if
(
err
)
goto
fail
;
/* use generator polynomial for computing encoding tables */
genpoly
=
compute_generator_polynomial
(
bch
);
if
(
genpoly
==
NULL
)
goto
fail
;
build_mod8_tables
(
bch
,
genpoly
);
kfree
(
genpoly
);
err
=
build_deg2_base
(
bch
);
if
(
err
)
goto
fail
;
return
bch
;
fail:
free_bch
(
bch
);
return
NULL
;
}
static
void
swap_bits
(
uint8_t
*
buf
,
int
len
)
{
int
i
,
j
;
for
(
j
=
0
;
j
<
len
;
j
++
)
{
uint8_t
byte
=
buf
[
j
];
buf
[
j
]
=
0
;
for
(
i
=
0
;
i
<
8
;
i
++
)
{
if
(
byte
&
(
1
<<
i
))
buf
[
j
]
|=
(
1
<<
(
7
-
i
));
}
}
}
static
uint16_t
lfsr_step
(
uint16_t
state
,
int
count
)
{
state
&=
0x7fff
;
while
(
count
--
)
state
=
((
state
>>
1
)
|
((((
state
>>
0
)
^
(
state
>>
1
))
&
1
)
<<
14
))
&
0x7fff
;
return
state
;
}
static
uint16_t
default_scrambler_seeds
[]
=
{
0x2b75
,
0x0bd0
,
0x5ca3
,
0x62d1
,
0x1c93
,
0x07e9
,
0x2162
,
0x3a72
,
0x0d67
,
0x67f9
,
0x1be7
,
0x077d
,
0x032f
,
0x0dac
,
0x2716
,
0x2436
,
0x7922
,
0x1510
,
0x3860
,
0x5287
,
0x480f
,
0x4252
,
0x1789
,
0x5a2d
,
0x2a49
,
0x5e10
,
0x437f
,
0x4b4e
,
0x2f45
,
0x216e
,
0x5cb7
,
0x7130
,
0x2a3f
,
0x60e4
,
0x4dc9
,
0x0ef0
,
0x0f52
,
0x1bb9
,
0x6211
,
0x7a56
,
0x226d
,
0x4ea7
,
0x6f36
,
0x3692
,
0x38bf
,
0x0c62
,
0x05eb
,
0x4c55
,
0x60f4
,
0x728c
,
0x3b6f
,
0x2037
,
0x7f69
,
0x0936
,
0x651a
,
0x4ceb
,
0x6218
,
0x79f3
,
0x383f
,
0x18d9
,
0x4f05
,
0x5c82
,
0x2912
,
0x6f17
,
0x6856
,
0x5938
,
0x1007
,
0x61ab
,
0x3e7f
,
0x57c2
,
0x542f
,
0x4f62
,
0x7454
,
0x2eac
,
0x7739
,
0x42d4
,
0x2f90
,
0x435a
,
0x2e52
,
0x2064
,
0x637c
,
0x66ad
,
0x2c90
,
0x0bad
,
0x759c
,
0x0029
,
0x0986
,
0x7126
,
0x1ca7
,
0x1605
,
0x386a
,
0x27f5
,
0x1380
,
0x6d75
,
0x24c3
,
0x0f8e
,
0x2b7a
,
0x1418
,
0x1fd1
,
0x7dc1
,
0x2d8e
,
0x43af
,
0x2267
,
0x7da3
,
0x4e3d
,
0x1338
,
0x50db
,
0x454d
,
0x764d
,
0x40a3
,
0x42e6
,
0x262b
,
0x2d2e
,
0x1aea
,
0x2e17
,
0x173d
,
0x3a6e
,
0x71bf
,
0x25f9
,
0x0a5d
,
0x7c57
,
0x0fbe
,
0x46ce
,
0x4939
,
0x6b17
,
0x37bb
,
0x3e91
,
0x76db
,
};
static
uint16_t
brom_scrambler_seeds
[]
=
{
0x4a80
};
static
void
scramble
(
const
struct
image_info
*
info
,
int
page
,
uint8_t
*
data
,
int
datalen
)
{
uint16_t
state
;
int
i
;
/* Boot0 is always scrambled no matter the command line option. */
if
(
info
->
boot0
)
{
state
=
brom_scrambler_seeds
[
0
];
}
else
{
unsigned
seedmod
=
info
->
eraseblock_size
/
info
->
page_size
;
/* Bail out earlier if the user didn't ask for scrambling. */
if
(
!
info
->
scramble
)
return
;
if
(
seedmod
>
ARRAY_SIZE
(
default_scrambler_seeds
))
seedmod
=
ARRAY_SIZE
(
default_scrambler_seeds
);
state
=
default_scrambler_seeds
[
page
%
seedmod
];
}
/* Prepare the initial state... */
state
=
lfsr_step
(
state
,
15
);
/* and start scrambling data. */
for
(
i
=
0
;
i
<
datalen
;
i
++
)
{
data
[
i
]
^=
state
;
state
=
lfsr_step
(
state
,
8
);
}
}
static
int
write_page
(
const
struct
image_info
*
info
,
uint8_t
*
buffer
,
FILE
*
src
,
FILE
*
rnd
,
FILE
*
dst
,
struct
bch_control
*
bch
,
int
page
)
{
int
steps
=
info
->
usable_page_size
/
info
->
ecc_step_size
;
int
eccbytes
=
DIV_ROUND_UP
(
info
->
ecc_strength
*
14
,
8
);
off_t
pos
=
ftell
(
dst
);
size_t
pad
,
cnt
;
int
i
;
if
(
eccbytes
%
2
)
eccbytes
++
;
memset
(
buffer
,
0xff
,
info
->
page_size
+
info
->
oob_size
);
cnt
=
fread
(
buffer
,
1
,
info
->
usable_page_size
,
src
);
if
(
!
cnt
)
{
if
(
!
feof
(
src
))
{
fprintf
(
stderr
,
"Failed to read data from the source
\n
"
);
return
-
1
;
}
else
{
return
0
;
}
}
fwrite
(
buffer
,
info
->
page_size
+
info
->
oob_size
,
1
,
dst
);
for
(
i
=
0
;
i
<
info
->
usable_page_size
;
i
++
)
{
if
(
buffer
[
i
]
!=
0xff
)
break
;
}
/* We leave empty pages at 0xff. */
if
(
i
==
info
->
usable_page_size
)
return
0
;
/* Restore the source pointer to read it again. */
fseek
(
src
,
-
cnt
,
SEEK_CUR
);
/* Randomize unused space if scrambling is required. */
if
(
info
->
scramble
)
{
int
offs
;
if
(
info
->
boot0
)
{
offs
=
steps
*
(
info
->
ecc_step_size
+
eccbytes
+
4
);
cnt
=
info
->
page_size
+
info
->
oob_size
-
offs
;
fread
(
buffer
+
offs
,
1
,
cnt
,
rnd
);
}
else
{
offs
=
info
->
page_size
+
(
steps
*
(
eccbytes
+
4
));
cnt
=
info
->
page_size
+
info
->
oob_size
-
offs
;
memset
(
buffer
+
offs
,
0xff
,
cnt
);
scramble
(
info
,
page
,
buffer
+
offs
,
cnt
);
}
fseek
(
dst
,
pos
+
offs
,
SEEK_SET
);
fwrite
(
buffer
+
offs
,
cnt
,
1
,
dst
);
}
for
(
i
=
0
;
i
<
steps
;
i
++
)
{
int
ecc_offs
,
data_offs
;
uint8_t
*
ecc
;
memset
(
buffer
,
0xff
,
info
->
ecc_step_size
+
eccbytes
+
4
);
ecc
=
buffer
+
info
->
ecc_step_size
+
4
;
if
(
info
->
boot0
)
{
data_offs
=
i
*
(
info
->
ecc_step_size
+
eccbytes
+
4
);
ecc_offs
=
data_offs
+
info
->
ecc_step_size
+
4
;
}
else
{
data_offs
=
i
*
info
->
ecc_step_size
;
ecc_offs
=
info
->
page_size
+
4
+
(
i
*
(
eccbytes
+
4
));
}
cnt
=
fread
(
buffer
,
1
,
info
->
ecc_step_size
,
src
);
if
(
!
cnt
&&
!
feof
(
src
))
{
fprintf
(
stderr
,
"Failed to read data from the source
\n
"
);
return
-
1
;
}
pad
=
info
->
ecc_step_size
-
cnt
;
if
(
pad
)
{
if
(
info
->
scramble
&&
info
->
boot0
)
fread
(
buffer
+
cnt
,
1
,
pad
,
rnd
);
else
memset
(
buffer
+
cnt
,
0xff
,
pad
);
}
memset
(
ecc
,
0
,
eccbytes
);
swap_bits
(
buffer
,
info
->
ecc_step_size
+
4
);
encode_bch
(
bch
,
buffer
,
info
->
ecc_step_size
+
4
,
ecc
);
swap_bits
(
buffer
,
info
->
ecc_step_size
+
4
);
swap_bits
(
ecc
,
eccbytes
);
scramble
(
info
,
page
,
buffer
,
info
->
ecc_step_size
+
4
+
eccbytes
);
fseek
(
dst
,
pos
+
data_offs
,
SEEK_SET
);
fwrite
(
buffer
,
info
->
ecc_step_size
,
1
,
dst
);
fseek
(
dst
,
pos
+
ecc_offs
-
4
,
SEEK_SET
);
fwrite
(
ecc
-
4
,
eccbytes
+
4
,
1
,
dst
);
}
/* Fix BBM. */
fseek
(
dst
,
pos
+
info
->
page_size
,
SEEK_SET
);
memset
(
buffer
,
0xff
,
2
);
fwrite
(
buffer
,
2
,
1
,
dst
);
/* Make dst pointer point to the next page. */
fseek
(
dst
,
pos
+
info
->
page_size
+
info
->
oob_size
,
SEEK_SET
);
return
0
;
}
static
int
create_image
(
const
struct
image_info
*
info
)
{
off_t
page
=
info
->
offset
/
info
->
page_size
;
struct
bch_control
*
bch
;
FILE
*
src
,
*
dst
,
*
rnd
;
uint8_t
*
buffer
;
bch
=
init_bch
(
14
,
info
->
ecc_strength
,
BCH_PRIMITIVE_POLY
);
if
(
!
bch
)
{
fprintf
(
stderr
,
"Failed to init the BCH engine
\n
"
);
return
-
1
;
}
buffer
=
malloc
(
info
->
page_size
+
info
->
oob_size
);
if
(
!
buffer
)
{
fprintf
(
stderr
,
"Failed to allocate the NAND page buffer
\n
"
);
return
-
1
;
}
memset
(
buffer
,
0xff
,
info
->
page_size
+
info
->
oob_size
);
src
=
fopen
(
info
->
source
,
"r"
);
if
(
!
src
)
{
fprintf
(
stderr
,
"Failed to open source file (%s)
\n
"
,
info
->
source
);
return
-
1
;
}
dst
=
fopen
(
info
->
dest
,
"w"
);
if
(
!
dst
)
{
fprintf
(
stderr
,
"Failed to open dest file (%s)
\n
"
,
info
->
dest
);
return
-
1
;
}
rnd
=
fopen
(
"/dev/urandom"
,
"r"
);
if
(
!
rnd
)
{
fprintf
(
stderr
,
"Failed to open /dev/urandom
\n
"
);
return
-
1
;
}
while
(
!
feof
(
src
))
{
int
ret
;
ret
=
write_page
(
info
,
buffer
,
src
,
rnd
,
dst
,
bch
,
page
++
);
if
(
ret
)
return
ret
;
}
return
0
;
}
static
void
display_help
(
int
status
)
{
fprintf
(
status
==
EXIT_SUCCESS
?
stdout
:
stderr
,
"Usage: sunxi-nand-image-builder [OPTIONS] source-image output-image
\n
"
"Creates a raw NAND image that can be read by the sunxi NAND controller.
\n
"
"
\n
"
"-h --help Display this help and exit
\n
"
"-c <strength>/<step> --ecc=<strength>/<step> ECC config
\n
"
" Valid strengths: 16, 24, 28, 32, 40, 48, 56, 60 and 64
\n
"
" Valid steps: 512 and 1024
\n
"
"-p <size> --page-size=<size> Page size
\n
"
"-o <size> --oob-size=<size> OOB size
\n
"
"-u <size> --usable-page-size=<size> Usable page size. Only needed for boot0 mode
\n
"
"-e <size> --eraseblock-size=<size> Erase block size
\n
"
"-b --boot0 Build a boot0 image.
\n
"
"-s --scramble Scramble data
\n
"
"-a <offset> --address Where the image will be programmed.
\n
"
" This option is only required for non boot0 images that are meant to be programmed at a non eraseblock aligned offset.
\n
"
"
\n
"
);
exit
(
status
);
}
static
int
check_image_info
(
struct
image_info
*
info
)
{
static
int
valid_ecc_strengths
[]
=
{
16
,
24
,
28
,
32
,
40
,
48
,
56
,
60
,
64
};
int
eccbytes
,
eccsteps
;
unsigned
i
;
if
(
!
info
->
page_size
||
!
info
->
oob_size
||
!
info
->
eraseblock_size
||
!
info
->
usable_page_size
)
return
-
EINVAL
;
if
(
info
->
ecc_step_size
!=
512
&&
info
->
ecc_step_size
!=
1024
)
return
-
EINVAL
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
valid_ecc_strengths
);
i
++
)
{
if
(
valid_ecc_strengths
[
i
]
==
info
->
ecc_strength
)
break
;
}
if
(
i
==
ARRAY_SIZE
(
valid_ecc_strengths
))
return
-
EINVAL
;
eccbytes
=
DIV_ROUND_UP
(
info
->
ecc_strength
*
14
,
8
);
if
(
eccbytes
%
2
)
eccbytes
++
;
eccbytes
+=
4
;
eccsteps
=
info
->
usable_page_size
/
info
->
ecc_step_size
;
if
(
info
->
page_size
+
info
->
oob_size
<
info
->
usable_page_size
+
(
eccsteps
*
(
eccbytes
)))
return
-
EINVAL
;
return
0
;
}
int
main
(
int
argc
,
char
**
argv
)
{
struct
image_info
info
;
memset
(
&
info
,
0
,
sizeof
(
info
));
/*
* Process user arguments
*/
for
(;;)
{
int
option_index
=
0
;
char
*
endptr
=
NULL
;
static
const
struct
option
long_options
[]
=
{
{
"help"
,
no_argument
,
0
,
0
},
{
"ecc"
,
required_argument
,
0
,
'c'
},
{
"page-size"
,
required_argument
,
0
,
'p'
},
{
"oob-size"
,
required_argument
,
0
,
'o'
},
{
"usable-page-size"
,
required_argument
,
0
,
'u'
},
{
"eraseblock-size"
,
required_argument
,
0
,
'e'
},
{
"boot0"
,
no_argument
,
0
,
'b'
},
{
"scramble"
,
no_argument
,
0
,
's'
},
{
"address"
,
required_argument
,
0
,
'a'
},
{
0
,
0
,
0
,
0
},
};
int
c
=
getopt_long
(
argc
,
argv
,
"c:p:o:u:e:ba:s"
,
long_options
,
&
option_index
);
if
(
c
==
EOF
)
break
;
switch
(
c
)
{
case
'h'
:
display_help
(
0
);
break
;
case
's'
:
info
.
scramble
=
1
;
break
;
case
'c'
:
info
.
ecc_strength
=
strtol
(
optarg
,
&
endptr
,
0
);
if
(
endptr
||
*
endptr
==
'/'
)
info
.
ecc_step_size
=
strtol
(
endptr
+
1
,
NULL
,
0
);
break
;
case
'p'
:
info
.
page_size
=
strtol
(
optarg
,
NULL
,
0
);
break
;
case
'o'
:
info
.
oob_size
=
strtol
(
optarg
,
NULL
,
0
);
break
;
case
'u'
:
info
.
usable_page_size
=
strtol
(
optarg
,
NULL
,
0
);
break
;
case
'e'
:
info
.
eraseblock_size
=
strtol
(
optarg
,
NULL
,
0
);
break
;
case
'b'
:
info
.
boot0
=
1
;
break
;
case
'a'
:
info
.
offset
=
strtoull
(
optarg
,
NULL
,
0
);
break
;
case
'?'
:
display_help
(
-
1
);
break
;
}
}
if
((
argc
-
optind
)
!=
2
)
display_help
(
-
1
);
info
.
source
=
argv
[
optind
];
info
.
dest
=
argv
[
optind
+
1
];
if
(
!
info
.
boot0
)
{
info
.
usable_page_size
=
info
.
page_size
;
}
else
if
(
!
info
.
usable_page_size
)
{
if
(
info
.
page_size
>
8192
)
info
.
usable_page_size
=
8192
;
else
if
(
info
.
page_size
>
4096
)
info
.
usable_page_size
=
4096
;
else
info
.
usable_page_size
=
1024
;
}
if
(
check_image_info
(
&
info
))
display_help
(
-
1
);
return
create_image
(
&
info
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment