1594b4cc7SMaxime Ripard /*
2594b4cc7SMaxime Ripard * Allwinner NAND randomizer and image builder implementation:
3594b4cc7SMaxime Ripard *
4594b4cc7SMaxime Ripard * Copyright © 2016 NextThing Co.
5594b4cc7SMaxime Ripard * Copyright © 2016 Free Electrons
6594b4cc7SMaxime Ripard *
7594b4cc7SMaxime Ripard * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
8594b4cc7SMaxime Ripard *
9594b4cc7SMaxime Ripard */
10594b4cc7SMaxime Ripard
11594b4cc7SMaxime Ripard #include <linux/bch.h>
12594b4cc7SMaxime Ripard
13594b4cc7SMaxime Ripard #include <getopt.h>
14594b4cc7SMaxime Ripard #include <version.h>
15594b4cc7SMaxime Ripard
16594b4cc7SMaxime Ripard #define BCH_PRIMITIVE_POLY 0x5803
17594b4cc7SMaxime Ripard
18594b4cc7SMaxime Ripard #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
19594b4cc7SMaxime Ripard #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
20594b4cc7SMaxime Ripard
21594b4cc7SMaxime Ripard struct image_info {
22594b4cc7SMaxime Ripard int ecc_strength;
23594b4cc7SMaxime Ripard int ecc_step_size;
24594b4cc7SMaxime Ripard int page_size;
25594b4cc7SMaxime Ripard int oob_size;
26594b4cc7SMaxime Ripard int usable_page_size;
27594b4cc7SMaxime Ripard int eraseblock_size;
28594b4cc7SMaxime Ripard int scramble;
29594b4cc7SMaxime Ripard int boot0;
30594b4cc7SMaxime Ripard off_t offset;
31594b4cc7SMaxime Ripard const char *source;
32594b4cc7SMaxime Ripard const char *dest;
33594b4cc7SMaxime Ripard };
34594b4cc7SMaxime Ripard
swap_bits(uint8_t * buf,int len)35594b4cc7SMaxime Ripard static void swap_bits(uint8_t *buf, int len)
36594b4cc7SMaxime Ripard {
37594b4cc7SMaxime Ripard int i, j;
38594b4cc7SMaxime Ripard
39594b4cc7SMaxime Ripard for (j = 0; j < len; j++) {
40594b4cc7SMaxime Ripard uint8_t byte = buf[j];
41594b4cc7SMaxime Ripard
42594b4cc7SMaxime Ripard buf[j] = 0;
43594b4cc7SMaxime Ripard for (i = 0; i < 8; i++) {
44594b4cc7SMaxime Ripard if (byte & (1 << i))
45594b4cc7SMaxime Ripard buf[j] |= (1 << (7 - i));
46594b4cc7SMaxime Ripard }
47594b4cc7SMaxime Ripard }
48594b4cc7SMaxime Ripard }
49594b4cc7SMaxime Ripard
lfsr_step(uint16_t state,int count)50594b4cc7SMaxime Ripard static uint16_t lfsr_step(uint16_t state, int count)
51594b4cc7SMaxime Ripard {
52594b4cc7SMaxime Ripard state &= 0x7fff;
53594b4cc7SMaxime Ripard while (count--)
54594b4cc7SMaxime Ripard state = ((state >> 1) |
55594b4cc7SMaxime Ripard ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff;
56594b4cc7SMaxime Ripard
57594b4cc7SMaxime Ripard return state;
58594b4cc7SMaxime Ripard }
59594b4cc7SMaxime Ripard
60594b4cc7SMaxime Ripard static uint16_t default_scrambler_seeds[] = {
61594b4cc7SMaxime Ripard 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
62594b4cc7SMaxime Ripard 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
63594b4cc7SMaxime Ripard 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
64594b4cc7SMaxime Ripard 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
65594b4cc7SMaxime Ripard 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
66594b4cc7SMaxime Ripard 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
67594b4cc7SMaxime Ripard 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
68594b4cc7SMaxime Ripard 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
69594b4cc7SMaxime Ripard 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
70594b4cc7SMaxime Ripard 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
71594b4cc7SMaxime Ripard 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
72594b4cc7SMaxime Ripard 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
73594b4cc7SMaxime Ripard 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
74594b4cc7SMaxime Ripard 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
75594b4cc7SMaxime Ripard 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
76594b4cc7SMaxime Ripard 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
77594b4cc7SMaxime Ripard };
78594b4cc7SMaxime Ripard
79594b4cc7SMaxime Ripard static uint16_t brom_scrambler_seeds[] = { 0x4a80 };
80594b4cc7SMaxime Ripard
scramble(const struct image_info * info,int page,uint8_t * data,int datalen)81594b4cc7SMaxime Ripard static void scramble(const struct image_info *info,
82594b4cc7SMaxime Ripard int page, uint8_t *data, int datalen)
83594b4cc7SMaxime Ripard {
84594b4cc7SMaxime Ripard uint16_t state;
85594b4cc7SMaxime Ripard int i;
86594b4cc7SMaxime Ripard
87594b4cc7SMaxime Ripard /* Boot0 is always scrambled no matter the command line option. */
88594b4cc7SMaxime Ripard if (info->boot0) {
89594b4cc7SMaxime Ripard state = brom_scrambler_seeds[0];
90594b4cc7SMaxime Ripard } else {
91594b4cc7SMaxime Ripard unsigned seedmod = info->eraseblock_size / info->page_size;
92594b4cc7SMaxime Ripard
93594b4cc7SMaxime Ripard /* Bail out earlier if the user didn't ask for scrambling. */
94594b4cc7SMaxime Ripard if (!info->scramble)
95594b4cc7SMaxime Ripard return;
96594b4cc7SMaxime Ripard
97594b4cc7SMaxime Ripard if (seedmod > ARRAY_SIZE(default_scrambler_seeds))
98594b4cc7SMaxime Ripard seedmod = ARRAY_SIZE(default_scrambler_seeds);
99594b4cc7SMaxime Ripard
100594b4cc7SMaxime Ripard state = default_scrambler_seeds[page % seedmod];
101594b4cc7SMaxime Ripard }
102594b4cc7SMaxime Ripard
103594b4cc7SMaxime Ripard /* Prepare the initial state... */
104594b4cc7SMaxime Ripard state = lfsr_step(state, 15);
105594b4cc7SMaxime Ripard
106594b4cc7SMaxime Ripard /* and start scrambling data. */
107594b4cc7SMaxime Ripard for (i = 0; i < datalen; i++) {
108594b4cc7SMaxime Ripard data[i] ^= state;
109594b4cc7SMaxime Ripard state = lfsr_step(state, 8);
110594b4cc7SMaxime Ripard }
111594b4cc7SMaxime Ripard }
112594b4cc7SMaxime Ripard
write_page(const struct image_info * info,uint8_t * buffer,FILE * src,FILE * rnd,FILE * dst,struct bch_control * bch,int page)113594b4cc7SMaxime Ripard static int write_page(const struct image_info *info, uint8_t *buffer,
114594b4cc7SMaxime Ripard FILE *src, FILE *rnd, FILE *dst,
115594b4cc7SMaxime Ripard struct bch_control *bch, int page)
116594b4cc7SMaxime Ripard {
117594b4cc7SMaxime Ripard int steps = info->usable_page_size / info->ecc_step_size;
118594b4cc7SMaxime Ripard int eccbytes = DIV_ROUND_UP(info->ecc_strength * 14, 8);
119594b4cc7SMaxime Ripard off_t pos = ftell(dst);
120594b4cc7SMaxime Ripard size_t pad, cnt;
121594b4cc7SMaxime Ripard int i;
122594b4cc7SMaxime Ripard
123594b4cc7SMaxime Ripard if (eccbytes % 2)
124594b4cc7SMaxime Ripard eccbytes++;
125594b4cc7SMaxime Ripard
126594b4cc7SMaxime Ripard memset(buffer, 0xff, info->page_size + info->oob_size);
127594b4cc7SMaxime Ripard cnt = fread(buffer, 1, info->usable_page_size, src);
128594b4cc7SMaxime Ripard if (!cnt) {
129594b4cc7SMaxime Ripard if (!feof(src)) {
130594b4cc7SMaxime Ripard fprintf(stderr,
131594b4cc7SMaxime Ripard "Failed to read data from the source\n");
132594b4cc7SMaxime Ripard return -1;
133594b4cc7SMaxime Ripard } else {
134594b4cc7SMaxime Ripard return 0;
135594b4cc7SMaxime Ripard }
136594b4cc7SMaxime Ripard }
137594b4cc7SMaxime Ripard
138594b4cc7SMaxime Ripard fwrite(buffer, info->page_size + info->oob_size, 1, dst);
139594b4cc7SMaxime Ripard
140594b4cc7SMaxime Ripard for (i = 0; i < info->usable_page_size; i++) {
141594b4cc7SMaxime Ripard if (buffer[i] != 0xff)
142594b4cc7SMaxime Ripard break;
143594b4cc7SMaxime Ripard }
144594b4cc7SMaxime Ripard
145594b4cc7SMaxime Ripard /* We leave empty pages at 0xff. */
146594b4cc7SMaxime Ripard if (i == info->usable_page_size)
147594b4cc7SMaxime Ripard return 0;
148594b4cc7SMaxime Ripard
149594b4cc7SMaxime Ripard /* Restore the source pointer to read it again. */
150594b4cc7SMaxime Ripard fseek(src, -cnt, SEEK_CUR);
151594b4cc7SMaxime Ripard
152594b4cc7SMaxime Ripard /* Randomize unused space if scrambling is required. */
153594b4cc7SMaxime Ripard if (info->scramble) {
154594b4cc7SMaxime Ripard int offs;
155594b4cc7SMaxime Ripard
156594b4cc7SMaxime Ripard if (info->boot0) {
157594b4cc7SMaxime Ripard size_t ret;
158594b4cc7SMaxime Ripard
159594b4cc7SMaxime Ripard offs = steps * (info->ecc_step_size + eccbytes + 4);
160594b4cc7SMaxime Ripard cnt = info->page_size + info->oob_size - offs;
161594b4cc7SMaxime Ripard ret = fread(buffer + offs, 1, cnt, rnd);
162594b4cc7SMaxime Ripard if (!ret && !feof(rnd)) {
163594b4cc7SMaxime Ripard fprintf(stderr,
164594b4cc7SMaxime Ripard "Failed to read random data\n");
165594b4cc7SMaxime Ripard return -1;
166594b4cc7SMaxime Ripard }
167594b4cc7SMaxime Ripard } else {
168594b4cc7SMaxime Ripard offs = info->page_size + (steps * (eccbytes + 4));
169594b4cc7SMaxime Ripard cnt = info->page_size + info->oob_size - offs;
170594b4cc7SMaxime Ripard memset(buffer + offs, 0xff, cnt);
171594b4cc7SMaxime Ripard scramble(info, page, buffer + offs, cnt);
172594b4cc7SMaxime Ripard }
173594b4cc7SMaxime Ripard fseek(dst, pos + offs, SEEK_SET);
174594b4cc7SMaxime Ripard fwrite(buffer + offs, cnt, 1, dst);
175594b4cc7SMaxime Ripard }
176594b4cc7SMaxime Ripard
177594b4cc7SMaxime Ripard for (i = 0; i < steps; i++) {
178594b4cc7SMaxime Ripard int ecc_offs, data_offs;
179594b4cc7SMaxime Ripard uint8_t *ecc;
180594b4cc7SMaxime Ripard
181594b4cc7SMaxime Ripard memset(buffer, 0xff, info->ecc_step_size + eccbytes + 4);
182594b4cc7SMaxime Ripard ecc = buffer + info->ecc_step_size + 4;
183594b4cc7SMaxime Ripard if (info->boot0) {
184594b4cc7SMaxime Ripard data_offs = i * (info->ecc_step_size + eccbytes + 4);
185594b4cc7SMaxime Ripard ecc_offs = data_offs + info->ecc_step_size + 4;
186594b4cc7SMaxime Ripard } else {
187594b4cc7SMaxime Ripard data_offs = i * info->ecc_step_size;
188594b4cc7SMaxime Ripard ecc_offs = info->page_size + 4 + (i * (eccbytes + 4));
189594b4cc7SMaxime Ripard }
190594b4cc7SMaxime Ripard
191594b4cc7SMaxime Ripard cnt = fread(buffer, 1, info->ecc_step_size, src);
192594b4cc7SMaxime Ripard if (!cnt && !feof(src)) {
193594b4cc7SMaxime Ripard fprintf(stderr,
194594b4cc7SMaxime Ripard "Failed to read data from the source\n");
195594b4cc7SMaxime Ripard return -1;
196594b4cc7SMaxime Ripard }
197594b4cc7SMaxime Ripard
198594b4cc7SMaxime Ripard pad = info->ecc_step_size - cnt;
199594b4cc7SMaxime Ripard if (pad) {
200594b4cc7SMaxime Ripard if (info->scramble && info->boot0) {
201594b4cc7SMaxime Ripard size_t ret;
202594b4cc7SMaxime Ripard
203594b4cc7SMaxime Ripard ret = fread(buffer + cnt, 1, pad, rnd);
204594b4cc7SMaxime Ripard if (!ret && !feof(rnd)) {
205594b4cc7SMaxime Ripard fprintf(stderr,
206594b4cc7SMaxime Ripard "Failed to read random data\n");
207594b4cc7SMaxime Ripard return -1;
208594b4cc7SMaxime Ripard }
209594b4cc7SMaxime Ripard } else {
210594b4cc7SMaxime Ripard memset(buffer + cnt, 0xff, pad);
211594b4cc7SMaxime Ripard }
212594b4cc7SMaxime Ripard }
213594b4cc7SMaxime Ripard
214594b4cc7SMaxime Ripard memset(ecc, 0, eccbytes);
215594b4cc7SMaxime Ripard swap_bits(buffer, info->ecc_step_size + 4);
216594b4cc7SMaxime Ripard encode_bch(bch, buffer, info->ecc_step_size + 4, ecc);
217594b4cc7SMaxime Ripard swap_bits(buffer, info->ecc_step_size + 4);
218594b4cc7SMaxime Ripard swap_bits(ecc, eccbytes);
219594b4cc7SMaxime Ripard scramble(info, page, buffer, info->ecc_step_size + 4 + eccbytes);
220594b4cc7SMaxime Ripard
221594b4cc7SMaxime Ripard fseek(dst, pos + data_offs, SEEK_SET);
222594b4cc7SMaxime Ripard fwrite(buffer, info->ecc_step_size, 1, dst);
223594b4cc7SMaxime Ripard fseek(dst, pos + ecc_offs - 4, SEEK_SET);
224594b4cc7SMaxime Ripard fwrite(ecc - 4, eccbytes + 4, 1, dst);
225594b4cc7SMaxime Ripard }
226594b4cc7SMaxime Ripard
227594b4cc7SMaxime Ripard /* Fix BBM. */
228594b4cc7SMaxime Ripard fseek(dst, pos + info->page_size, SEEK_SET);
229594b4cc7SMaxime Ripard memset(buffer, 0xff, 2);
230594b4cc7SMaxime Ripard fwrite(buffer, 2, 1, dst);
231594b4cc7SMaxime Ripard
232594b4cc7SMaxime Ripard /* Make dst pointer point to the next page. */
233594b4cc7SMaxime Ripard fseek(dst, pos + info->page_size + info->oob_size, SEEK_SET);
234594b4cc7SMaxime Ripard
235594b4cc7SMaxime Ripard return 0;
236594b4cc7SMaxime Ripard }
237594b4cc7SMaxime Ripard
create_image(const struct image_info * info)238594b4cc7SMaxime Ripard static int create_image(const struct image_info *info)
239594b4cc7SMaxime Ripard {
240594b4cc7SMaxime Ripard off_t page = info->offset / info->page_size;
241594b4cc7SMaxime Ripard struct bch_control *bch;
242594b4cc7SMaxime Ripard FILE *src, *dst, *rnd;
243594b4cc7SMaxime Ripard uint8_t *buffer;
244594b4cc7SMaxime Ripard
245594b4cc7SMaxime Ripard bch = init_bch(14, info->ecc_strength, BCH_PRIMITIVE_POLY);
246594b4cc7SMaxime Ripard if (!bch) {
247594b4cc7SMaxime Ripard fprintf(stderr, "Failed to init the BCH engine\n");
248594b4cc7SMaxime Ripard return -1;
249594b4cc7SMaxime Ripard }
250594b4cc7SMaxime Ripard
251594b4cc7SMaxime Ripard buffer = malloc(info->page_size + info->oob_size);
252594b4cc7SMaxime Ripard if (!buffer) {
253594b4cc7SMaxime Ripard fprintf(stderr, "Failed to allocate the NAND page buffer\n");
254594b4cc7SMaxime Ripard return -1;
255594b4cc7SMaxime Ripard }
256594b4cc7SMaxime Ripard
257594b4cc7SMaxime Ripard memset(buffer, 0xff, info->page_size + info->oob_size);
258594b4cc7SMaxime Ripard
259594b4cc7SMaxime Ripard src = fopen(info->source, "r");
260594b4cc7SMaxime Ripard if (!src) {
261594b4cc7SMaxime Ripard fprintf(stderr, "Failed to open source file (%s)\n",
262594b4cc7SMaxime Ripard info->source);
263594b4cc7SMaxime Ripard return -1;
264594b4cc7SMaxime Ripard }
265594b4cc7SMaxime Ripard
266594b4cc7SMaxime Ripard dst = fopen(info->dest, "w");
267594b4cc7SMaxime Ripard if (!dst) {
268594b4cc7SMaxime Ripard fprintf(stderr, "Failed to open dest file (%s)\n", info->dest);
269594b4cc7SMaxime Ripard return -1;
270594b4cc7SMaxime Ripard }
271594b4cc7SMaxime Ripard
272594b4cc7SMaxime Ripard rnd = fopen("/dev/urandom", "r");
273594b4cc7SMaxime Ripard if (!rnd) {
274594b4cc7SMaxime Ripard fprintf(stderr, "Failed to open /dev/urandom\n");
275594b4cc7SMaxime Ripard return -1;
276594b4cc7SMaxime Ripard }
277594b4cc7SMaxime Ripard
278594b4cc7SMaxime Ripard while (!feof(src)) {
279594b4cc7SMaxime Ripard int ret;
280594b4cc7SMaxime Ripard
281594b4cc7SMaxime Ripard ret = write_page(info, buffer, src, rnd, dst, bch, page++);
282594b4cc7SMaxime Ripard if (ret)
283594b4cc7SMaxime Ripard return ret;
284594b4cc7SMaxime Ripard }
285594b4cc7SMaxime Ripard
286594b4cc7SMaxime Ripard return 0;
287594b4cc7SMaxime Ripard }
288594b4cc7SMaxime Ripard
display_help(int status)289594b4cc7SMaxime Ripard static void display_help(int status)
290594b4cc7SMaxime Ripard {
291594b4cc7SMaxime Ripard fprintf(status == EXIT_SUCCESS ? stdout : stderr,
292594b4cc7SMaxime Ripard "sunxi-nand-image-builder %s\n"
293594b4cc7SMaxime Ripard "\n"
294594b4cc7SMaxime Ripard "Usage: sunxi-nand-image-builder [OPTIONS] source-image output-image\n"
295594b4cc7SMaxime Ripard "\n"
296594b4cc7SMaxime Ripard "Creates a raw NAND image that can be read by the sunxi NAND controller.\n"
297594b4cc7SMaxime Ripard "\n"
298594b4cc7SMaxime Ripard "-h --help Display this help and exit\n"
299594b4cc7SMaxime Ripard "-c <str>/<step> --ecc=<str>/<step> ECC config (strength/step-size)\n"
300594b4cc7SMaxime Ripard "-p <size> --page=<size> Page size\n"
301594b4cc7SMaxime Ripard "-o <size> --oob=<size> OOB size\n"
302594b4cc7SMaxime Ripard "-u <size> --usable=<size> Usable page size\n"
303594b4cc7SMaxime Ripard "-e <size> --eraseblock=<size> Erase block size\n"
304594b4cc7SMaxime Ripard "-b --boot0 Build a boot0 image.\n"
305594b4cc7SMaxime Ripard "-s --scramble Scramble data\n"
306594b4cc7SMaxime Ripard "-a <offset> --address=<offset> Where the image will be programmed.\n"
307594b4cc7SMaxime Ripard "\n"
308594b4cc7SMaxime Ripard "Notes:\n"
309594b4cc7SMaxime Ripard "All the information you need to pass to this tool should be part of\n"
310594b4cc7SMaxime Ripard "the NAND datasheet.\n"
311594b4cc7SMaxime Ripard "\n"
312594b4cc7SMaxime Ripard "The NAND controller only supports the following ECC configs\n"
313594b4cc7SMaxime Ripard " Valid ECC strengths: 16, 24, 28, 32, 40, 48, 56, 60 and 64\n"
314594b4cc7SMaxime Ripard " Valid ECC step size: 512 and 1024\n"
315594b4cc7SMaxime Ripard "\n"
316594b4cc7SMaxime Ripard "If you are building a boot0 image, you'll have specify extra options.\n"
317594b4cc7SMaxime Ripard "These options should be chosen based on the layouts described here:\n"
318594b4cc7SMaxime Ripard " http://linux-sunxi.org/NAND#More_information_on_BROM_NAND\n"
319594b4cc7SMaxime Ripard "\n"
320594b4cc7SMaxime Ripard " --usable should be assigned the 'Hardware page' value\n"
321594b4cc7SMaxime Ripard " --ecc should be assigned the 'ECC capacity'/'ECC page' values\n"
322594b4cc7SMaxime Ripard " --usable should be smaller than --page\n"
323594b4cc7SMaxime Ripard "\n"
324594b4cc7SMaxime Ripard "The --address option is only required for non-boot0 images that are \n"
325594b4cc7SMaxime Ripard "meant to be programmed at a non eraseblock aligned offset.\n"
326594b4cc7SMaxime Ripard "\n"
327594b4cc7SMaxime Ripard "Examples:\n"
328594b4cc7SMaxime Ripard " The H27UCG8T2BTR-BC NAND exposes\n"
329594b4cc7SMaxime Ripard " * 16k pages\n"
330594b4cc7SMaxime Ripard " * 1280 OOB bytes per page\n"
331594b4cc7SMaxime Ripard " * 4M eraseblocks\n"
332594b4cc7SMaxime Ripard " * requires data scrambling\n"
333594b4cc7SMaxime Ripard " * expects a minimum ECC of 40bits/1024bytes\n"
334594b4cc7SMaxime Ripard "\n"
335594b4cc7SMaxime Ripard " A normal image can be generated with\n"
336594b4cc7SMaxime Ripard " sunxi-nand-image-builder -p 16384 -o 1280 -e 0x400000 -s -c 40/1024\n"
337594b4cc7SMaxime Ripard " A boot0 image can be generated with\n"
338594b4cc7SMaxime Ripard " sunxi-nand-image-builder -p 16384 -o 1280 -e 0x400000 -s -b -u 4096 -c 64/1024\n",
339594b4cc7SMaxime Ripard PLAIN_VERSION);
340594b4cc7SMaxime Ripard exit(status);
341594b4cc7SMaxime Ripard }
342594b4cc7SMaxime Ripard
check_image_info(struct image_info * info)343594b4cc7SMaxime Ripard static int check_image_info(struct image_info *info)
344594b4cc7SMaxime Ripard {
345594b4cc7SMaxime Ripard static int valid_ecc_strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
346594b4cc7SMaxime Ripard int eccbytes, eccsteps;
347594b4cc7SMaxime Ripard unsigned i;
348594b4cc7SMaxime Ripard
349594b4cc7SMaxime Ripard if (!info->page_size) {
350594b4cc7SMaxime Ripard fprintf(stderr, "--page is missing\n");
351594b4cc7SMaxime Ripard return -EINVAL;
352594b4cc7SMaxime Ripard }
353594b4cc7SMaxime Ripard
354594b4cc7SMaxime Ripard if (!info->page_size) {
355594b4cc7SMaxime Ripard fprintf(stderr, "--oob is missing\n");
356594b4cc7SMaxime Ripard return -EINVAL;
357594b4cc7SMaxime Ripard }
358594b4cc7SMaxime Ripard
359594b4cc7SMaxime Ripard if (!info->eraseblock_size) {
360594b4cc7SMaxime Ripard fprintf(stderr, "--eraseblock is missing\n");
361594b4cc7SMaxime Ripard return -EINVAL;
362594b4cc7SMaxime Ripard }
363594b4cc7SMaxime Ripard
364594b4cc7SMaxime Ripard if (info->ecc_step_size != 512 && info->ecc_step_size != 1024) {
365594b4cc7SMaxime Ripard fprintf(stderr, "Invalid ECC step argument: %d\n",
366594b4cc7SMaxime Ripard info->ecc_step_size);
367594b4cc7SMaxime Ripard return -EINVAL;
368594b4cc7SMaxime Ripard }
369594b4cc7SMaxime Ripard
370594b4cc7SMaxime Ripard for (i = 0; i < ARRAY_SIZE(valid_ecc_strengths); i++) {
371594b4cc7SMaxime Ripard if (valid_ecc_strengths[i] == info->ecc_strength)
372594b4cc7SMaxime Ripard break;
373594b4cc7SMaxime Ripard }
374594b4cc7SMaxime Ripard
375594b4cc7SMaxime Ripard if (i == ARRAY_SIZE(valid_ecc_strengths)) {
376594b4cc7SMaxime Ripard fprintf(stderr, "Invalid ECC strength argument: %d\n",
377594b4cc7SMaxime Ripard info->ecc_strength);
378594b4cc7SMaxime Ripard return -EINVAL;
379594b4cc7SMaxime Ripard }
380594b4cc7SMaxime Ripard
381594b4cc7SMaxime Ripard eccbytes = DIV_ROUND_UP(info->ecc_strength * 14, 8);
382594b4cc7SMaxime Ripard if (eccbytes % 2)
383594b4cc7SMaxime Ripard eccbytes++;
384594b4cc7SMaxime Ripard eccbytes += 4;
385594b4cc7SMaxime Ripard
386594b4cc7SMaxime Ripard eccsteps = info->usable_page_size / info->ecc_step_size;
387594b4cc7SMaxime Ripard
388594b4cc7SMaxime Ripard if (info->page_size + info->oob_size <
389594b4cc7SMaxime Ripard info->usable_page_size + (eccsteps * eccbytes)) {
390594b4cc7SMaxime Ripard fprintf(stderr,
391594b4cc7SMaxime Ripard "ECC bytes do not fit in the NAND page, choose a weaker ECC\n");
392594b4cc7SMaxime Ripard return -EINVAL;
393594b4cc7SMaxime Ripard }
394594b4cc7SMaxime Ripard
395594b4cc7SMaxime Ripard return 0;
396594b4cc7SMaxime Ripard }
397594b4cc7SMaxime Ripard
main(int argc,char ** argv)398594b4cc7SMaxime Ripard int main(int argc, char **argv)
399594b4cc7SMaxime Ripard {
400594b4cc7SMaxime Ripard struct image_info info;
401594b4cc7SMaxime Ripard
402594b4cc7SMaxime Ripard memset(&info, 0, sizeof(info));
403594b4cc7SMaxime Ripard /*
404594b4cc7SMaxime Ripard * Process user arguments
405594b4cc7SMaxime Ripard */
406594b4cc7SMaxime Ripard for (;;) {
407594b4cc7SMaxime Ripard int option_index = 0;
408594b4cc7SMaxime Ripard char *endptr = NULL;
409594b4cc7SMaxime Ripard static const struct option long_options[] = {
410594b4cc7SMaxime Ripard {"help", no_argument, 0, 'h'},
411594b4cc7SMaxime Ripard {"ecc", required_argument, 0, 'c'},
412594b4cc7SMaxime Ripard {"page", required_argument, 0, 'p'},
413594b4cc7SMaxime Ripard {"oob", required_argument, 0, 'o'},
414594b4cc7SMaxime Ripard {"usable", required_argument, 0, 'u'},
415594b4cc7SMaxime Ripard {"eraseblock", required_argument, 0, 'e'},
416594b4cc7SMaxime Ripard {"boot0", no_argument, 0, 'b'},
417594b4cc7SMaxime Ripard {"scramble", no_argument, 0, 's'},
418594b4cc7SMaxime Ripard {"address", required_argument, 0, 'a'},
419594b4cc7SMaxime Ripard {0, 0, 0, 0},
420594b4cc7SMaxime Ripard };
421594b4cc7SMaxime Ripard
422594b4cc7SMaxime Ripard int c = getopt_long(argc, argv, "c:p:o:u:e:ba:sh",
423594b4cc7SMaxime Ripard long_options, &option_index);
424594b4cc7SMaxime Ripard if (c == EOF)
425594b4cc7SMaxime Ripard break;
426594b4cc7SMaxime Ripard
427594b4cc7SMaxime Ripard switch (c) {
428594b4cc7SMaxime Ripard case 'h':
429594b4cc7SMaxime Ripard display_help(0);
430594b4cc7SMaxime Ripard break;
431594b4cc7SMaxime Ripard case 's':
432594b4cc7SMaxime Ripard info.scramble = 1;
433594b4cc7SMaxime Ripard break;
434594b4cc7SMaxime Ripard case 'c':
435594b4cc7SMaxime Ripard info.ecc_strength = strtol(optarg, &endptr, 0);
436*f59a3b21Sxypron.glpk@gmx.de if (*endptr == '/')
437594b4cc7SMaxime Ripard info.ecc_step_size = strtol(endptr + 1, NULL, 0);
438594b4cc7SMaxime Ripard break;
439594b4cc7SMaxime Ripard case 'p':
440594b4cc7SMaxime Ripard info.page_size = strtol(optarg, NULL, 0);
441594b4cc7SMaxime Ripard break;
442594b4cc7SMaxime Ripard case 'o':
443594b4cc7SMaxime Ripard info.oob_size = strtol(optarg, NULL, 0);
444594b4cc7SMaxime Ripard break;
445594b4cc7SMaxime Ripard case 'u':
446594b4cc7SMaxime Ripard info.usable_page_size = strtol(optarg, NULL, 0);
447594b4cc7SMaxime Ripard break;
448594b4cc7SMaxime Ripard case 'e':
449594b4cc7SMaxime Ripard info.eraseblock_size = strtol(optarg, NULL, 0);
450594b4cc7SMaxime Ripard break;
451594b4cc7SMaxime Ripard case 'b':
452594b4cc7SMaxime Ripard info.boot0 = 1;
453594b4cc7SMaxime Ripard break;
454594b4cc7SMaxime Ripard case 'a':
455594b4cc7SMaxime Ripard info.offset = strtoull(optarg, NULL, 0);
456594b4cc7SMaxime Ripard break;
457594b4cc7SMaxime Ripard case '?':
458594b4cc7SMaxime Ripard display_help(-1);
459594b4cc7SMaxime Ripard break;
460594b4cc7SMaxime Ripard }
461594b4cc7SMaxime Ripard }
462594b4cc7SMaxime Ripard
463594b4cc7SMaxime Ripard if ((argc - optind) != 2)
464594b4cc7SMaxime Ripard display_help(-1);
465594b4cc7SMaxime Ripard
466594b4cc7SMaxime Ripard info.source = argv[optind];
467594b4cc7SMaxime Ripard info.dest = argv[optind + 1];
468594b4cc7SMaxime Ripard
469594b4cc7SMaxime Ripard if (!info.boot0) {
470594b4cc7SMaxime Ripard info.usable_page_size = info.page_size;
471594b4cc7SMaxime Ripard } else if (!info.usable_page_size) {
472594b4cc7SMaxime Ripard if (info.page_size > 8192)
473594b4cc7SMaxime Ripard info.usable_page_size = 8192;
474594b4cc7SMaxime Ripard else if (info.page_size > 4096)
475594b4cc7SMaxime Ripard info.usable_page_size = 4096;
476594b4cc7SMaxime Ripard else
477594b4cc7SMaxime Ripard info.usable_page_size = 1024;
478594b4cc7SMaxime Ripard }
479594b4cc7SMaxime Ripard
480594b4cc7SMaxime Ripard if (check_image_info(&info))
481594b4cc7SMaxime Ripard display_help(-1);
482594b4cc7SMaxime Ripard
483594b4cc7SMaxime Ripard return create_image(&info);
484594b4cc7SMaxime Ripard }
485