diff options
author | Marcin Bukat <marcin.bukat@gmail.com> | 2013-09-02 12:35:47 +0200 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2013-09-02 12:35:47 +0200 |
commit | f182a11f3362017a6c669871414a9bb448ee050d (patch) | |
tree | 5afc1ba1713af14aea513ca9b53d00369e083447 /utils/rk27utils/nandextract/nandextract.c | |
parent | b97cdc8f5efa8b447e1e1398d86eb87c80ed4b22 (diff) | |
download | rockbox-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.c | 235 |
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 */ | ||
14 | enum { | ||
15 | CONTINOUS_ENC, /* scramble whole block at once */ | ||
16 | PAGE_ENC /* nand bootloader is scrambled in 0x200 chunks */ | ||
17 | }; | ||
18 | |||
19 | static 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 | |||
31 | static 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 */ | ||
68 | static void encode_page(uint8_t *inpg, uint8_t *outpg, const int size) | ||
69 | { | ||
70 | |||
71 | uint8_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 | */ | ||
113 | static 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 | |||
136 | static 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 | |||
168 | int 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 | } | ||