summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2016-02-05 00:49:33 +0100
committerCástor Muñoz <cmvidal@gmail.com>2016-08-19 00:15:59 +0200
commit7083110f8154f6ca47845fc747bad5062515658c (patch)
tree2c3bf23060d0435be9966c152710e93b2272516b
parentbf89c757ddee815ebdd42bba91413daa96d50b75 (diff)
downloadrockbox-7083110f8154f6ca47845fc747bad5062515658c.tar.gz
rockbox-7083110f8154f6ca47845fc747bad5062515658c.zip
iPod Classic: NOR support for bootloader
- NOR driver (based on emCORE) - read/write IM3 images - read 'flsh' files Change-Id: Ie0654e0d298affc0f47ed64b823767118bd1aa3f
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/target/arm/s5l8702/nor-s5l8702.c369
-rw-r--r--firmware/target/arm/s5l8702/nor-target.h167
3 files changed, 537 insertions, 0 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 391b9af185..6a18639eb2 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1630,6 +1630,7 @@ target/arm/s5l8702/ipod6g/adc-ipod6g.c
1630#else 1630#else
1631target/arm/s5l8702/spi-s5l8702.c 1631target/arm/s5l8702/spi-s5l8702.c
1632target/arm/s5l8702/crypto-s5l8702.c 1632target/arm/s5l8702/crypto-s5l8702.c
1633target/arm/s5l8702/nor-s5l8702.c
1633#endif 1634#endif
1634#endif 1635#endif
1635 1636
diff --git a/firmware/target/arm/s5l8702/nor-s5l8702.c b/firmware/target/arm/s5l8702/nor-s5l8702.c
new file mode 100644
index 0000000000..ac60ede2de
--- /dev/null
+++ b/firmware/target/arm/s5l8702/nor-s5l8702.c
@@ -0,0 +1,369 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id:
9 *
10 * Copyright © 2009 Michael Sparmann
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include <stdint.h>
22#include <stdbool.h>
23#include <string.h>
24
25#include "config.h"
26#include "system.h"
27#include "s5l8702.h"
28#include "spi-s5l8702.h"
29#include "crypto-s5l8702.h"
30#include "nor-target.h"
31
32static void bootflash_ce(int port, bool state)
33{
34 spi_ce(port, state);
35}
36
37void bootflash_init(int port)
38{
39 spi_init(port, true);
40 bootflash_ce(port, false);
41}
42
43void bootflash_close(int port)
44{
45 spi_init(port, false);
46}
47
48static void bootflash_wait_ready(int port)
49{
50 while (true)
51 {
52 bootflash_ce(port, true);
53 spi_write(port, 5);
54 if (!(spi_write(port, 0xff) & 1)) break;
55 bootflash_ce(port, false);
56 }
57 bootflash_ce(port, false);
58}
59
60static void bootflash_enable_writing(int port, bool state)
61{
62 if (!state)
63 {
64 bootflash_ce(port, true);
65 spi_write(port, 4);
66 bootflash_ce(port, false);
67 }
68 bootflash_ce(port, true);
69 spi_write(port, 0x50);
70 bootflash_ce(port, false);
71 bootflash_ce(port, true);
72 spi_write(port, 1);
73 spi_write(port, state ? 0 : 0x1c);
74 bootflash_ce(port, false);
75 if (state)
76 {
77 bootflash_ce(port, true);
78 spi_write(port, 6);
79 bootflash_ce(port, false);
80 }
81}
82
83void bootflash_read(int port, uint32_t addr, uint32_t size, void* buf)
84{
85 spi_prepare(port);
86 bootflash_wait_ready(port);
87 bootflash_ce(port, true);
88 spi_write(port, 3);
89 spi_write(port, (addr >> 16) & 0xff);
90 spi_write(port, (addr >> 8) & 0xff);
91 spi_write(port, addr & 0xff);
92 spi_read(port, size, buf);
93 bootflash_ce(port, false);
94 spi_release(port);
95}
96
97static void bootflash_write_internal(int port, uint32_t addr, uint32_t size, void* buf)
98{
99 uint8_t* buffer = (uint8_t*)buf;
100 bool first = true;
101 spi_prepare(port);
102 bootflash_wait_ready(port);
103 bootflash_enable_writing(port, true);
104 while (size)
105 {
106 bootflash_ce(port, true);
107 spi_write(port, 0xad);
108 if (first)
109 {
110 spi_write(port, (addr >> 16) & 0xff);
111 spi_write(port, (addr >> 8) & 0xff);
112 spi_write(port, addr & 0xff);
113 first = false;
114 }
115 spi_write(port, *buffer++);
116 spi_write(port, *buffer++);
117 bootflash_ce(port, false);
118 bootflash_wait_ready(port);
119 size -= 2;
120 }
121 bootflash_enable_writing(port, false);
122 spi_release(port);
123}
124
125static void bootflash_erase_internal(int port, uint32_t addr)
126{
127 spi_prepare(port);
128 bootflash_wait_ready(port);
129 bootflash_enable_writing(port, true);
130 bootflash_ce(port, true);
131 spi_write(port, 0x20);
132 spi_write(port, (addr >> 16) & 0xff);
133 spi_write(port, (addr >> 8) & 0xff);
134 spi_write(port, addr & 0xff);
135 bootflash_ce(port, false);
136 bootflash_enable_writing(port, false);
137 spi_release(port);
138}
139
140void bootflash_erase_blocks(int port, int first, int n)
141{
142 uint32_t offset = first << 12;
143 while (n--)
144 {
145 bootflash_erase_internal(port, offset);
146 offset += 0x1000;
147 }
148}
149
150int bootflash_compare(int port, int offset, void* addr, int size)
151{
152 int i;
153 int result = 0;
154 uint8_t buf[32];
155 spi_prepare(port);
156 bootflash_wait_ready(port);
157 bootflash_ce(port, true);
158 spi_write(port, 3);
159 spi_write(port, (offset >> 16) & 0xff);
160 spi_write(port, (offset >> 8) & 0xff);
161 spi_write(port, offset & 0xff);
162 while (size > 0)
163 {
164 spi_read(port, MIN((int)sizeof(buf), size), buf);
165 if (memcmp((uint8_t*)addr, buf, MIN((int)sizeof(buf), size)))
166 result |= 1;
167 for (i = 0; i < MIN((int)sizeof(buf), size); i += 2)
168 if (buf[i] != 0xff) result |= 2;
169 addr = (void*)(((uint32_t)addr) + sizeof(buf));
170 size -= sizeof(buf);
171 }
172 bootflash_ce(port, false);
173 spi_release(port);
174 return result;
175}
176
177void bootflash_write(int port, int offset, void* addr, int size)
178{
179 int i;
180 bool needswrite;
181 while (size > 0)
182 {
183 int remainder = MIN(0x1000 - (offset & 0xfff), size);
184 int contentinfo = bootflash_compare(port, offset, addr, remainder);
185 if (contentinfo & 1)
186 {
187 if (contentinfo & 2)
188 bootflash_erase_internal(port, offset & ~0xfff);
189 needswrite = false;
190 for (i = 0; i < remainder; i += 1)
191 if (((uint8_t*)addr)[i] != 0xff)
192 {
193 needswrite = true;
194 break;
195 }
196 if (needswrite)
197 bootflash_write_internal(port, offset, remainder, addr);
198 }
199 addr = (void*)(((uint32_t)addr) + remainder);
200 offset += remainder;
201 size -= remainder;
202 }
203}
204
205
206/*
207 * IM3
208 */
209static uint32_t get_uint32le(unsigned char *p)
210{
211 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
212}
213
214/* return full IM3 size aligned to NOR sector size */
215unsigned im3_nor_sz(struct Im3Info* hinfo)
216{
217 return ALIGN_UP(IM3HDR_SZ + get_uint32le(hinfo->data_sz), 0x1000);
218}
219
220/* calculates SHA1, truncate the result to 128 bits, and encrypt it */
221void im3_sign(uint32_t keyidx, void* data, uint32_t size, void* sign)
222{
223 unsigned char hash[SHA1_SZ];
224 sha1(data, size, hash);
225 memcpy(sign, hash, SIGN_SZ);
226 hwkeyaes(HWKEYAES_ENCRYPT, keyidx, sign, SIGN_SZ);
227}
228
229/* only supports enc_type 1 and 2 (UKEY) */
230void im3_crypt(enum hwkeyaes_direction direction,
231 struct Im3Info *hinfo, void *fw_addr)
232{
233 uint32_t fw_size = get_uint32le(hinfo->data_sz);
234 hinfo->enc_type = (direction == HWKEYAES_ENCRYPT) ? 1 : 2;
235 im3_sign(HWKEYAES_UKEY, hinfo, IM3INFOSIGN_SZ, hinfo->info_sign);
236 hwkeyaes(direction, HWKEYAES_UKEY, fw_addr, fw_size);
237}
238
239int im3_read(uint32_t offset, struct Im3Info *hinfo, void *fw_addr)
240{
241 unsigned char hash[SIGN_SZ];
242 uint32_t fw_size;
243
244 /* header */
245 bootflash_init(SPI_PORT);
246 bootflash_read(SPI_PORT, offset, IM3HDR_SZ, hinfo);
247 bootflash_close(SPI_PORT);
248
249 if (memcmp(hinfo, IM3_IDENT, 4) != 0)
250 return -1; /* OF not found */
251
252 im3_sign(HWKEYAES_UKEY, hinfo, IM3INFOSIGN_SZ, hash);
253 if (memcmp(hash, hinfo->info_sign, SIGN_SZ) != 0)
254 return -2; /* corrupt header */
255
256 fw_size = get_uint32le(hinfo->data_sz);
257 if ((fw_size > NORBOOT_MAXSZ - IM3HDR_SZ) ||
258 (get_uint32le(hinfo->entry) > fw_size))
259 return -3; /* wrong info */
260
261 if (hinfo->enc_type != 1 && hinfo->enc_type != 2)
262 return -4; /* encrypt type not supported */
263
264 if (fw_addr)
265 {
266 /* body */
267 bootflash_init(SPI_PORT);
268 bootflash_read(SPI_PORT, offset + IM3HDR_SZ, fw_size, fw_addr);
269 bootflash_close(SPI_PORT);
270 if (hinfo->enc_type == 1)
271 im3_crypt(HWKEYAES_DECRYPT, hinfo, fw_addr);
272 im3_sign(HWKEYAES_UKEY, fw_addr, fw_size, hash);
273 if (memcmp(hash, hinfo->u.enc12.data_sign, SIGN_SZ) != 0)
274 return -5; /* corrupt data */
275 }
276
277 return 0;
278}
279
280bool im3_write(int offset, void *im3_addr)
281{
282 bool res;
283 struct Im3Info *hinfo = (struct Im3Info *)im3_addr;
284 uint32_t im3_size = get_uint32le(hinfo->data_sz) + IM3HDR_SZ;
285 bootflash_init(SPI_PORT);
286 bootflash_write(SPI_PORT, offset, im3_addr, im3_size);
287 /* check if the IM3 image was written correctly */
288 res = !(bootflash_compare(SPI_PORT, offset, im3_addr, im3_size) & 1);
289 bootflash_close(SPI_PORT);
290 return res;
291}
292
293
294/*
295 * flsh FS
296 */
297unsigned flsh_get_unused(void)
298{
299 unsigned tail = DIR_OFF;
300 bootflash_init(SPI_PORT);
301 for (int i = 0; i < MAX_ENTRY; i++)
302 {
303 image_t entry;
304 bootflash_read(SPI_PORT, DIR_OFF + ENTRY_SZ*i, ENTRY_SZ, &entry);
305 if (memcmp(entry.type, "hslf", 4))
306 break; /* no more entries */
307 if (entry.devOffset < tail)
308 tail = entry.devOffset;
309 }
310 bootflash_close(SPI_PORT);
311 return tail - (NORBOOT_OFF + NORBOOT_MAXSZ);
312}
313
314int flsh_find_file(char *name, image_t *entry)
315{
316 int found = 0;
317 bootflash_init(SPI_PORT);
318 for (int off = 0; off < ENTRY_SZ*MAX_ENTRY; off += ENTRY_SZ)
319 {
320 bootflash_read(SPI_PORT, DIR_OFF+off, ENTRY_SZ, entry);
321 if (memcmp(&(entry->type), "hslf", 4))
322 break; /* no more entries */
323 if (!memcmp(&(entry->id), name, 4)) {
324 found = 1;
325 break;
326 }
327 }
328 bootflash_close(SPI_PORT);
329 return found;
330}
331
332#define KEYIDX_OFF 0x8 /* TBC */
333#define DATALEN_OFF 0x14
334#define DATASHA_OFF 0x1c
335#define HEADSHA_OFF 0x1d4
336
337int flsh_load_file(image_t *entry, void *hdr, void *data)
338{
339 uint8_t orig_hash[SHA1_SZ];
340 uint8_t calc_hash[SHA1_SZ];
341 uint32_t data_len;
342
343 /* header */
344 bootflash_init(SPI_PORT);
345 bootflash_read(SPI_PORT, entry->devOffset, FILEHDR_SZ, hdr);
346 bootflash_close(SPI_PORT);
347 memcpy(orig_hash, hdr + HEADSHA_OFF, SHA1_SZ);
348 memset(hdr+HEADSHA_OFF, 0, SHA1_SZ);
349 sha1(hdr, FILEHDR_SZ, calc_hash);
350 if (memcmp(calc_hash, orig_hash, SHA1_SZ))
351 return -1; /* corrupt header */
352 memcpy(hdr+HEADSHA_OFF, orig_hash, SHA1_SZ);
353 data_len = ALIGN_UP(*(uint32_t*)(hdr+DATALEN_OFF), 0x10);
354 if (data_len > ALIGN_UP(entry->len, 0x10))
355 return -2; /* bad size */
356
357 if (data)
358 {
359 bootflash_init(SPI_PORT);
360 bootflash_read(SPI_PORT, entry->devOffset+FILEHDR_SZ, data_len, data);
361 bootflash_close(SPI_PORT);
362 hwkeyaes(HWKEYAES_DECRYPT, *(uint8_t*)(hdr+KEYIDX_OFF), data, data_len);
363 sha1(data, data_len, calc_hash);
364 if (memcmp(calc_hash, hdr+DATASHA_OFF, SHA1_SZ))
365 return -3; /* corrupt data */
366 }
367
368 return 0;
369}
diff --git a/firmware/target/arm/s5l8702/nor-target.h b/firmware/target/arm/s5l8702/nor-target.h
new file mode 100644
index 0000000000..4ebe1d58d4
--- /dev/null
+++ b/firmware/target/arm/s5l8702/nor-target.h
@@ -0,0 +1,167 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id:
9 *
10 * Copyright © 2009 Michael Sparmann
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef __NOR_TARGET_H__
22#define __NOR_TARGET_H__
23
24#include <stdint.h>
25#include <stdbool.h>
26
27#include "config.h"
28#include "crypto-s5l8702.h"
29
30
31/* NOR memory map (Classic 6G):
32 *
33 * 1MB ______________
34 * | |
35 * | flsh DIR |
36 * 1MB-0x200 |______________|
37 * | |
38 * | File 1 |
39 * |..............|
40 * | |
41 * | File 2 |
42 * |..............|
43 * | |
44 * . .
45 * . .
46 * . .
47 * | |
48 * |..............|
49 * | |
50 * | File N |
51 * |______________|
52 * | |
53 * | |
54 * | |
55 * | | IM3 (128KB)
56 * | Unused | . . . . . . ______________
57 * | | / | |
58 * | | / | |
59 * | | / | |
60 * 160KB |______________|/ | |
61 * | | | |
62 * | | | EFI |
63 * | Original | | Volumen |
64 * | NOR Boot | | |
65 * | | | |
66 * | | | |
67 * 32KB |______________| | |
68 * | |\ 0x800 |______________|
69 * | | \ | |
70 * | | \ | IM3 Header |
71 * |______________| \ . . . . . . |______________|
72 * | |
73 * | SysCfg |
74 * 0 |______________|
75 */
76
77#define SPI_PORT 0
78
79#define NOR_SZ 0x100000
80#define NORBOOT_OFF 0x8000
81#define NORBOOT_MAXSZ 0x20000
82
83void bootflash_init(int port);
84void bootflash_read(int port, uint32_t addr, uint32_t size, void* buf);
85void bootflash_write(int port, int offset, void* addr, int size);
86int bootflash_compare(int port, int offset, void* addr, int size);
87void bootflash_erase_blocks(int port, int first, int n);
88void bootflash_close(int port);
89
90
91/*
92 * IM3
93 */
94#define IM3_IDENT "8702"
95#define IM3_VERSION "1.0"
96#define IM3HDR_SZ 0x800
97#define IM3INFO_SZ (sizeof(struct Im3Info))
98#define IM3INFOSIGN_SZ (offsetof(struct Im3Info, info_sign))
99
100#define SIGN_SZ 16
101
102struct Im3Info
103{
104 uint8_t ident[4];
105 uint8_t version[3];
106 uint8_t enc_type;
107 uint8_t entry[4]; /* LE */
108 uint8_t data_sz[4]; /* LE */
109 union {
110 struct {
111 uint8_t data_sign[SIGN_SZ];
112 uint8_t _reserved[32];
113 } enc12;
114 struct {
115 uint8_t sign_off[4]; /* LE */
116 uint8_t cert_off[4]; /* LE */
117 uint8_t cert_sz[4]; /* LE */
118 uint8_t _reserved[36];
119 } enc34;
120 } u;
121 uint8_t info_sign[SIGN_SZ];
122} __attribute__ ((packed));
123
124struct Im3Hdr
125{
126 struct Im3Info info;
127 uint8_t _zero[IM3HDR_SZ - sizeof(struct Im3Info)];
128} __attribute__ ((packed));
129
130unsigned im3_nor_sz(struct Im3Info* hinfo);
131void im3_sign(uint32_t keyidx, void* data, uint32_t size, void* sign);
132void im3_crypt(enum hwkeyaes_direction direction,
133 struct Im3Info *hinfo, void *fw_addr);
134int im3_read(uint32_t offset, struct Im3Info *hinfo, void *fw_addr);
135bool im3_write(int offset, void *im3_addr);
136
137
138/*
139 * flsh FS
140 */
141#define DIR_SZ 0x200
142#define DIR_OFF (NOR_SZ - DIR_SZ)
143#define ENTRY_SZ ((int)sizeof(image_t))
144#define MAX_ENTRY (DIR_SZ / ENTRY_SZ)
145
146#define FILEHDR_SZ 0x200
147
148/* from tools/ipod_fw.c */
149typedef struct _image {
150 char type[4]; /* '' */
151 unsigned id; /* */
152 char pad1[4]; /* 0000 0000 */
153 unsigned devOffset; /* byte offset of start of image code */
154 unsigned len; /* length in bytes of image */
155 unsigned addr; /* load address */
156 unsigned entryOffset; /* execution start within image */
157 unsigned chksum; /* checksum for image */
158 unsigned vers; /* image version */
159 unsigned loadAddr; /* load address for image */
160} image_t;
161
162int flsh_get_info(int *used, int *unused);
163unsigned flsh_get_unused(void);
164int flsh_find_file(char *name, image_t *entry);
165int flsh_load_file(image_t *entry, void *hdr, void *data);
166
167#endif /* __NOR_TARGET_H__ */