summaryrefslogtreecommitdiff
path: root/utils/rk27utils/nandextract/nandextract.c
diff options
context:
space:
mode:
authorMarcin Bukat <marcin.bukat@gmail.com>2013-09-02 12:35:47 +0200
committerMarcin Bukat <marcin.bukat@gmail.com>2013-09-02 12:35:47 +0200
commitf182a11f3362017a6c669871414a9bb448ee050d (patch)
tree5afc1ba1713af14aea513ca9b53d00369e083447 /utils/rk27utils/nandextract/nandextract.c
parentb97cdc8f5efa8b447e1e1398d86eb87c80ed4b22 (diff)
downloadrockbox-f182a11f3362017a6c669871414a9bb448ee050d.tar.gz
rockbox-f182a11f3362017a6c669871414a9bb448ee050d.zip
rk27utils: Add nandextract utility
This quick and dirty utility allows to extract nand bootloader from raw 1st nand block dump. I post it mainly to somewhat document how BCH error correction engine of the rk27xx works. Change-Id: I37ca91add7d372e3576d2722afc946d0f08971a9
Diffstat (limited to 'utils/rk27utils/nandextract/nandextract.c')
-rw-r--r--utils/rk27utils/nandextract/nandextract.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/utils/rk27utils/nandextract/nandextract.c b/utils/rk27utils/nandextract/nandextract.c
new file mode 100644
index 0000000000..c9b41d26e3
--- /dev/null
+++ b/utils/rk27utils/nandextract/nandextract.c
@@ -0,0 +1,235 @@
1#include <stdio.h>
2#include <stdint.h>
3#include <stdbool.h>
4#include <stdlib.h>
5#include <string.h>
6#include "libbch.h"
7
8#define SECTOR_DATA_SIZE 512
9#define SECTOR_META_SIZE 3
10#define SECTOR_ECC_SIZE 13
11#define SECTOR_SIZE (SECTOR_DATA_SIZE + SECTOR_META_SIZE + SECTOR_ECC_SIZE)
12
13/* scramble mode */
14enum {
15 CONTINOUS_ENC, /* scramble whole block at once */
16 PAGE_ENC /* nand bootloader is scrambled in 0x200 chunks */
17};
18
19static uint8_t reverse_bits(uint8_t b)
20{
21 return (((b & 0x80) >> 7)|
22 ((b & 0x40) >> 5)|
23 ((b & 0x20) >> 3)|
24 ((b & 0x10) >> 1)|
25 ((b & 0x08) << 1)|
26 ((b & 0x04) << 3)|
27 ((b & 0x02) << 5)|
28 ((b & 0x01) << 7));
29}
30
31static int libbch_decode_sec(struct bch_control *bch, uint8_t *inbuf, uint8_t *outbuf)
32{
33 unsigned int errloc[8];
34 static const uint8_t mask[13] = {
35 0x4e, 0x8c, 0x9d, 0x52,
36 0x2d, 0x6c, 0x7c, 0xcb,
37 0xc3, 0x12, 0x14, 0x19,
38 0x37,
39 };
40
41 int i, err_num = 0;
42
43 /* ecc masking polynomial */
44 for (i=0; i<SECTOR_ECC_SIZE; i++)
45 inbuf[SECTOR_DATA_SIZE+SECTOR_META_SIZE+i] ^= mask[i];
46
47 /* fix ordering of input bits */
48 for (i = 0; i < SECTOR_SIZE; i++)
49 inbuf[i] = reverse_bits(inbuf[i]);
50
51 err_num = decode_bch(bch, inbuf,
52 (SECTOR_SIZE - SECTOR_ECC_SIZE),
53 &inbuf[SECTOR_SIZE - SECTOR_ECC_SIZE],
54 NULL, NULL, errloc);
55
56 /* apply fixups */
57 for(i=0; i<err_num; i++)
58 inbuf[errloc[i]/8] ^= 1 << (errloc[i] % 8);
59
60 /* reverse bits back (data part only), remining bytes are scratched */
61 for (i = 0; i < SECTOR_DATA_SIZE; i++)
62 outbuf[i] = reverse_bits(inbuf[i]);
63
64 return err_num;
65}
66
67/* scrambling/descrambling reverse engineered by AleMaxx */
68static void encode_page(uint8_t *inpg, uint8_t *outpg, const int size)
69{
70
71uint8_t key[] = {
72 0x7C, 0x4E, 0x03, 0x04,
73 0x55, 0x05, 0x09, 0x07,
74 0x2D, 0x2C, 0x7B, 0x38,
75 0x17, 0x0D, 0x17, 0x11
76};
77 int i, i3, x, val, idx;
78
79 uint8_t key1[0x100];
80 uint8_t key2[0x100];
81
82 for (i=0; i<0x100; i++) {
83 key1[i] = i;
84 key2[i] = key[i&0xf];
85 }
86
87 i3 = 0;
88 for (i=0; i<0x100; i++) {
89 x = key1[i];
90 i3 = key1[i] + i3;
91 i3 += key2[i];
92 i3 &= 0xff;
93 key1[i] = key1[i3];
94 key1[i3] = x;
95 }
96
97 idx = 0;
98 for (i=0; i<size; i++) {
99 x = key1[(i+1) & 0xff];
100 val = x;
101 idx = (x + idx) & 0xff;
102 key1[(i+1) & 0xff] = key1[idx];
103 key1[idx] = (x & 0xff);
104 val = (key1[(i+1)&0xff] + x) & 0xff;
105 val = key1[val];
106 outpg[i] = val ^ inpg[i];
107 }
108}
109
110/* returns offset in bytes of the sector
111 * NOTE: bootrom assumes 4 secs per page (regardles of actual pagesize)
112 */
113static int offset(int sec_num, int page_size, int rom)
114{
115 int sec_per_page, page_num, page_offset;
116
117 if (rom)
118 sec_per_page = 4;
119 else
120 sec_per_page = page_size / SECTOR_SIZE;
121
122 page_num = sec_num / sec_per_page;
123 page_offset = sec_num % sec_per_page;
124
125 printf("Sec per page: %d\n", sec_per_page);
126 printf("Page num: %d\n", page_num);
127 printf("Offset in page (sec): %d\n", page_offset);
128 printf("Offset in file (bytes): %d (0x%0x)\n", (page_num * page_size) +
129 (page_offset * SECTOR_SIZE),
130 (page_num * page_size) +
131 (page_offset * SECTOR_SIZE));
132
133 return ((page_num * page_size) + (page_offset * SECTOR_SIZE));
134}
135
136static int sector_read(FILE *fp, void *buff, int sec_num, int nand_page_size, struct bch_control *bch)
137{
138 int ret;
139 int file_offset = offset(sec_num, nand_page_size, 1);
140 uint8_t inbuf[SECTOR_SIZE];
141 uint8_t outbuf[SECTOR_SIZE];
142
143 if (fp == NULL)
144 return -1;
145
146 /* seek to the begining of the data */
147 fseek(fp, file_offset, SEEK_SET);
148
149 /* read into the buffer */
150 ret = fread(inbuf, 1, SECTOR_SIZE, fp);
151
152 if (ret != SECTOR_SIZE)
153 {
154 return -2;
155 }
156
157 ret = libbch_decode_sec(bch, inbuf, outbuf);
158
159 if (ret)
160 {
161 printf("LIBBCH Data %d error(s) in sector %d\n", ret, sec_num);
162 }
163
164 memcpy(buff, outbuf, SECTOR_DATA_SIZE);
165 return ret;
166}
167
168int main (int argc, char **argv)
169{
170 FILE *ifp, *ofp;
171 void *obuf;
172 int i, size, sector, num_sectors, nand_page_size;
173 char *infile, *outfile, *ptr;
174
175 struct bch_control *bch;
176
177 if (argc < 6)
178 {
179 printf("Usage: %s infile outfile start_sector num_sectors nand_page_size\n", argv[0]);
180 return 0;
181 }
182
183 infile = argv[1];
184 outfile = argv[2];
185 sector = atoi(argv[3]);
186 num_sectors = atoi(argv[4]);
187 nand_page_size = atoi(argv[5]);
188
189 size = SECTOR_DATA_SIZE * num_sectors;
190 obuf = malloc(size);
191
192 if (obuf == NULL)
193 {
194 printf("Error allocating %d bytes of buffer\n", size);
195 return -1;
196 }
197
198 ifp = fopen(infile, "rb");
199
200 if (ifp == NULL)
201 {
202 printf("Cannot open %s file\n", infile);
203 free(obuf);
204 return -2;
205 }
206
207 ofp = fopen(outfile, "wb");
208
209 if (ifp == NULL)
210 {
211 printf("Cannot open %s file\n", outfile);
212 fclose(ifp);
213 free(obuf);
214 return -3;
215 }
216
217 bch = init_bch(13, 8, 0x25af);
218
219 ptr = (char *)obuf;
220 for(i=0; i<num_sectors; i++)
221 {
222 sector_read(ifp, ptr, sector++, nand_page_size, bch);
223 encode_page((uint8_t *)ptr, (uint8_t *)ptr, SECTOR_DATA_SIZE);
224 ptr += SECTOR_DATA_SIZE;
225 }
226
227 fwrite(obuf, 1, size, ofp);
228
229 fclose(ifp);
230 fclose(ofp);
231 free(obuf);
232
233 free_bch(bch);
234 return 0;
235}