diff options
author | James Buren <braewoods+rb@braewoods.net> | 2020-11-11 10:54:05 +0000 |
---|---|---|
committer | James Buren <braewoods+rb@braewoods.net> | 2020-11-11 13:30:09 +0000 |
commit | 4aa749b59db9fb46a47a0bcff4f19a6f0552b2aa (patch) | |
tree | 39784c771741613847e83dce95b122f27cf485e6 /apps/plugins | |
parent | c4254d10fc61779656dd5692c2f6333692ba73f8 (diff) | |
download | rockbox-4aa749b59db9fb46a47a0bcff4f19a6f0552b2aa.tar.gz rockbox-4aa749b59db9fb46a47a0bcff4f19a6f0552b2aa.zip |
iriver_flash: refactor code to be easier to understand
This overhauls most of the code to be easier to understand in terms
of the interactions with the flash. I found the original to be rather
confusing with how it kept switching between byte and word offsets.
My solution was to make all external access to the flash in terms of
sectors and bytes. Whatever the flash uses internally is now handled
by the subroutines for performing the erase, program, and verify
operations.
This helps make it far more consistent for the code that actually uses
these operations as they do not need to concern themselves with word
sizes and offsets anymore.
As a side effect of this change the flash operations are now done
entirely by subroutines; even the batch operations that used to use
custom loops.
Additionally some functions were merged with other functions in order
to reduce the amount of functions as well as consolidating common
code fragments.
Change-Id: I4698e920a226a3bbe8070004a14e5848abdd70ec
Diffstat (limited to 'apps/plugins')
-rw-r--r-- | apps/plugins/iriver_flash.c | 1123 |
1 files changed, 612 insertions, 511 deletions
diff --git a/apps/plugins/iriver_flash.c b/apps/plugins/iriver_flash.c index 9d1c3456a6..e5da55c7ed 100644 --- a/apps/plugins/iriver_flash.c +++ b/apps/plugins/iriver_flash.c | |||
@@ -9,7 +9,8 @@ | |||
9 | * | 9 | * |
10 | * !!! DON'T MESS WITH THIS CODE UNLESS YOU'RE ABSOLUTELY SURE WHAT YOU DO !!! | 10 | * !!! DON'T MESS WITH THIS CODE UNLESS YOU'RE ABSOLUTELY SURE WHAT YOU DO !!! |
11 | * | 11 | * |
12 | * Copyright (C) 2006 by Miika Pekkarinen | 12 | * Copyright (C) 2020 by James Buren (refactor + H300 support) |
13 | * Copyright (C) 2006 by Miika Pekkarinen (original + H100/H120 support) | ||
13 | * | 14 | * |
14 | * This program is free software; you can redistribute it and/or | 15 | * This program is free software; you can redistribute it and/or |
15 | * modify it under the terms of the GNU General Public License | 16 | * modify it under the terms of the GNU General Public License |
@@ -22,93 +23,185 @@ | |||
22 | ****************************************************************************/ | 23 | ****************************************************************************/ |
23 | #include "plugin.h" | 24 | #include "plugin.h" |
24 | 25 | ||
25 | /* cfi_program_word() relies on writing to address 0, which normally is illegal. | 26 | /* |
26 | So we need this to ensure we don't helpfully optimize it away into a TRAP | 27 | * Flash commands may rely on null pointer dereferences to work correctly. |
27 | when compiled with -fdelete-null-pointer-checks, which is enabled by default | 28 | * Disable this feature of GCC that may interfere with proper code generation. |
28 | at -Os with our current gcc 4.9.x toolchain. | 29 | */ |
29 | */ | ||
30 | #pragma GCC optimize "no-delete-null-pointer-checks" | 30 | #pragma GCC optimize "no-delete-null-pointer-checks" |
31 | 31 | ||
32 | /* All CFI flash routines are copied and ported from firmware_flash.c */ | 32 | enum firmware |
33 | { | ||
34 | FIRMWARE_ROCKBOX, /* all .iriver firmwares */ | ||
35 | FIRMWARE_ROMDUMP, /* a debug romdump */ | ||
36 | FIRMWARE_ORIGINAL, /* an unscrambled original firmware */ | ||
37 | }; | ||
38 | |||
39 | #define WORD_SIZE 2 | ||
40 | #define BOOT_VECTOR_SIZE 8 | ||
41 | #define BOOT_SECTOR_OFFSET 0 | ||
42 | #define SECTOR_SIZE 4096 | ||
43 | #define BOOTLOADER_MAX_SIZE 65536 | ||
44 | #define BOOTLOADER_SECTORS (BOOTLOADER_MAX_SIZE / SECTOR_SIZE) | ||
45 | #define RAM_IMAGE_RAW_SIZE (FLASH_ROMIMAGE_ENTRY - FLASH_RAMIMAGE_ENTRY) | ||
46 | #define RAM_IMAGE_MAX_SIZE (RAM_IMAGE_RAW_SIZE - sizeof(struct flash_header)) | ||
47 | #define RAM_IMAGE_SECTORS (RAM_IMAGE_RAW_SIZE / SECTOR_SIZE) | ||
48 | #define ROM_IMAGE_RAW_SIZE (BOOTLOADER_ENTRYPOINT - FLASH_ROMIMAGE_ENTRY) | ||
49 | #define ROM_IMAGE_MAX_SIZE (ROM_IMAGE_RAW_SIZE - sizeof(struct flash_header)) | ||
50 | #define ROM_IMAGE_SECTORS (ROM_IMAGE_RAW_SIZE / SECTOR_SIZE) | ||
51 | #define ROM_IMAGE_RELOCATION (FLASH_ROMIMAGE_ENTRY + sizeof(struct flash_header)) | ||
52 | #define WHOLE_FIRMWARE_SECTORS (BOOTLOADER_ENTRYPOINT / SECTOR_SIZE) | ||
53 | #define FIRMWARE_OFFSET 544 | ||
54 | |||
55 | #if BOOTLOADER_ENTRYPOINT + BOOTLOADER_MAX_SIZE != FLASH_SIZE | ||
56 | #error "Bootloader is not located at the end of flash." | ||
57 | #endif | ||
33 | 58 | ||
34 | uint8_t* audiobuf; | 59 | #if FLASH_ROMIMAGE_ENTRY < FLASH_RAMIMAGE_ENTRY |
35 | ssize_t audiobuf_size; | 60 | #error "RAM image must be located before the ROM image." |
61 | #endif | ||
62 | |||
63 | #if BOOTLOADER_ENTRYPOINT < FLASH_ROMIMAGE_ENTRY | ||
64 | #error "ROM image must be located before the bootloader." | ||
65 | #endif | ||
36 | 66 | ||
37 | #if !defined(IRIVER_H100_SERIES) && !defined(IRIVER_H300_SERIES) | 67 | #if FLASH_SIZE == 2048 * 1024 |
38 | #error this platform is not (yet) flashable | 68 | #define ROMDUMP "/internal_rom_000000-1FFFFF.bin" |
69 | #elif FLASH_SIZE == 4096 * 1024 | ||
70 | #define ROMDUMP "/internal_rom_000000-3FFFFF.bin" | ||
39 | #endif | 71 | #endif |
40 | 72 | ||
41 | #if CONFIG_KEYPAD == IRIVER_H100_PAD | 73 | #ifdef IRIVER_H100 |
42 | #define KEY1 BUTTON_OFF | 74 | #define MODEL (const uint8_t[]) { 'h', '1', '0', '0' } |
43 | #define KEY2 BUTTON_ON | 75 | #define ORIGINAL "/ihp_100.bin" |
44 | #define KEY3 BUTTON_SELECT | 76 | #elif defined(IRIVER_H120) |
45 | #define KEYNAME1 "[Stop]" | 77 | #define MODEL (const uint8_t[]) { 'h', '1', '2', '0' } |
46 | #define KEYNAME2 "[On]" | 78 | #define ORIGINAL "/ihp_120.bin" |
47 | #define KEYNAME3 "[Select]" | 79 | #elif defined(IRIVER_H300) |
80 | #define MODEL (const uint8_t[]) { 'h', '3', '0', '0' } | ||
81 | #define ORIGINAL "/H300.bin" | ||
82 | #else | ||
83 | #error "Unsupported target." | ||
48 | #endif | 84 | #endif |
49 | 85 | ||
50 | struct flash_info | 86 | struct flash_info |
51 | { | 87 | { |
52 | uint16_t manufacturer; | 88 | uint16_t vendor; |
53 | uint16_t id; | 89 | uint16_t product; |
54 | uint32_t size; | 90 | uint32_t size; |
55 | char name[32]; | 91 | char name[16]; |
56 | }; | 92 | }; |
57 | 93 | ||
58 | #if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES) | 94 | /* checks if the region has a valid bootloader */ |
59 | #define SEC_SIZE 4096 | 95 | static bool detect_valid_bootloader(const void* ptr, uint32_t size) |
60 | #define BOOTLOADER_ERASEGUARD (BOOTLOADER_ENTRYPOINT / SEC_SIZE) | 96 | { |
61 | enum sections { | 97 | static const struct |
62 | SECT_RAMIMAGE = 1, | 98 | { |
63 | SECT_ROMIMAGE = 2, | 99 | uint32_t size; |
64 | }; | 100 | uint32_t crc32; |
65 | 101 | } | |
66 | static volatile uint16_t* FB = (uint16_t*)0x00000000; /* Flash base address */ | 102 | bootloaders[] = |
103 | { | ||
104 | #ifdef IRIVER_H100 | ||
105 | { 48760, 0x2efc3323 }, /* 7-pre4 */ | ||
106 | { 56896, 0x0cd8dad4 }, /* 7-pre5 */ | ||
107 | #elif defined(IRIVER_H120) | ||
108 | { 63788, 0x08ff01a9 }, /* 7-pre3, improved failsafe functions */ | ||
109 | { 48764, 0xc674323e }, /* 7-pre4. Fixed audio thump & remote bootup */ | ||
110 | { 56896, 0x167f5d25 }, /* 7-pre5, various ATA fixes */ | ||
111 | #elif defined(IRIVER_H300) | ||
67 | #endif | 112 | #endif |
113 | {0} | ||
114 | }; | ||
68 | 115 | ||
69 | #ifdef IRIVER_H100 /* iRiver H110/H115 */ | 116 | for (size_t i = 0; bootloaders[i].size != 0; i++) |
70 | #define MODEL "h100" | 117 | { |
71 | #elif defined(IRIVER_H120) /* iRiver H120/H140 */ | 118 | uint32_t crc32; |
72 | #define MODEL "h120" | 119 | |
73 | #elif defined(IRIVER_H300) /* iRiver H320/H340 */ | 120 | if (size != 0 && size != bootloaders[i].size) |
74 | #define MODEL "h300" | 121 | continue; |
75 | #endif | 122 | |
123 | crc32 = rb->crc_32(ptr, bootloaders[i].size, 0xFFFFFFFF); | ||
124 | if (crc32 == bootloaders[i].crc32) | ||
125 | return true; | ||
126 | } | ||
127 | |||
128 | return false; | ||
129 | } | ||
130 | |||
131 | /* get read-only access to flash at the given offset */ | ||
132 | static const void* flash(uint32_t offset) | ||
133 | { | ||
134 | const uint16_t* FB = (uint16_t*) FLASH_BASE; | ||
135 | return &FB[offset / WORD_SIZE]; | ||
136 | } | ||
76 | 137 | ||
77 | /* read the manufacturer and device ID */ | 138 | /* queries the rom for information and returns it if it is known */ |
78 | static void cfi_read_id(struct flash_info* pInfo) | 139 | static bool flash_get_info(const struct flash_info** out_info) |
79 | { | 140 | { |
80 | FB[0x5555] = 0xAA; /* enter command mode */ | 141 | static const struct flash_info roms[] = |
142 | { | ||
143 | { 0x00BF, 0x2782, 2048 * 1024, "SST39VF160" }, | ||
144 | { 0x00BF, 0x235B, 4096 * 1024, "SST39VF3201" }, | ||
145 | {0} | ||
146 | }; | ||
147 | static struct flash_info unknown_rom = {0}; | ||
148 | volatile uint16_t* FB = (uint16_t*) FLASH_BASE; | ||
149 | uint16_t vendor; | ||
150 | uint16_t product; | ||
151 | |||
152 | /* execute the software ID entry command */ | ||
153 | FB[0x5555] = 0xAA; | ||
81 | FB[0x2AAA] = 0x55; | 154 | FB[0x2AAA] = 0x55; |
82 | FB[0x5555] = 0x90; /* enter ID mode */ | 155 | FB[0x5555] = 0x90; |
83 | rb->sleep(HZ/100); /* we only need to sleep 150ns but 10ms is the minimum */ | 156 | rb->sleep(HZ / 100); |
84 | 157 | ||
85 | pInfo->manufacturer = FB[0]; | 158 | /* copy the IDs from the previous command */ |
86 | pInfo->id = FB[1]; | 159 | vendor = FB[0]; |
160 | product = FB[1]; | ||
87 | 161 | ||
88 | FB[0x5555] = 0xAA; /* enter command mode */ | 162 | /* execute the software ID exit command */ |
163 | FB[0x5555] = 0xAA; | ||
89 | FB[0x2AAA] = 0x55; | 164 | FB[0x2AAA] = 0x55; |
90 | FB[0x5555] = 0xF0; /* exit ID mode */ | 165 | FB[0x5555] = 0xF0; |
91 | rb->sleep(HZ/100); /* we only need to sleep 150ns but 10ms is the minimum */ | 166 | rb->sleep(HZ / 100); |
167 | |||
168 | /* search for a known match */ | ||
169 | for (size_t i = 0; roms[i].size != 0; i++) | ||
170 | { | ||
171 | if (roms[i].vendor == vendor && roms[i].product == product) | ||
172 | { | ||
173 | *out_info = &roms[i]; | ||
174 | return true; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | /* return only the vendor / product ids if unknown */ | ||
179 | unknown_rom.vendor = vendor; | ||
180 | unknown_rom.product = product; | ||
181 | *out_info = &unknown_rom; | ||
182 | return false; | ||
92 | } | 183 | } |
93 | 184 | ||
94 | /* wait until the rom signals completion of an operation */ | 185 | /* wait until the rom signals completion of an operation */ |
95 | static bool cfi_wait_for_rom(volatile uint16_t* pAddr) | 186 | static bool flash_wait_for_rom(uint32_t offset) |
96 | { | 187 | { |
97 | const unsigned MAX_TIMEOUT = 0xffffff; /* should be sufficient for most targets */ | 188 | const size_t MAX_TIMEOUT = 0xFFFFFF; /* should be sufficient for most targets */ |
98 | const unsigned RECOVERY_TIME = 64; /* based on 140MHz MCF 5249 */ | 189 | const size_t RECOVERY_TIME = 64; /* based on 140MHz MCF 5249 */ |
99 | uint16_t old_data = *pAddr & 0x0040; /* we only want DQ6 */ | 190 | volatile uint16_t* FB = (uint16_t*) FLASH_BASE; |
100 | volatile unsigned i; /* disables certain optimizations */ | 191 | uint16_t old_data = FB[offset / WORD_SIZE] & 0x0040; /* we only want DQ6 */ |
192 | volatile size_t i; /* disables certain optimizations */ | ||
193 | bool result; | ||
101 | 194 | ||
102 | /* repeat up to MAX_TIMEOUT times or until DQ6 stops flipping */ | 195 | /* repeat up to MAX_TIMEOUT times or until DQ6 stops flipping */ |
103 | for (i = 0; i < MAX_TIMEOUT; i++) | 196 | for (i = 0; i < MAX_TIMEOUT; i++) |
104 | { | 197 | { |
105 | uint16_t new_data = *pAddr & 0x0040; /* we only want DQ6 */ | 198 | uint16_t new_data = FB[offset / WORD_SIZE] & 0x0040; /* we only want DQ6 */ |
106 | if(old_data == new_data) | 199 | if (old_data == new_data) |
107 | break; | 200 | break; |
108 | old_data = new_data; | 201 | old_data = new_data; |
109 | } | 202 | } |
110 | 203 | ||
111 | bool result = i != MAX_TIMEOUT; | 204 | result = i != MAX_TIMEOUT; |
112 | 205 | ||
113 | /* delay at least 1us to give the bus time to recover */ | 206 | /* delay at least 1us to give the bus time to recover */ |
114 | for (i = 0; i < RECOVERY_TIME; i++); | 207 | for (i = 0; i < RECOVERY_TIME; i++); |
@@ -116,646 +209,654 @@ static bool cfi_wait_for_rom(volatile uint16_t* pAddr) | |||
116 | return result; | 209 | return result; |
117 | } | 210 | } |
118 | 211 | ||
119 | /* erase the sector which contains the given address */ | 212 | /* erase the sector at the given offset */ |
120 | static bool cfi_erase_sector(volatile uint16_t* pAddr) | 213 | static bool flash_erase_sector(uint32_t offset) |
121 | { | 214 | { |
122 | FB[0x5555] = 0xAA; /* enter command mode */ | 215 | volatile uint16_t* FB = (uint16_t*) FLASH_BASE; |
216 | |||
217 | /* execute the sector erase command */ | ||
218 | FB[0x5555] = 0xAA; | ||
123 | FB[0x2AAA] = 0x55; | 219 | FB[0x2AAA] = 0x55; |
124 | FB[0x5555] = 0x80; /* erase command */ | 220 | FB[0x5555] = 0x80; |
125 | FB[0x5555] = 0xAA; /* enter command mode */ | 221 | FB[0x5555] = 0xAA; |
126 | FB[0x2AAA] = 0x55; | 222 | FB[0x2AAA] = 0x55; |
127 | *pAddr = 0x30; /* erase the sector */ | 223 | FB[offset / WORD_SIZE] = 0x30; |
128 | 224 | ||
129 | return cfi_wait_for_rom(pAddr); | 225 | return flash_wait_for_rom(offset); |
130 | } | 226 | } |
131 | 227 | ||
132 | /* address must be in an erased location */ | 228 | /* program a word at the given offset */ |
133 | static bool cfi_program_word(volatile uint16_t* pAddr, uint16_t data) | 229 | static bool flash_program_word(uint32_t offset, uint16_t word) |
134 | { | 230 | { |
135 | FB[0x5555] = 0xAA; /* enter command mode */ | 231 | volatile uint16_t* FB = (uint16_t*) FLASH_BASE; |
232 | |||
233 | /* execute the word program command */ | ||
234 | FB[0x5555] = 0xAA; | ||
136 | FB[0x2AAA] = 0x55; | 235 | FB[0x2AAA] = 0x55; |
137 | FB[0x5555] = 0xA0; /* byte program command */ | 236 | FB[0x5555] = 0xA0; |
138 | *pAddr = data; /* write the word data */ | 237 | FB[offset / WORD_SIZE] = word; |
139 | 238 | ||
140 | return cfi_wait_for_rom(pAddr); | 239 | return flash_wait_for_rom(offset); |
141 | } | 240 | } |
142 | 241 | ||
143 | /* fills in the struct with data about the flash rom */ | 242 | /* bulk erase of adjacent sectors */ |
144 | static void cfi_get_flash_info(struct flash_info* pInfo) | 243 | static void flash_erase_sectors(uint32_t offset, uint32_t sectors, |
244 | bool progress) | ||
145 | { | 245 | { |
146 | uint32_t size = 0; | 246 | for (uint32_t i = 0; i < sectors; i++) |
147 | const char* name = ""; | 247 | { |
148 | 248 | flash_erase_sector(offset + i * SECTOR_SIZE); | |
149 | cfi_read_id(pInfo); | ||
150 | |||
151 | switch (pInfo->manufacturer) | ||
152 | { | ||
153 | /* SST */ | ||
154 | case 0x00BF: | ||
155 | switch (pInfo->id) | ||
156 | { | ||
157 | case 0x2782: | ||
158 | size = 2048 * 1024; /* 2 MiB */ | ||
159 | name = "SST39VF160"; | ||
160 | break; | ||
161 | case 0x235B: | ||
162 | size = 4096 * 1024; /* 4 MiB */ | ||
163 | name = "SST39VF3201"; | ||
164 | break; | ||
165 | } | ||
166 | break; | ||
167 | } | ||
168 | 249 | ||
169 | pInfo->size = size; | 250 | /* display a progress report if requested */ |
170 | rb->strcpy(pInfo->name, name); | 251 | if (progress) |
252 | { | ||
253 | rb->lcd_putsf(0, 3, "Erasing... %u%%", (i + 1) * 100 / sectors); | ||
254 | rb->lcd_update(); | ||
255 | } | ||
256 | } | ||
171 | } | 257 | } |
172 | 258 | ||
173 | /***************** User Interface Functions *****************/ | 259 | /* bulk program of bytes */ |
174 | static int wait_for_button(void) | 260 | static void flash_program_bytes(uint32_t offset, const void* ptr, |
261 | uint32_t len, bool progress) | ||
175 | { | 262 | { |
176 | int button; | 263 | const uint8_t* data = ptr; |
177 | 264 | ||
178 | do | 265 | for (uint32_t i = 0; i < len; i += WORD_SIZE) |
179 | { | 266 | { |
180 | button = rb->button_get(true); | 267 | uint32_t j = i + 1; |
181 | } while (IS_SYSEVENT(button) || (button & BUTTON_REL)); | 268 | uint32_t k = ((j < len) ? j : i) + 1; |
269 | uint16_t word = (data[i] << 8) | (j < len ? data[j] : 0xFF); | ||
270 | |||
271 | flash_program_word(offset + i, word); | ||
182 | 272 | ||
183 | return button; | 273 | /* display a progress report if requested */ |
274 | if (progress && ((i % SECTOR_SIZE) == 0 || k == len)) | ||
275 | { | ||
276 | rb->lcd_putsf(0, 4, "Programming... %u%%", k * 100 / len); | ||
277 | rb->lcd_update(); | ||
278 | } | ||
279 | } | ||
184 | } | 280 | } |
185 | 281 | ||
186 | /* helper for DoUserDialog() */ | 282 | /* bulk verify of programmed bytes */ |
187 | static void ShowFlashInfo(const struct flash_info* pInfo) | 283 | static bool flash_verify_bytes(uint32_t offset, const void* ptr, |
284 | uint32_t len, bool progress) | ||
188 | { | 285 | { |
189 | if (!pInfo->manufacturer) | 286 | const uint8_t* FB = flash(offset); |
190 | { | 287 | const uint8_t* data = ptr; |
191 | rb->lcd_puts(0, 0, "Flash: M=???? D=????"); | 288 | |
192 | rb->lcd_puts(0, 1, "Impossible to program"); | 289 | /* don't use memcmp so we can provide progress updates */ |
193 | } | 290 | for (uint32_t i = 0; i < len; i++) |
194 | else | ||
195 | { | 291 | { |
196 | rb->lcd_putsf(0, 0, "Flash: M=%04x D=%04x", | 292 | uint32_t j = i + 1; |
197 | pInfo->manufacturer, pInfo->id); | ||
198 | 293 | ||
199 | if (pInfo->size) | 294 | if (FB[i] != data[i]) |
200 | { | 295 | return false; |
201 | rb->lcd_puts(0, 1, pInfo->name); | 296 | |
202 | rb->lcd_putsf(0, 2, "Size: %u KB", pInfo->size / 1024); | 297 | /* display a progress report if requested */ |
203 | } | 298 | if (progress && ((i % SECTOR_SIZE) == 0 || j == len)) |
204 | else | ||
205 | { | 299 | { |
206 | rb->lcd_puts(0, 1, "Unsupported chip"); | 300 | rb->lcd_putsf(0, 5, "Verifying... %u%%", j * 100 / len); |
301 | rb->lcd_update(); | ||
207 | } | 302 | } |
208 | } | 303 | } |
209 | 304 | ||
210 | rb->lcd_update(); | 305 | return true; |
211 | } | 306 | } |
212 | 307 | ||
308 | /* print information about the flash chip */ | ||
213 | static bool show_info(void) | 309 | static bool show_info(void) |
214 | { | 310 | { |
215 | struct flash_info fi; | 311 | static const struct flash_info* fi = NULL; |
216 | 312 | ||
217 | rb->lcd_clear_display(); | 313 | rb->lcd_clear_display(); |
218 | cfi_get_flash_info(&fi); | 314 | |
219 | ShowFlashInfo(&fi); | 315 | if (fi == NULL) |
220 | if (fi.size == 0) /* no valid chip */ | 316 | flash_get_info(&fi); |
317 | |||
318 | rb->lcd_putsf(0, 0, "Flash: V=%04x P=%04x", fi->vendor, fi->product); | ||
319 | |||
320 | if (fi->size != 0) | ||
321 | { | ||
322 | rb->lcd_puts(0, 1, fi->name); | ||
323 | rb->lcd_putsf(0, 2, "Size: %u KB", fi->size / 1024); | ||
324 | } | ||
325 | else | ||
221 | { | 326 | { |
222 | rb->splash(HZ*3, "Sorry!"); | 327 | rb->lcd_puts(0, 1, "Unknown chip"); |
223 | return false; /* exit */ | 328 | } |
329 | |||
330 | rb->lcd_update(); | ||
331 | |||
332 | if (fi->size == 0) | ||
333 | { | ||
334 | rb->splash(HZ * 3, "Sorry!"); | ||
335 | return false; | ||
224 | } | 336 | } |
225 | 337 | ||
226 | return true; | 338 | return true; |
227 | } | 339 | } |
228 | 340 | ||
229 | static bool confirm(const char* msg) | 341 | /* confirm a user's choice */ |
342 | static bool confirm_choice(const char* msg) | ||
230 | { | 343 | { |
344 | long button; | ||
231 | rb->splashf(0, "%s ([PLAY] to CONFIRM)", msg); | 345 | rb->splashf(0, "%s ([PLAY] to CONFIRM)", msg); |
232 | bool ret = (wait_for_button() == BUTTON_ON); | 346 | do |
347 | button = rb->button_get(true); | ||
348 | while (IS_SYSEVENT(button) || (button & BUTTON_REL)); | ||
233 | show_info(); | 349 | show_info(); |
234 | return ret; | 350 | return (button == BUTTON_ON); |
235 | } | 351 | } |
236 | 352 | ||
237 | static off_t load_firmware_file(const char* filename, uint32_t* out_checksum) | 353 | /* all-in-one firmware loader */ |
354 | static bool load_firmware(const char* filename, enum firmware firmware, | ||
355 | const void** data, size_t* data_len) | ||
238 | { | 356 | { |
239 | int fd; | 357 | bool result = false; |
240 | off_t len; | 358 | const char* msg = NULL; |
241 | uint32_t checksum; | 359 | int fd = -1; |
242 | char model[4]; | 360 | off_t fd_len; |
243 | uint32_t sum; | 361 | uint8_t* buffer; |
362 | size_t buffer_len; | ||
244 | 363 | ||
245 | fd = rb->open(filename, O_RDONLY); | 364 | fd = rb->open(filename, O_RDONLY); |
246 | if (fd < 0) | 365 | if (fd < 0) |
247 | return -1; | ||
248 | |||
249 | len = rb->filesize(fd); | ||
250 | if (audiobuf_size < len) | ||
251 | { | ||
252 | rb->splash(HZ*3, "Aborting: Out of memory!"); | ||
253 | rb->close(fd); | ||
254 | return -2; | ||
255 | } | ||
256 | |||
257 | if (rb->read(fd, &checksum, sizeof(checksum)) != sizeof(checksum)) | ||
258 | { | 366 | { |
259 | rb->splash(HZ*3, "Aborting: Read failure"); | 367 | msg = "Aborting: open failure"; |
260 | rb->close(fd); | 368 | goto bail; |
261 | return -3; | ||
262 | } | 369 | } |
263 | 370 | ||
264 | if (rb->read(fd, model, sizeof(model)) != sizeof(model)) | 371 | /* get file and buffer lengths and acquire the buffer */ |
265 | { | 372 | fd_len = rb->filesize(fd); |
266 | rb->splash(HZ*3, "Aborting: Read failure"); | 373 | buffer = rb->plugin_get_audio_buffer(&buffer_len); |
267 | rb->close(fd); | ||
268 | return -4; | ||
269 | } | ||
270 | 374 | ||
271 | len -= FIRMWARE_OFFSET_FILE_DATA; | 375 | /* ensure there's enough space in the buffer */ |
272 | 376 | if ((size_t) fd_len > buffer_len) | |
273 | if (rb->read(fd, audiobuf, len) != len) | ||
274 | { | 377 | { |
275 | rb->splash(HZ*3, "Aborting: Read failure"); | 378 | msg = "Aborting: out of memory"; |
276 | rb->close(fd); | 379 | goto bail; |
277 | return -5; | ||
278 | } | 380 | } |
279 | 381 | ||
280 | rb->close(fd); | 382 | /* all known firmwares are less than or equal to FLASH_SIZE */ |
281 | 383 | if (fd_len > FLASH_SIZE) | |
282 | /* Verify the checksum */ | ||
283 | sum = MODEL_NUMBER; | ||
284 | for (off_t i = 0; i < len; i++) | ||
285 | sum += audiobuf[i]; | ||
286 | |||
287 | if (sum != checksum) | ||
288 | { | ||
289 | rb->splash(HZ*3, "Aborting: Checksums mismatch!"); | ||
290 | return -6; | ||
291 | } | ||
292 | |||
293 | /* Verify the model */ | ||
294 | if (rb->memcmp(model, MODEL, sizeof(model)) != 0) | ||
295 | { | 384 | { |
296 | rb->splash(HZ*3, "Aborting: Models mismatch!"); | 385 | msg = "Aborting: firmware too big"; |
297 | return -7; | 386 | goto bail; |
298 | } | 387 | } |
299 | 388 | ||
300 | if (out_checksum != NULL) | 389 | /* rockbox firmware specific code */ |
301 | *out_checksum = checksum; | 390 | if (firmware == FIRMWARE_ROCKBOX) |
302 | |||
303 | return len; | ||
304 | } | ||
305 | |||
306 | static const uint32_t valid_bootloaders[][2] = { | ||
307 | /* Size-8, CRC32 */ | ||
308 | #ifdef IRIVER_H100 /* iRiver H110/H115 */ | ||
309 | { 48760, 0x2efc3323 }, /* 7-pre4 */ | ||
310 | { 56896, 0x0cd8dad4 }, /* 7-pre5 */ | ||
311 | #elif defined(IRIVER_H120) /* iRiver H120/H140 */ | ||
312 | { 63788, 0x08ff01a9 }, /* 7-pre3, improved failsafe functions */ | ||
313 | { 48764, 0xc674323e }, /* 7-pre4. Fixed audio thump & remote bootup */ | ||
314 | { 56896, 0x167f5d25 }, /* 7-pre5, various ATA fixes */ | ||
315 | #elif defined(IRIVER_H300) /* iRiver H320/H340 */ | ||
316 | #endif | ||
317 | {} | ||
318 | }; | ||
319 | |||
320 | /* check if the bootloader is a known good one */ | ||
321 | static bool detect_valid_bootloader(const uint8_t* pAddr, uint32_t len) | ||
322 | { | ||
323 | for (size_t i = 0; valid_bootloaders[i][0]; i++) | ||
324 | { | 391 | { |
325 | if (len != valid_bootloaders[i][0]) | 392 | uint32_t checksum; |
326 | continue; | 393 | uint8_t model[4]; |
394 | uint32_t sum; | ||
327 | 395 | ||
328 | uint32_t crc32 = rb->crc_32(pAddr, valid_bootloaders[i][0], 0xffffffff); | 396 | /* subtract the header length */ |
329 | if (crc32 == valid_bootloaders[i][1]) | 397 | fd_len -= sizeof(checksum) + sizeof(model); |
330 | return true; | ||
331 | } | ||
332 | 398 | ||
333 | return false; | 399 | /* sanity check the length */ |
334 | } | 400 | if (fd_len < WORD_SIZE) |
401 | { | ||
402 | msg = "Aborting: firmware too small"; | ||
403 | goto bail; | ||
404 | } | ||
335 | 405 | ||
336 | static int get_section_address(int section) | 406 | /* read the various parts */ |
337 | { | 407 | if ( |
338 | if (section == SECT_RAMIMAGE) | 408 | rb->read(fd, &checksum, sizeof(checksum)) != sizeof(checksum) || |
339 | return FLASH_RAMIMAGE_ENTRY; | 409 | rb->read(fd, model, sizeof(model)) != sizeof(model) || |
340 | else if (section == SECT_ROMIMAGE) | 410 | rb->read(fd, buffer, fd_len) != fd_len |
341 | return FLASH_ROMIMAGE_ENTRY; | 411 | ) |
342 | else | 412 | { |
343 | return -1; | 413 | msg = "Aborting: read failure"; |
344 | } | 414 | goto bail; |
415 | } | ||
345 | 416 | ||
346 | int flash_rockbox(const char *filename, int section) | 417 | /* calculate the checksum */ |
347 | { | 418 | sum = MODEL_NUMBER; |
348 | struct flash_header hdr; | 419 | for (off_t i = 0; i < fd_len; i++) |
349 | int pos, i, len/*, rc */; | 420 | sum += buffer[i]; |
350 | unsigned long checksum, sum; | ||
351 | unsigned char *p8; | ||
352 | uint16_t *p16; | ||
353 | 421 | ||
354 | if (get_section_address(section) < 0) | 422 | /* verify the checksum */ |
355 | return -1; | 423 | if (sum != checksum) |
424 | { | ||
425 | msg = "Aborting: checksum mismatch"; | ||
426 | goto bail; | ||
427 | } | ||
356 | 428 | ||
357 | p8 = (char *)BOOTLOADER_ENTRYPOINT; | 429 | /* verify the model */ |
358 | if (!detect_valid_bootloader(p8, 0)) | 430 | if (rb->memcmp(model, MODEL, sizeof(model)) != 0) |
359 | { | 431 | { |
360 | rb->splash(HZ*3, "Incompatible bootloader"); | 432 | msg = "Aborting: model mismatch"; |
361 | return -1; | 433 | goto bail; |
434 | } | ||
362 | } | 435 | } |
363 | 436 | ||
364 | if (!rb->detect_original_firmware()) | 437 | /* romdump specific code */ |
365 | { | 438 | if (firmware == FIRMWARE_ROMDUMP) |
366 | if (!confirm("Update Rockbox flash image?")) | ||
367 | return -2; | ||
368 | } | ||
369 | else | ||
370 | { | 439 | { |
371 | if (!confirm("Erase original firmware?")) | 440 | /* the romdump should be exactly the same size as the flash */ |
372 | return -3; | 441 | if (fd_len != FLASH_SIZE) |
373 | } | 442 | { |
374 | 443 | msg = "Aborting: firmware size incorrect"; | |
375 | len = load_firmware_file(filename, &checksum); | 444 | goto bail; |
376 | if (len <= 0) | 445 | } |
377 | return len * 10; | ||
378 | |||
379 | pos = get_section_address(section); | ||
380 | 446 | ||
381 | /* Check if image relocation seems to be sane. */ | 447 | /* exclude boot vector and boot loader regions */ |
382 | if (section == SECT_ROMIMAGE) | 448 | fd_len = BOOTLOADER_ENTRYPOINT - BOOT_VECTOR_SIZE; |
383 | { | ||
384 | uint32_t *p32 = (uint32_t *)audiobuf; | ||
385 | 449 | ||
386 | if (pos+sizeof(struct flash_header) != *p32) | 450 | /* skip the boot vector */ |
451 | if (rb->lseek(fd, BOOT_VECTOR_SIZE, SEEK_SET) != BOOT_VECTOR_SIZE) | ||
387 | { | 452 | { |
388 | rb->splashf(HZ*10, "Incorrect relocation: 0x%08lx/0x%08lx", | 453 | msg = "Aborting: lseek failure"; |
389 | *p32, pos+sizeof(struct flash_header)); | 454 | goto bail; |
390 | return -1; | ||
391 | } | 455 | } |
392 | 456 | ||
457 | /* read everything up to the boot loader */ | ||
458 | if (rb->read(fd, buffer, fd_len) != fd_len) | ||
459 | { | ||
460 | msg = "Aborting: read failure"; | ||
461 | goto bail; | ||
462 | } | ||
393 | } | 463 | } |
394 | 464 | ||
395 | /* Erase the program flash. */ | 465 | /* original firmware specific code */ |
396 | for (i = 0; i + pos < BOOTLOADER_ENTRYPOINT && i < len + 32; i += SEC_SIZE) | 466 | if (firmware == FIRMWARE_ORIGINAL) |
397 | { | 467 | { |
398 | /* Additional safety check. */ | 468 | uint32_t boot_vector[2]; |
399 | if (i + pos < SEC_SIZE) | ||
400 | return -1; | ||
401 | |||
402 | rb->lcd_putsf(0, 3, "Erasing... %d%%", (i+SEC_SIZE)*100/len); | ||
403 | rb->lcd_update(); | ||
404 | 469 | ||
405 | /*rc = */cfi_erase_sector(FB + (i + pos)/2); | 470 | /* subtract the offset and the size of the boot vector */ |
406 | } | 471 | fd_len -= FIRMWARE_OFFSET + sizeof(boot_vector); |
407 | |||
408 | /* Write the magic and size. */ | ||
409 | rb->memset(&hdr, 0, sizeof(struct flash_header)); | ||
410 | hdr.magic = FLASH_MAGIC; | ||
411 | hdr.length = len; | ||
412 | // rb->strncpy(hdr.version, rb->rbversion , sizeof(hdr.version)-1); | ||
413 | p16 = (uint16_t *)&hdr; | ||
414 | 472 | ||
415 | rb->lcd_puts(0, 4, "Programming..."); | 473 | /* sanity check the length */ |
416 | rb->lcd_update(); | 474 | if (fd_len < WORD_SIZE) |
475 | { | ||
476 | msg = "Aborting: firmware too small"; | ||
477 | goto bail; | ||
478 | } | ||
417 | 479 | ||
418 | pos = get_section_address(section)/2; | 480 | /* skip the leading bytes, whatever they are */ |
419 | for (i = 0; i < (long)sizeof(struct flash_header)/2; i++) | 481 | if (rb->lseek(fd, FIRMWARE_OFFSET, SEEK_SET) != FIRMWARE_OFFSET) |
420 | { | 482 | { |
421 | cfi_program_word(FB + pos, p16[i]); | 483 | msg = "Aborting: lseek failure"; |
422 | pos++; | 484 | goto bail; |
423 | } | 485 | } |
424 | 486 | ||
425 | p16 = (uint16_t *)audiobuf; | 487 | /* read the various parts */ |
426 | for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++) | 488 | if ( |
427 | { | 489 | rb->read(fd, boot_vector, sizeof(boot_vector)) != sizeof(boot_vector) || |
428 | if (i % SEC_SIZE == 0) | 490 | rb->read(fd, buffer, fd_len) != fd_len |
491 | ) | ||
429 | { | 492 | { |
430 | rb->lcd_putsf(0, 4, "Programming... %d%%", (i+1)*100/(len/2)); | 493 | msg = "Aborting: read failure"; |
431 | rb->lcd_update(); | 494 | goto bail; |
432 | } | 495 | } |
433 | 496 | ||
434 | cfi_program_word(FB + pos + i, p16[i]); | 497 | /* verify the boot vector */ |
498 | if (boot_vector[0] != 0x10017ff0 || boot_vector[1] != 0x00000008) | ||
499 | { | ||
500 | msg = "Aborting: not an original firmware"; | ||
501 | goto bail; | ||
502 | } | ||
435 | } | 503 | } |
436 | 504 | ||
437 | /* Verify */ | 505 | /* write the resulting buffer and length in the output parameters */ |
438 | rb->lcd_puts(0, 5, "Verifying"); | 506 | *data = buffer; |
439 | rb->lcd_update(); | 507 | *data_len = fd_len; |
440 | |||
441 | p8 = (char *)get_section_address(section); | ||
442 | p8 += sizeof(struct flash_header); | ||
443 | sum = MODEL_NUMBER; | ||
444 | for (i = 0; i < len; i++) | ||
445 | sum += p8[i]; | ||
446 | |||
447 | if (sum != checksum) | ||
448 | { | ||
449 | rb->splash(HZ*3, "Verify failed!"); | ||
450 | /* Erase the magic sector so bootloader does not try to load | ||
451 | * rockbox from flash and crash. */ | ||
452 | if (section == SECT_RAMIMAGE) | ||
453 | cfi_erase_sector(FB + FLASH_RAMIMAGE_ENTRY/2); | ||
454 | else | ||
455 | cfi_erase_sector(FB + FLASH_ROMIMAGE_ENTRY/2); | ||
456 | return -5; | ||
457 | } | ||
458 | 508 | ||
459 | rb->splash(HZ*2, "Success"); | 509 | /* mark success */ |
510 | result = true; | ||
460 | 511 | ||
461 | return 0; | 512 | bail: /* common exit code */ |
513 | if (fd >= 0) | ||
514 | rb->close(fd); | ||
515 | if (msg != NULL) | ||
516 | rb->splash(HZ * 3, msg); | ||
517 | return result; | ||
462 | } | 518 | } |
463 | 519 | ||
520 | /* prints fatal error if a critical failure occurs */ | ||
464 | static void show_fatal_error(void) | 521 | static void show_fatal_error(void) |
465 | { | 522 | { |
466 | rb->splash(HZ*30, "Disable idle poweroff, connect AC power and DON'T TURN PLAYER OFF!!"); | 523 | rb->splash(HZ * 30, "Disable idle poweroff, connect AC power and DON'T TURN PLAYER OFF!"); |
467 | rb->splash(HZ*30, "Contact Rockbox developers as soon as possible!"); | 524 | rb->splash(HZ * 30, "Contact Rockbox developers as soon as possible!"); |
468 | rb->splash(HZ*30, "Your device won't be bricked unless you turn off the power"); | 525 | rb->splash(HZ * 30, "Your device won't be bricked unless you turn off the power!"); |
469 | rb->splash(HZ*30, "Don't use the device before further instructions from Rockbox developers"); | 526 | rb->splash(HZ * 30, "Don't use the device before further instructions from Rockbox developers!"); |
470 | } | 527 | } |
471 | 528 | ||
472 | int flash_bootloader(const char *filename) | 529 | /* flash a bootloader */ |
530 | static bool flash_bootloader(const char* filename) | ||
473 | { | 531 | { |
474 | char *bootsector; | 532 | bool result = false; |
475 | int pos, i, len/*, rc*/; | 533 | const char* msg = NULL; |
476 | unsigned long checksum, sum; | 534 | bool show_fatal = false; |
477 | unsigned char *p8; | 535 | const void* data; |
478 | uint16_t *p16; | 536 | size_t data_len; |
537 | static uint8_t boot_sector[SECTOR_SIZE]; | ||
479 | 538 | ||
480 | bootsector = audiobuf; | 539 | /* load the firmware */ |
481 | audiobuf += SEC_SIZE; | 540 | if (!load_firmware(filename, FIRMWARE_ROCKBOX, &data, &data_len)) |
482 | audiobuf_size -= SEC_SIZE; | 541 | goto bail; |
483 | 542 | ||
484 | if (!confirm("Update bootloader?")) | 543 | /* the bootloader can only be so big */ |
485 | return -2; | 544 | if (data_len > BOOTLOADER_MAX_SIZE) |
486 | |||
487 | len = load_firmware_file(filename, &checksum); | ||
488 | if (len <= 0) | ||
489 | return len * 10; | ||
490 | |||
491 | if (len > 0xFFFF) | ||
492 | { | 545 | { |
493 | rb->splash(HZ*3, "Too big bootloader"); | 546 | msg = "Aborting: bootloader too large"; |
494 | return -1; | 547 | goto bail; |
495 | } | 548 | } |
496 | 549 | ||
497 | /* Verify the crc32 checksum also. */ | 550 | /* only support known bootloaders */ |
498 | if (!detect_valid_bootloader(audiobuf, len)) | 551 | if (!detect_valid_bootloader(data, data_len)) |
499 | { | 552 | { |
500 | rb->splash(HZ*3, "Incompatible/Untested bootloader"); | 553 | msg = "Aborting: bootloader is invalid"; |
501 | return -1; | 554 | goto bail; |
502 | } | 555 | } |
503 | 556 | ||
504 | rb->lcd_puts(0, 3, "Flashing..."); | 557 | /* ask before doing anything dangerous */ |
505 | rb->lcd_update(); | 558 | if (!confirm_choice("Update bootloader?")) |
559 | goto bail; | ||
506 | 560 | ||
507 | /* Backup the bootloader sector first. */ | 561 | /* copy the original boot sector */ |
508 | p8 = (char *)FB; | 562 | rb->memcpy(boot_sector, flash(BOOT_SECTOR_OFFSET), SECTOR_SIZE); |
509 | rb->memcpy(bootsector, p8, SEC_SIZE); | ||
510 | 563 | ||
511 | /* Erase the boot sector and write a proper reset vector. */ | 564 | /* update the boot vector */ |
512 | cfi_erase_sector(FB); | 565 | rb->memcpy(boot_sector, data, BOOT_VECTOR_SIZE); |
513 | p16 = (uint16_t *)audiobuf; | ||
514 | for (i = 0; i < 8/2; i++) | ||
515 | cfi_program_word(FB + i, p16[i]); | ||
516 | 566 | ||
517 | /* And restore original content for original FW to function. */ | 567 | /* erase the boot sector */ |
518 | p16 = (uint16_t *)bootsector; | 568 | flash_erase_sector(BOOT_SECTOR_OFFSET); |
519 | for (i = 8/2; i < SEC_SIZE/2; i++) | ||
520 | cfi_program_word(FB + i, p16[i]); | ||
521 | 569 | ||
522 | /* Erase the bootloader flash section. */ | 570 | /* erase the bootloader sectors */ |
523 | for (i = BOOTLOADER_ERASEGUARD; i < BOOTLOADER_ERASEGUARD+16; i++) | 571 | flash_erase_sectors(BOOTLOADER_ENTRYPOINT, BOOTLOADER_SECTORS, false); |
524 | /*rc =*/ cfi_erase_sector(FB + (SEC_SIZE/2) * i); | ||
525 | 572 | ||
526 | pos = BOOTLOADER_ENTRYPOINT/2; | 573 | /* program the new boot sector */ |
527 | p16 = (uint16_t *)audiobuf; | 574 | flash_program_bytes(BOOT_SECTOR_OFFSET, boot_sector, SECTOR_SIZE, false); |
528 | for (i = 0; i < len/2; i++) | ||
529 | cfi_program_word(FB + pos + i, p16[i]); | ||
530 | 575 | ||
531 | /* Verify */ | 576 | /* program the new bootloader */ |
532 | p8 = (char *)BOOTLOADER_ENTRYPOINT; | 577 | flash_program_bytes(BOOTLOADER_ENTRYPOINT, data, data_len, false); |
533 | sum = MODEL_NUMBER; | ||
534 | for (i = 0; i < len; i++) | ||
535 | sum += p8[i]; | ||
536 | 578 | ||
537 | if (sum != checksum) | 579 | /* verify the new boot sector */ |
580 | if (!flash_verify_bytes(BOOT_SECTOR_OFFSET, boot_sector, SECTOR_SIZE, false)) | ||
538 | { | 581 | { |
539 | rb->splash(HZ*3, "Verify failed!"); | 582 | msg = "Boot sector corrupt!"; |
540 | show_fatal_error(); | 583 | show_fatal = true; |
541 | return -5; | 584 | goto bail; |
542 | } | 585 | } |
543 | 586 | ||
544 | p8 = (char *)FB; | 587 | /* verify the new bootloader */ |
545 | for (i = 0; i < 8; i++) | 588 | if (!flash_verify_bytes(BOOTLOADER_ENTRYPOINT, data, data_len, false)) |
546 | { | 589 | { |
547 | if (p8[i] != audiobuf[i]) | 590 | msg = "Verify failed!"; |
548 | { | 591 | show_fatal = true; |
549 | rb->splash(HZ*3, "Bootvector corrupt!"); | 592 | goto bail; |
550 | show_fatal_error(); | ||
551 | return -6; | ||
552 | } | ||
553 | } | 593 | } |
554 | 594 | ||
555 | rb->splash(HZ*2, "Success"); | 595 | /* report success */ |
596 | rb->splash(HZ * 3, "Success!"); | ||
556 | 597 | ||
557 | return 0; | 598 | /* mark success */ |
599 | result = true; | ||
600 | |||
601 | bail: /* common exit code */ | ||
602 | if (msg != NULL) | ||
603 | rb->splash(HZ * 3, msg); | ||
604 | if (show_fatal) | ||
605 | show_fatal_error(); | ||
606 | return result; | ||
558 | } | 607 | } |
559 | 608 | ||
560 | int flash_original_fw(int len) | 609 | /* flash a rockbox ram / rom image */ |
610 | static bool flash_rockbox(const char* filename, uint32_t offset) | ||
561 | { | 611 | { |
562 | unsigned char reset_vector[8]; | 612 | bool result = false; |
563 | int pos, i, rc; | 613 | const char* msg = NULL; |
564 | unsigned char *p8; | 614 | const void* data; |
565 | uint16_t *p16; | 615 | size_t data_len; |
616 | struct flash_header header; | ||
566 | 617 | ||
567 | rb->lcd_puts(0, 3, "Critical section..."); | 618 | /* load the firmware */ |
568 | rb->lcd_update(); | 619 | if (!load_firmware(filename, FIRMWARE_ROCKBOX, &data, &data_len)) |
569 | 620 | goto bail; | |
570 | p8 = (char *)FB; | ||
571 | rb->memcpy(reset_vector, p8, sizeof reset_vector); | ||
572 | 621 | ||
573 | /* Erase the boot sector and write back the reset vector. */ | 622 | /* sanity check that the offset was set correctly */ |
574 | cfi_erase_sector(FB); | 623 | if (offset != FLASH_RAMIMAGE_ENTRY && offset != FLASH_ROMIMAGE_ENTRY) |
575 | p16 = (uint16_t *)reset_vector; | ||
576 | for (i = 0; i < (long)sizeof(reset_vector)/2; i++) | ||
577 | cfi_program_word(FB + i, p16[i]); | ||
578 | |||
579 | rb->lcd_puts(0, 4, "Flashing orig. FW"); | ||
580 | rb->lcd_update(); | ||
581 | |||
582 | /* Erase the program flash. */ | ||
583 | for (i = 1; i < BOOTLOADER_ERASEGUARD && (i-1)*4096 < len; i++) | ||
584 | { | 624 | { |
585 | rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i); | 625 | msg = "Aborting: invalid image offset"; |
586 | rb->lcd_putsf(0, 5, "Erase: 0x%03x (%d)", i, rc); | 626 | goto bail; |
587 | rb->lcd_update(); | ||
588 | } | 627 | } |
589 | 628 | ||
590 | rb->lcd_puts(0, 6, "Programming"); | 629 | /* ensure there's enough room for the ram / rom image */ |
591 | rb->lcd_update(); | 630 | if ( |
592 | 631 | (offset == FLASH_RAMIMAGE_ENTRY && data_len > RAM_IMAGE_MAX_SIZE) || | |
593 | pos = 0x00000008/2; | 632 | (offset == FLASH_ROMIMAGE_ENTRY && data_len > ROM_IMAGE_MAX_SIZE) |
594 | p16 = (uint16_t *)audiobuf; | 633 | ) |
595 | for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++) | 634 | { |
596 | cfi_program_word(FB + pos + i, p16[i]); | 635 | msg = "Aborting: ram / rom image too large"; |
636 | goto bail; | ||
637 | } | ||
597 | 638 | ||
598 | rb->lcd_puts(0, 7, "Verifying"); | 639 | /* check for bootloader that can load rockbox from ram / rom */ |
599 | rb->lcd_update(); | 640 | if (!detect_valid_bootloader(flash(BOOTLOADER_ENTRYPOINT), 0)) |
641 | { | ||
642 | msg = "Aborting: incompatible bootloader"; | ||
643 | goto bail; | ||
644 | } | ||
600 | 645 | ||
601 | /* Verify reset vectors. */ | 646 | /* rom image specific checks */ |
602 | p8 = (char *)FB; | 647 | if (offset == FLASH_ROMIMAGE_ENTRY) |
603 | for (i = 0; i < 8; i++) | ||
604 | { | 648 | { |
605 | if (p8[i] != reset_vector[i]) | 649 | uint32_t relocation = *((const uint32_t*) data); |
650 | |||
651 | /* sanity check of the image relocation */ | ||
652 | if (relocation != ROM_IMAGE_RELOCATION) | ||
606 | { | 653 | { |
607 | rb->splash(HZ*3, "Bootvector corrupt!"); | 654 | msg = "Aborting: invalid image relocation"; |
608 | show_fatal_error(); | 655 | goto bail; |
609 | break; | ||
610 | } | 656 | } |
611 | } | 657 | } |
612 | 658 | ||
613 | /* Verify */ | 659 | /* ask before doing anything dangerous */ |
614 | p8 = (char *)0x00000008; | 660 | if (!rb->detect_original_firmware()) |
615 | for (i = 0; i < len; i++) | ||
616 | { | 661 | { |
617 | if (p8[i] != audiobuf[i]) | 662 | if (!confirm_choice("Update Rockbox flash image?")) |
618 | { | 663 | goto bail; |
619 | rb->splash(HZ*3, "Verify failed!"); | 664 | } |
620 | rb->splashf(HZ*10, "at: 0x%08x", i); | 665 | else |
621 | return -5; | 666 | { |
622 | } | 667 | if (!confirm_choice("Erase original firmware?")) |
668 | goto bail; | ||
623 | } | 669 | } |
624 | 670 | ||
625 | rb->splash(HZ*2, "Success"); | 671 | /* erase all ram / rom image sectors */ |
672 | if (offset == FLASH_RAMIMAGE_ENTRY) | ||
673 | flash_erase_sectors(offset, RAM_IMAGE_SECTORS, true); | ||
674 | else if (offset == FLASH_ROMIMAGE_ENTRY) | ||
675 | flash_erase_sectors(offset, ROM_IMAGE_SECTORS, true); | ||
676 | |||
677 | /* prepare the header */ | ||
678 | header.magic = FLASH_MAGIC; | ||
679 | header.length = data_len; | ||
680 | rb->memset(&header.version, 0x00, sizeof(header.version)); | ||
681 | |||
682 | /* program the header */ | ||
683 | flash_program_bytes(offset, &header, sizeof(header), false); | ||
684 | |||
685 | /* program the ram / rom image */ | ||
686 | flash_program_bytes(offset + sizeof(header), data, data_len, true); | ||
687 | |||
688 | /* verify the header and ram / rom image */ | ||
689 | if ( | ||
690 | !flash_verify_bytes(offset, &header, sizeof(header), false) || | ||
691 | !flash_verify_bytes(offset + sizeof(header), data, data_len, true) | ||
692 | ) | ||
693 | { | ||
694 | msg = "Verify failed!"; | ||
695 | /* | ||
696 | * erase the ram / rom image header to prevent the bootloader | ||
697 | * from trying to boot from it | ||
698 | */ | ||
699 | flash_erase_sector(offset); | ||
700 | goto bail; | ||
701 | } | ||
626 | 702 | ||
627 | return 0; | 703 | /* report success */ |
704 | rb->splash(HZ * 3, "Success!"); | ||
705 | |||
706 | /* mark success */ | ||
707 | result = true; | ||
708 | |||
709 | bail: /* common exit code */ | ||
710 | if (msg != NULL) | ||
711 | rb->splash(HZ * 3, msg); | ||
712 | return result; | ||
628 | } | 713 | } |
629 | 714 | ||
630 | int load_original_bin(const char *filename) | 715 | /* flash whole firmware; common code for romdump / original */ |
716 | static bool flash_whole_firmware(const void* data, size_t data_len) | ||
631 | { | 717 | { |
632 | unsigned long magic[2]; | 718 | bool result = false; |
633 | int len, rc; | 719 | const char* msg = NULL; |
634 | int fd; | 720 | bool show_fatal = false; |
721 | uint8_t boot_vector[BOOT_VECTOR_SIZE]; | ||
635 | 722 | ||
636 | if (!confirm("Restore original firmware (bootloader will be kept)?")) | 723 | /* copy the original boot vector */ |
637 | return -2; | 724 | rb->memcpy(boot_vector, flash(BOOT_SECTOR_OFFSET), BOOT_VECTOR_SIZE); |
638 | 725 | ||
639 | fd = rb->open(filename, O_RDONLY); | 726 | /* erase everything except the bootloader */ |
640 | if (fd < 0) | 727 | flash_erase_sectors(BOOT_SECTOR_OFFSET, WHOLE_FIRMWARE_SECTORS, true); |
641 | return -1; | 728 | |
729 | /* program the original boot vector */ | ||
730 | flash_program_bytes(BOOT_SECTOR_OFFSET, boot_vector, BOOT_VECTOR_SIZE, false); | ||
731 | |||
732 | /* program the whole firmware */ | ||
733 | flash_program_bytes(BOOT_SECTOR_OFFSET + BOOT_VECTOR_SIZE, data, data_len, true); | ||
642 | 734 | ||
643 | len = rb->filesize(fd) - 0x228; | 735 | /* verify the new boot vector */ |
644 | rb->lseek(fd, 0x220, SEEK_SET); | 736 | if (!flash_verify_bytes(BOOT_SECTOR_OFFSET, boot_vector, BOOT_VECTOR_SIZE, false)) |
645 | rb->read(fd, magic, 8); | ||
646 | if (magic[1] != 0x00000008 || len <= 0 || len > audiobuf_size) | ||
647 | { | 737 | { |
648 | rb->splash(HZ*2, "Not an original firmware file"); | 738 | msg = "Boot vector corrupt!"; |
649 | rb->close(fd); | 739 | show_fatal = true; |
650 | return -1; | 740 | goto bail; |
651 | } | 741 | } |
652 | 742 | ||
653 | rc = rb->read(fd, audiobuf, len); | 743 | /* verify the new firmware */ |
654 | rb->close(fd); | 744 | if (!flash_verify_bytes(BOOT_SECTOR_OFFSET + BOOT_VECTOR_SIZE, data, data_len, true)) |
655 | |||
656 | if (rc != len) | ||
657 | { | 745 | { |
658 | rb->splash(HZ*2, "Read error"); | 746 | msg = "Verify failed!"; |
659 | return -2; | 747 | goto bail; |
660 | } | 748 | } |
661 | 749 | ||
662 | if (len % 2) | 750 | /* report success */ |
663 | len++; | 751 | rb->splash(HZ * 3, "Success!"); |
752 | |||
753 | /* mark success */ | ||
754 | result = true; | ||
664 | 755 | ||
665 | return flash_original_fw(len); | 756 | bail: /* common exit code */ |
757 | if (msg != NULL) | ||
758 | rb->splash(HZ * 3, msg); | ||
759 | if (show_fatal) | ||
760 | show_fatal_error(); | ||
761 | return result; | ||
666 | } | 762 | } |
667 | 763 | ||
668 | int load_romdump(const char *filename) | 764 | /* flash rom dumps */ |
765 | static bool flash_romdump(const char* filename) | ||
669 | { | 766 | { |
670 | int len, rc; | 767 | const void* data; |
671 | int fd; | 768 | size_t data_len; |
672 | 769 | ||
673 | if (!confirm("Restore firmware section (bootloader will be kept)?")) | 770 | /* load the firmware */ |
674 | return -2; | 771 | if (!load_firmware(filename, FIRMWARE_ROMDUMP, &data, &data_len)) |
772 | return false; | ||
675 | 773 | ||
676 | fd = rb->open(filename, O_RDONLY); | 774 | /* ask before doing anything dangerous */ |
677 | if (fd < 0) | 775 | if (!confirm_choice("Restore firmware section (bootloader will be kept)?")) |
678 | return -1; | 776 | return false; |
679 | |||
680 | len = rb->filesize(fd) - 8; | ||
681 | if (len <= 0) | ||
682 | return -1; | ||
683 | 777 | ||
684 | rb->lseek(fd, 8, SEEK_SET); | 778 | return flash_whole_firmware(data, data_len); |
685 | rc = rb->read(fd, audiobuf, len); | 779 | } |
686 | rb->close(fd); | ||
687 | 780 | ||
688 | if (rc != len) | 781 | /* flash original firmware */ |
689 | { | 782 | static bool flash_original(const char* filename) |
690 | rb->splash(HZ*2, "Read error"); | 783 | { |
691 | return -2; | 784 | const void* data; |
692 | } | 785 | size_t data_len; |
693 | 786 | ||
694 | if (len % 2) | 787 | /* load the firmware */ |
695 | len++; | 788 | if (!load_firmware(filename, FIRMWARE_ORIGINAL, &data, &data_len)) |
789 | return false; | ||
696 | 790 | ||
697 | if (len > BOOTLOADER_ENTRYPOINT - 8) | 791 | /* ask before doing anything dangerous */ |
698 | len = BOOTLOADER_ENTRYPOINT - 8; | 792 | if (!confirm_choice("Restore original firmware (bootloader will be kept)?")) |
793 | return false; | ||
699 | 794 | ||
700 | return flash_original_fw(len); | 795 | return flash_whole_firmware(data, data_len); |
701 | } | 796 | } |
702 | 797 | ||
703 | /* Kind of our main function, defines the application flow. */ | 798 | /* main function of plugin */ |
704 | static void DoUserDialog(const char* filename) | 799 | static void iriver_flash(const char* filename) |
705 | { | 800 | { |
706 | /* check whether we're running from ROM */ | 801 | /* refuse to run from ROM */ |
707 | uint16_t* RB = (uint16_t*) rb; | 802 | const uint8_t* RB = (uint8_t*) rb; |
708 | if (RB >= FB && RB < FB + (FLASH_SIZE / sizeof(*FB))) | 803 | const uint8_t* FB = (uint8_t*) flash(0); |
804 | if (RB >= FB && RB < FB + FLASH_SIZE) | ||
709 | { | 805 | { |
710 | rb->splash(HZ*3, "Not from ROM"); | 806 | rb->splash(HZ * 3, "Refusing to run from ROM"); |
711 | return; /* exit */ | 807 | return; |
712 | } | 808 | } |
713 | 809 | ||
714 | /* refuse to work if the power may fail meanwhile */ | 810 | /* refuse to run with low battery */ |
715 | if (!rb->battery_level_safe()) | 811 | if (!rb->battery_level_safe()) |
716 | { | 812 | { |
717 | rb->splash(HZ*3, "Battery too low!"); | 813 | rb->splash(HZ * 3, "Refusing to run with low battery"); |
718 | return; /* exit */ | 814 | return; |
719 | } | 815 | } |
720 | 816 | ||
817 | /* print information about flash; exit if not supported */ | ||
721 | if (!show_info()) | 818 | if (!show_info()) |
722 | return; /* exit */ | 819 | return; |
723 | 820 | ||
821 | /* exit if no filename was provided */ | ||
724 | if (filename == NULL) | 822 | if (filename == NULL) |
725 | { | 823 | { |
726 | rb->splash(HZ*3, "Please use this plugin with \"Open with...\""); | 824 | rb->splash(HZ * 3, "Please use this plugin with \"Open with...\""); |
727 | return; /* exit */ | 825 | return; |
728 | } | 826 | } |
729 | 827 | ||
730 | audiobuf = rb->plugin_get_audio_buffer((size_t *)&audiobuf_size); | 828 | /* choose what to do with the file */ |
731 | 829 | if (rb->strcasestr(filename, "/bootloader.iriver") != NULL) | |
732 | if (rb->strcasestr(filename, "/rockbox.iriver")) | ||
733 | flash_rockbox(filename, SECT_RAMIMAGE); | ||
734 | else if (rb->strcasestr(filename, "/rombox.iriver")) | ||
735 | flash_rockbox(filename, SECT_ROMIMAGE); | ||
736 | else if (rb->strcasestr(filename, "/bootloader.iriver")) | ||
737 | flash_bootloader(filename); | 830 | flash_bootloader(filename); |
738 | else if (rb->strcasestr(filename, "/ihp_120.bin")) | 831 | else if (rb->strcasestr(filename, "/rockbox.iriver") != NULL) |
739 | load_original_bin(filename); | 832 | flash_rockbox(filename, FLASH_RAMIMAGE_ENTRY); |
740 | else if (rb->strcasestr(filename, "/internal_rom_000000-1FFFFF.bin")) | 833 | else if (rb->strcasestr(filename, "/rombox.iriver") != NULL) |
741 | load_romdump(filename); | 834 | flash_rockbox(filename, FLASH_ROMIMAGE_ENTRY); |
835 | else if (rb->strcasestr(filename, ROMDUMP) != NULL) | ||
836 | flash_romdump(filename); | ||
837 | else if (rb->strcasestr(filename, ORIGINAL) != NULL) | ||
838 | flash_original(filename); | ||
742 | else | 839 | else |
743 | rb->splash(HZ*3, "Unknown file type"); | 840 | rb->splash(HZ * 3, "Unknown file type"); |
744 | } | 841 | } |
745 | 842 | ||
746 | 843 | /* plugin entry point */ | |
747 | /***************** Plugin Entry Point *****************/ | ||
748 | |||
749 | enum plugin_status plugin_start(const void* parameter) | 844 | enum plugin_status plugin_start(const void* parameter) |
750 | { | 845 | { |
751 | int oldmode; | 846 | /* need to disable memguard to write to flash */ |
847 | int mode = rb->system_memory_guard(MEMGUARD_NONE); | ||
752 | 848 | ||
753 | /* now go ahead and have fun! */ | 849 | /* setup LCD font */ |
754 | oldmode = rb->system_memory_guard(MEMGUARD_NONE); /*disable memory guard */ | ||
755 | rb->lcd_setfont(FONT_SYSFIXED); | 850 | rb->lcd_setfont(FONT_SYSFIXED); |
756 | DoUserDialog(parameter); | 851 | |
852 | /* run the main entry function */ | ||
853 | iriver_flash(parameter); | ||
854 | |||
855 | /* restore LCD font */ | ||
757 | rb->lcd_setfont(FONT_UI); | 856 | rb->lcd_setfont(FONT_UI); |
758 | rb->system_memory_guard(oldmode); /* re-enable memory guard */ | 857 | |
858 | /* restore original memory guard setting */ | ||
859 | rb->system_memory_guard(mode); | ||
759 | 860 | ||
760 | return PLUGIN_OK; | 861 | return PLUGIN_OK; |
761 | } | 862 | } |