forked from ak-hard/brcm-nand-bch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbrcm-nand-bch.c
90 lines (80 loc) · 2.54 KB
/
brcm-nand-bch.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
87
88
89
90
#include <stdlib.h>
#include <stdio.h>
#include <string.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.
*
*/
#define BCH_T 4
#define BCH_N 13
#define SECTOR_SZ 512
#define OOB_SZ 16
#define SECTORS_PER_PAGE 4
#define OOB_ECC_OFS 9
#define OOB_ECC_LEN 7
// 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;
}
int main(int argc, char *argv[])
{
unsigned poly = argc < 2 ? 0 : strtoul(argv[1], NULL, 0);
struct bch_control *bch = init_bch(BCH_N, BCH_T, poly);
if (!bch)
return -1;
uint8_t page_buffer[(SECTOR_SZ + OOB_SZ) * SECTORS_PER_PAGE];
while (1)
{
if (fread(page_buffer, (SECTOR_SZ + OOB_SZ) * SECTORS_PER_PAGE, 1, stdin) != 1)
break;
// Erased pages have ECC = 0xff .. ff even though there may be user bytes in the OOB region
int erased_block = 1;
unsigned i;
for (i = 0; i != SECTOR_SZ * SECTORS_PER_PAGE; ++i)
if (page_buffer[i] != 0xff)
{
erased_block = 0;
break;
}
for (i = 0; i != SECTORS_PER_PAGE; ++i)
{
const uint8_t *sector_data = page_buffer + SECTOR_SZ * i;
uint8_t *sector_oob = page_buffer + SECTOR_SZ * SECTORS_PER_PAGE + OOB_SZ * i;
if (erased_block)
{
// erased page ECC consumes full 7 bytes, including high 4 bits set to 0xf
memset(sector_oob + OOB_ECC_OFS, 0xff, OOB_ECC_LEN);
}
else
{
// concatenate input data
uint8_t buffer[SECTOR_SZ + OOB_ECC_OFS + 1];
buffer[0] = 0;
shift_half_byte(sector_data, buffer, SECTOR_SZ);
shift_half_byte(sector_oob, buffer + SECTOR_SZ, OOB_ECC_OFS);
// compute ECC
uint8_t ecc[OOB_ECC_LEN];
memset(ecc, 0, OOB_ECC_LEN);
encode_bch(bch, buffer, SECTOR_SZ + OOB_ECC_OFS + 1, ecc);
// copy the result in its OOB block, shifting right by 4 bits
shift_half_byte(ecc, sector_oob + OOB_ECC_OFS, OOB_ECC_LEN - 1);
sector_oob[OOB_ECC_OFS + OOB_ECC_LEN - 1] |= ecc[OOB_ECC_LEN - 1] >> 4;
}
}
fwrite(page_buffer, (SECTOR_SZ + OOB_SZ) * SECTORS_PER_PAGE, 1, stdout);
}
}