Newer
Older
/*
* Nand-image-builder for broadcom chipset.
*
* Copyright (C) 2019 iopsys Software Solutions AB. All rights reserved.
*
* 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
*
* This code is referenced from https://github.com/ak-hard/brcm-nand-bch/.
*
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
*/
// BCH 4 algorithm is used for ECC level. In schematic bootstrap sets to 4 bit ECC level for NAND.
#define BCH_4 4
//Field order for above 7.0 broadcom NAND Controller is 14, older is 13
#define FIELD_ORDER 14
#define MIN_FIELD 13
#define MAX_FIELD 14
#define SECTORS_PER_PAGE 4
// The parameters are for subpages (2048/4)
#define SECTOR_SZ (PAGE_SIZE / SECTORS_PER_PAGE)
#define OOB_SZ (SPARE_AREA / SECTORS_PER_PAGE)
#define OOB_ECC_OFS 9
#define OOB_ECC_LEN 7
// Default NAND Confgiuration for 2k paged with 64bytes spare size.
#define PAGE_SIZE 2048
#define BLOCK_SIZE 131072
#define SPARE_AREA 64
#define SUB_PAGE_SIZE 512
#define MAX_BLOCK_SIZE 524288
#define MAX_OOB_BLOCK_SIZE 540672
#define DEFAULT_POLY 0x201b
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <getopt.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <inttypes.h>
#include "bch.h"
/**
*
* This program reads raw NAND image from standard input and updates ECC bytes in the OOB block for each sector.
* Data layout is as following:
*
* 2 KB page, consisting of 4 x 512 B sectors
* 64 bytes OOB, consisting of 4 x 16 B OOB regions, one for each sector
*
* In each OOB region, the first 9 1/2 bytes are user defined and the remaining 6 1/2 bytes are ECC.
*
*/
uint8_t page_buffer[(SECTOR_SZ + OOB_SZ) * SECTORS_PER_PAGE];
// Wide right shift by 4 bits. Preserves the very first 4 bits of the output.
static void shift_half_byte(const uint8_t *src, uint8_t *dest, size_t sz)
{
// go right to left since input and output may overlap
unsigned j;
dest[sz] = src[sz - 1] << 4;
for (j = sz; j != 0; --j)
dest[j] = src[j] >> 4 | src[j - 1] << 4;
dest[0] |= src[0] >> 4;
}
/* swap buffer for another */
static void swap(uint8_t *first, uint8_t *second, int amount)
{
uint8_t temp;
{
temp = *first;
*first++ = *second;
*second++ = temp;
}
}
static void clear_pagebuffer()
{
uint32_t index = 0U;
/* Clears the buffers before programming the Page. */
for (index = 0; index < ((SECTOR_SZ + OOB_SZ) * SECTORS_PER_PAGE); index++)
{
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
}
}
void print_usage(void)
{
printf("nand image builder version 1.0\n");
printf("Usage: nand-image-builder [OPTION]...\n");
printf("\t -i input file\n");
printf("\t -o output file\n");
printf("\t -b block size\n");
printf("\t -p page size\n");
printf("\t -s spare area/oob area size\n");
printf("\t -e ecc mode\n");
printf("\t -m field order\n");
printf("Default command is:\n");
printf("./nand-image-builder -i infile -o outfile -b 131072 -p 2048 -s 64 -e 4 -m 14\n");
return;
}
int main(int argc, char **argv)
{
int option = 0;
uint32_t blocks_app, blocks_num = 0;
uint32_t code_length, parity, msg_length, data_bytes = 0;
uint32_t countMask, count = 0;
uint32_t numBytesRead, totalBytesRead = 0;
uint32_t block_size, page_size, ecc_level, spare_area, field_order = 0;
uint32_t sector_sz, oob_sz, oob_ecc_len, oob_ecc_ofs = 0;
int pages_in_block, i, j = 0;
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
const char *input_file = NULL;
const char *output_file = NULL;
FILE *in_file = NULL, *out_file = NULL;
struct stat st;
uint8_t *buffer;
uint8_t cmp_buffer[SECTOR_SZ * SECTORS_PER_PAGE];
uint8_t ecc[OOB_ECC_LEN];
/* Initialize BCH lib data and parameters */
while ((option = getopt(argc, argv, "e:i:o:b:p:s:m:")) != -1)
{
switch (option)
{
case 'i':
input_file = optarg;
break;
case 'o':
output_file = optarg;
break;
case 'b':
block_size = atoi(optarg);
break;
case 'p':
page_size = atoi(optarg);
break;
case 'e':
ecc_level = atoi(optarg);
break;
case 's':
spare_area = atoi(optarg);
break;
case 'm':
field_order = atoi(optarg);
break;
default:
print_usage();
goto exit;
}
}
if (argc < 4)
{
print_usage();
goto exit;
}
/* Validate input */
if (block_size == 0)
{
printf("Block size can not be 0\n");
goto exit;
}
if (block_size > MAX_BLOCK_SIZE)
{
printf("Block size to large\n");
}
if (page_size == 0)
{
printf("Page size can not be 0\n");
goto exit;
}
if (page_size % 32)
{
printf("Page size not even multiple of 32\n");
goto exit;
}
if (field_order < MIN_FIELD || field_order > MAX_FIELD)
{
printf("Field order %d and %d are supported.\n", MIN_FIELD, MAX_FIELD);
goto exit;
}
if (ecc_level != BCH_4)
{
printf("-e unsupported ecc level %d.\n", ecc_level);
goto exit;
}
if (input_file == NULL)
{
printf("-i input file name is missing\n");
goto exit;
}
if (output_file == NULL)
{
printf("-o output file name is missing\n");
goto exit;
}
in_file = fopen(input_file, "r");
if (in_file == NULL)
{
printf("In file open error\n");
goto exit;
}
out_file = fopen(output_file, "wb");
if (out_file == NULL)
{
printf("Out file open error\n");
goto exit;
}
fseek(in_file, 0, SEEK_SET);
fseek(out_file, 0, SEEK_SET);
/* Validate that the input file size is a multiple of the block size */
if (stat(input_file, &st) != 0)
{
printf("File size error\n");
goto exit;
}
/* if ((st.st_size % block_size) != 0)
{
printf("File size not multiple of block size\n");
goto exit;
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
blocks_app = st.st_size / block_size; //calculate total blocks needed for application.
pages_in_block = block_size / page_size;
sector_sz = page_size / SECTORS_PER_PAGE;
oob_sz = spare_area / SECTORS_PER_PAGE;
/* Codeword length (in bits) */
code_length = (sector_sz + oob_sz) * 8;
/* Parity length (in bits). */
parity = field_order * ecc_level;
/* Message length (in bits). */
msg_length = code_length - parity;
/* Number of data bytes = floor(k/8) */
data_bytes = msg_length / 8;
/* Number of ecc_code bytes, v = ceil(p/8) = ceil(m*t/8) = p_partial_page_size+s-u */
oob_ecc_len = (sector_sz + oob_sz) - data_bytes;
if (oob_ecc_len > oob_sz)
{
printf(" Number of spare bytes must be at least %d\n", oob_ecc_len);
goto exit;
}
oob_ecc_ofs = oob_sz - oob_ecc_len;
countMask = pages_in_block - 1;
buffer = malloc(sector_sz + oob_ecc_ofs + 1);
if (!buffer)
{
printf("Failed to allocate the buffer\n");
goto exit;
}
/* Initialize BCH lib data and parameters */
struct bch_control *bch = init_bch(field_order, ecc_level, 0);
if (!bch)
goto exit;
printf("Input file %s\n", input_file);
printf("Size of input file: %d\n", (int)st.st_size);
printf("Block size: %d\n", block_size);
printf("Input file blocks %d\n", blocks_app);
printf("Page size: %d\n", page_size);
printf("Pages in block: %d\n", pages_in_block);
printf("ECC mode: %d\n", ecc_level);
printf("OOB area size %d\n", spare_area);
printf("Sub Page size %d\n", sector_sz);
printf("OOB size per subpage %d\n", oob_sz);
printf("OOB_ECC_LEN %d\n", oob_ecc_len);
printf("OOB_ECC_OFS %d\n", oob_ecc_ofs);
while (!feof(in_file))
{
clear_pagebuffer();
numBytesRead = fread(page_buffer, (size_t)1, (sector_sz)*SECTORS_PER_PAGE, in_file);
totalBytesRead += numBytesRead;
/* printf("\r\nRead %d bytes [%d%%] from file...\r\n",
totalBytesRead,
((totalBytesRead * 100U) / st.st_size)); */
if (numBytesRead == 0)
{
printf("Fread failed or file size is zero\r\n");
goto exit;
}
if (numBytesRead != (sector_sz)*SECTORS_PER_PAGE)
break; // skip if byte is less than bytes read from fread api.
memset(cmp_buffer, 0, sector_sz * SECTORS_PER_PAGE);
memcpy(cmp_buffer, page_buffer, (sector_sz)*SECTORS_PER_PAGE);
/*printf("Page %d memory dump before ECC data\n", count);
for(i=0; i < 2048; i++)
printf("0x%X\t", cmp_buffer[i]); */
for (i = 0; i < SECTOR_SZ * SECTORS_PER_PAGE; i += 4)
if (*(uint32_t *)(page_buffer + i) != 0xffffffff)
{
for (i = 0; i != SECTORS_PER_PAGE; ++i)
{
swap(&page_buffer[(i + 1) * sector_sz], &page_buffer[sector_sz * SECTORS_PER_PAGE + (i * oob_sz)], oob_sz);
memset(ecc, 0, OOB_ECC_LEN);
encode_bch(bch, &page_buffer[i * sector_sz], sector_sz + oob_ecc_ofs, ecc);
swap(&page_buffer[(i + 1) * sector_sz], &page_buffer[sector_sz * SECTORS_PER_PAGE + (i * oob_sz)], oob_sz);
for (j = 0; j < oob_ecc_len; j++)
{
page_buffer[sector_sz * SECTORS_PER_PAGE + ((i + 1) * oob_sz) - oob_ecc_len + j] = ecc[j];
}
//page_buffer[sector_sz * SECTORS_PER_PAGE + ((i + 1) * oob_sz) - oob_ecc_len +] |= ecc[oob_ecc_len - 1] >> 4;
//fwrite(page_buffer, (sector_sz + oob_sz) * SECTORS_PER_PAGE, 1, out_file);
//goto exit;
//sector_oob[oob_ecc_ofs + oob_ecc_len - 1] |= ecc[oob_ecc_len - 1] >> 4;
}
}
break;
}
fwrite(page_buffer, (sector_sz + oob_sz) * SECTORS_PER_PAGE, 1, out_file);
i = memcmp(cmp_buffer, page_buffer, (sector_sz)*SECTORS_PER_PAGE); // compare the page buffer with cmp_buffer to verify page has not modified except oob spare size.
if (i != 0)
printf("\r\npage no %d and block no %d compare failed\n", count, blocks_num);
/* printf("\r\nPage %d memory dump after adding ECC data\n", count);
for(i=0; i < 2048; i++)
printf("0x%X\t", page_buffer[i]); */
count++;
if (!(count & countMask))
{
/* printf("block No %d\r\n", blocks_num); */