diff options
Diffstat (limited to 'apps/plugins/iriver_flash.c')
-rw-r--r-- | apps/plugins/iriver_flash.c | 343 |
1 files changed, 266 insertions, 77 deletions
diff --git a/apps/plugins/iriver_flash.c b/apps/plugins/iriver_flash.c index f919a0d25c..1cae1767a9 100644 --- a/apps/plugins/iriver_flash.c +++ b/apps/plugins/iriver_flash.c | |||
@@ -58,7 +58,7 @@ static struct plugin_api* rb; /* here is a global api struct pointer */ | |||
58 | 58 | ||
59 | #ifdef IRIVER_H100_SERIES | 59 | #ifdef IRIVER_H100_SERIES |
60 | #define SEC_SIZE 4096 | 60 | #define SEC_SIZE 4096 |
61 | #define BOOTLOADER_ERASEGUARD (BOOTLOADER_ENTRYPOINT / SEC_SIZE - 1) | 61 | #define BOOTLOADER_ERASEGUARD (BOOTLOADER_ENTRYPOINT / SEC_SIZE) |
62 | 62 | ||
63 | static volatile uint16_t* FB = (uint16_t*)0x00000000; /* Flash base address */ | 63 | static volatile uint16_t* FB = (uint16_t*)0x00000000; /* Flash base address */ |
64 | #endif | 64 | #endif |
@@ -181,7 +181,7 @@ bool cfi_get_flash_info(struct flash_info* pInfo) | |||
181 | 181 | ||
182 | /* Tool function to calculate a CRC32 across some buffer */ | 182 | /* Tool function to calculate a CRC32 across some buffer */ |
183 | /* third argument is either 0xFFFFFFFF to start or value from last piece */ | 183 | /* third argument is either 0xFFFFFFFF to start or value from last piece */ |
184 | unsigned crc_32(unsigned char* buf, unsigned len, unsigned crc32) | 184 | unsigned crc_32(const unsigned char* buf, unsigned len, unsigned crc32) |
185 | { | 185 | { |
186 | /* CCITT standard polynomial 0x04C11DB7 */ | 186 | /* CCITT standard polynomial 0x04C11DB7 */ |
187 | static const unsigned crc32_lookup[16] = | 187 | static const unsigned crc32_lookup[16] = |
@@ -217,7 +217,7 @@ unsigned crc_32(unsigned char* buf, unsigned len, unsigned crc32) | |||
217 | 217 | ||
218 | 218 | ||
219 | /***************** User Interface Functions *****************/ | 219 | /***************** User Interface Functions *****************/ |
220 | int WaitForButton(void) | 220 | int wait_for_button(void) |
221 | { | 221 | { |
222 | int button; | 222 | int button; |
223 | 223 | ||
@@ -262,6 +262,36 @@ void ShowFlashInfo(struct flash_info* pInfo) | |||
262 | rb->lcd_update(); | 262 | rb->lcd_update(); |
263 | } | 263 | } |
264 | 264 | ||
265 | bool show_info(void) | ||
266 | { | ||
267 | struct flash_info fi; | ||
268 | |||
269 | rb->lcd_clear_display(); | ||
270 | cfi_get_flash_info(&fi); | ||
271 | ShowFlashInfo(&fi); | ||
272 | if (fi.size == 0) /* no valid chip */ | ||
273 | { | ||
274 | rb->splash(HZ*3, true, "Sorry!"); | ||
275 | return false; /* exit */ | ||
276 | } | ||
277 | |||
278 | return true; | ||
279 | } | ||
280 | |||
281 | bool confirm(const char *msg) | ||
282 | { | ||
283 | char buf[128]; | ||
284 | bool ret; | ||
285 | |||
286 | rb->snprintf(buf, sizeof buf, "%s ([PLAY] to CONFIRM)", msg); | ||
287 | rb->splash(0, true, buf); | ||
288 | |||
289 | ret = (wait_for_button() == BUTTON_ON); | ||
290 | show_info(); | ||
291 | |||
292 | return ret; | ||
293 | } | ||
294 | |||
265 | int load_firmware_file(const char *filename, uint32_t *checksum) | 295 | int load_firmware_file(const char *filename, uint32_t *checksum) |
266 | { | 296 | { |
267 | int fd; | 297 | int fd; |
@@ -308,6 +338,45 @@ int load_firmware_file(const char *filename, uint32_t *checksum) | |||
308 | return len; | 338 | return len; |
309 | } | 339 | } |
310 | 340 | ||
341 | bool detect_flashed_rockbox(void) | ||
342 | { | ||
343 | struct flash_header hdr; | ||
344 | uint8_t *src = (uint8_t *)FLASH_ENTRYPOINT; | ||
345 | |||
346 | rb->memcpy(&hdr, src, sizeof(struct flash_header)); | ||
347 | |||
348 | if (hdr.magic != FLASH_MAGIC) | ||
349 | return false; | ||
350 | |||
351 | return true; | ||
352 | } | ||
353 | |||
354 | unsigned long valid_bootloaders[][2] = | ||
355 | { | ||
356 | { 62332, 0x77395351 }, | ||
357 | { 0, 0 } | ||
358 | }; | ||
359 | |||
360 | |||
361 | bool detect_valid_bootloader(const unsigned char *addr, int len) | ||
362 | { | ||
363 | int i; | ||
364 | unsigned long crc32; | ||
365 | |||
366 | /* Try to scan through all valid bootloaders. */ | ||
367 | for (i = 0; valid_bootloaders[i][0]; i++) | ||
368 | { | ||
369 | if (len > 0 && len != (long)valid_bootloaders[i][0]) | ||
370 | continue; | ||
371 | |||
372 | crc32 = crc_32(addr, valid_bootloaders[i][0], 0xffffffff); | ||
373 | if (crc32 == valid_bootloaders[i][1]) | ||
374 | return true; | ||
375 | } | ||
376 | |||
377 | return false; | ||
378 | } | ||
379 | |||
311 | int flash_rockbox(const char *filename) | 380 | int flash_rockbox(const char *filename) |
312 | { | 381 | { |
313 | struct flash_header hdr; | 382 | struct flash_header hdr; |
@@ -318,9 +387,27 @@ int flash_rockbox(const char *filename) | |||
318 | uint16_t *p16; | 387 | uint16_t *p16; |
319 | 388 | ||
320 | len = load_firmware_file(filename, &checksum); | 389 | len = load_firmware_file(filename, &checksum); |
321 | if (len < 0) | 390 | if (len <= 0) |
322 | return len * 10; | 391 | return len * 10; |
323 | 392 | ||
393 | p8 = (char *)BOOTLOADER_ENTRYPOINT; | ||
394 | if (!detect_valid_bootloader(p8, 0)) | ||
395 | { | ||
396 | rb->splash(HZ*3, true, "Incompatible bootloader"); | ||
397 | return -1; | ||
398 | } | ||
399 | |||
400 | if (detect_flashed_rockbox()) | ||
401 | { | ||
402 | if (!confirm("Update Rockbox flash image?")) | ||
403 | return -2; | ||
404 | } | ||
405 | else | ||
406 | { | ||
407 | if (!confirm("Erase original firmware?")) | ||
408 | return -3; | ||
409 | } | ||
410 | |||
324 | /* Erase the program flash. */ | 411 | /* Erase the program flash. */ |
325 | for (i = 1; i < BOOTLOADER_ERASEGUARD && (i-1)*4096 < len + 32; i++) | 412 | for (i = 1; i < BOOTLOADER_ERASEGUARD && (i-1)*4096 < len + 32; i++) |
326 | { | 413 | { |
@@ -349,10 +436,14 @@ int flash_rockbox(const char *filename) | |||
349 | } | 436 | } |
350 | 437 | ||
351 | p16 = (uint16_t *)audiobuf; | 438 | p16 = (uint16_t *)audiobuf; |
352 | for (i = 0; i < len/2 && pos < (BOOTLOADER_ENTRYPOINT/2); i++) | 439 | for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++) |
353 | cfi_program_word(FB + pos + i, p16[i]); | 440 | cfi_program_word(FB + pos + i, p16[i]); |
354 | 441 | ||
355 | /* Verify */ | 442 | /* Verify */ |
443 | rb->snprintf(buf, sizeof(buf), "Verifying"); | ||
444 | rb->lcd_puts(0, 5, buf); | ||
445 | rb->lcd_update(); | ||
446 | |||
356 | p8 = (char *)FLASH_ENTRYPOINT; | 447 | p8 = (char *)FLASH_ENTRYPOINT; |
357 | p8 += sizeof(struct flash_header); | 448 | p8 += sizeof(struct flash_header); |
358 | sum = 0; | 449 | sum = 0; |
@@ -368,6 +459,8 @@ int flash_rockbox(const char *filename) | |||
368 | return -5; | 459 | return -5; |
369 | } | 460 | } |
370 | 461 | ||
462 | rb->splash(HZ*2, true, "Success"); | ||
463 | |||
371 | return 0; | 464 | return 0; |
372 | } | 465 | } |
373 | 466 | ||
@@ -383,13 +476,13 @@ int flash_bootloader(const char *filename) | |||
383 | { | 476 | { |
384 | char buf[32]; | 477 | char buf[32]; |
385 | int pos, i, len, rc; | 478 | int pos, i, len, rc; |
386 | unsigned long checksum, sum, crc32; | 479 | unsigned long checksum, sum; |
387 | unsigned char *p8; | 480 | unsigned char *p8; |
388 | uint16_t *p16; | 481 | uint16_t *p16; |
389 | 482 | ||
390 | (void)buf; | 483 | (void)buf; |
391 | len = load_firmware_file(filename, &checksum); | 484 | len = load_firmware_file(filename, &checksum); |
392 | if (len < 0) | 485 | if (len <= 0) |
393 | return len * 10; | 486 | return len * 10; |
394 | 487 | ||
395 | if (len > 0xFFFF) | 488 | if (len > 0xFFFF) |
@@ -399,18 +492,16 @@ int flash_bootloader(const char *filename) | |||
399 | } | 492 | } |
400 | 493 | ||
401 | /* Verify the crc32 checksum also. */ | 494 | /* Verify the crc32 checksum also. */ |
402 | crc32 = crc_32(audiobuf, len, 0xffffffff); | 495 | if (!detect_valid_bootloader(audiobuf, len)) |
403 | #if 0 | ||
404 | rb->snprintf(buf, sizeof buf, "crc32 = 0x%08x", crc32); | ||
405 | rb->splash(HZ*10, true, buf); | ||
406 | #else | ||
407 | if (crc32 != 0x77395351) | ||
408 | { | 496 | { |
409 | rb->splash(HZ*3, true, "Untested bootloader"); | 497 | rb->splash(HZ*3, true, "Incompatible/Untested bootloader"); |
410 | return -2; | 498 | return -1; |
411 | } | 499 | } |
412 | #endif | 500 | |
413 | rb->lcd_puts(0, 3, "Processing critical sections..."); | 501 | if (!confirm("Update bootloader?")) |
502 | return -2; | ||
503 | |||
504 | rb->lcd_puts(0, 3, "Flashing..."); | ||
414 | rb->lcd_update(); | 505 | rb->lcd_update(); |
415 | 506 | ||
416 | /* Erase the boot sector and write a proper reset vector. */ | 507 | /* Erase the boot sector and write a proper reset vector. */ |
@@ -448,92 +539,186 @@ int flash_bootloader(const char *filename) | |||
448 | { | 539 | { |
449 | rb->splash(HZ*3, true, "Bootvector corrupt!"); | 540 | rb->splash(HZ*3, true, "Bootvector corrupt!"); |
450 | show_fatal_error(); | 541 | show_fatal_error(); |
451 | break; | 542 | return -6; |
452 | } | 543 | } |
453 | } | 544 | } |
454 | 545 | ||
546 | rb->splash(HZ*2, true, "Success"); | ||
547 | |||
455 | return 0; | 548 | return 0; |
456 | } | 549 | } |
457 | 550 | ||
458 | /* Kind of our main function, defines the application flow. */ | 551 | int flash_original_fw(int len) |
459 | void DoUserDialog(char* filename) | ||
460 | { | 552 | { |
461 | struct flash_info fi; | 553 | unsigned char reset_vector[8]; |
462 | int rc; /* generic return code */ | 554 | char buf[32]; |
555 | int pos, i, rc; | ||
556 | unsigned char *p8; | ||
557 | uint16_t *p16; | ||
558 | |||
559 | (void)buf; | ||
560 | |||
561 | rb->lcd_puts(0, 3, "Critical section..."); | ||
562 | rb->lcd_update(); | ||
463 | 563 | ||
464 | /* this can only work if Rockbox runs in DRAM, not flash ROM */ | 564 | p8 = (char *)FB; |
465 | if ((uint16_t*)rb >= FB && (uint16_t*)rb < FB + 4096*1024) /* 4 MB max */ | 565 | rb->memcpy(reset_vector, p8, sizeof reset_vector); |
466 | { /* we're running from flash */ | 566 | |
467 | rb->splash(HZ*3, true, "Not from ROM"); | 567 | /* Erase the boot sector and write back the reset vector. */ |
468 | return; /* exit */ | 568 | cfi_erase_sector(FB); |
569 | p16 = (uint16_t *)reset_vector; | ||
570 | for (i = 0; i < (long)sizeof(reset_vector)/2; i++) | ||
571 | cfi_program_word(FB + i, p16[i]); | ||
572 | |||
573 | rb->lcd_puts(0, 4, "Flashing orig. FW"); | ||
574 | rb->lcd_update(); | ||
575 | |||
576 | /* Erase the program flash. */ | ||
577 | for (i = 1; i < BOOTLOADER_ERASEGUARD && (i-1)*4096 < len; i++) | ||
578 | { | ||
579 | rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i); | ||
580 | rb->snprintf(buf, sizeof(buf), "Erase: 0x%03x (%d)", i, rc); | ||
581 | rb->lcd_puts(0, 5, buf); | ||
582 | rb->lcd_update(); | ||
469 | } | 583 | } |
470 | 584 | ||
471 | /* refuse to work if the power may fail meanwhile */ | 585 | rb->snprintf(buf, sizeof(buf), "Programming"); |
472 | if (!rb->battery_level_safe()) | 586 | rb->lcd_puts(0, 6, buf); |
587 | rb->lcd_update(); | ||
588 | |||
589 | pos = 0x00000008/2; | ||
590 | p16 = (uint16_t *)audiobuf; | ||
591 | for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++) | ||
592 | cfi_program_word(FB + pos + i, p16[i]); | ||
593 | |||
594 | rb->snprintf(buf, sizeof(buf), "Verifying"); | ||
595 | rb->lcd_puts(0, 7, buf); | ||
596 | rb->lcd_update(); | ||
597 | |||
598 | /* Verify reset vectors. */ | ||
599 | p8 = (char *)FB; | ||
600 | for (i = 0; i < 8; i++) | ||
473 | { | 601 | { |
474 | rb->splash(HZ*3, true, "Battery too low!"); | 602 | if (p8[i] != reset_vector[i]) |
475 | return; /* exit */ | 603 | { |
604 | rb->splash(HZ*3, true, "Bootvector corrupt!"); | ||
605 | show_fatal_error(); | ||
606 | break; | ||
607 | } | ||
476 | } | 608 | } |
477 | 609 | ||
478 | rb->lcd_setfont(FONT_SYSFIXED); | 610 | /* Verify */ |
479 | 611 | p8 = (char *)0x00000008; | |
480 | rc = cfi_get_flash_info(&fi); | 612 | for (i = 0; i < len; i++) |
481 | ShowFlashInfo(&fi); | ||
482 | if (fi.size == 0) /* no valid chip */ | ||
483 | { | 613 | { |
484 | rb->splash(HZ*3, true, "Sorry!"); | 614 | if (p8[i] != audiobuf[i]) |
485 | return; /* exit */ | 615 | { |
616 | rb->splash(HZ*3, true, "Verify failed!"); | ||
617 | rb->snprintf(buf, sizeof buf, "at: 0x%08x", i); | ||
618 | rb->splash(HZ*10, true, buf); | ||
619 | return -5; | ||
620 | } | ||
486 | } | 621 | } |
487 | |||
488 | /* Debug? */ | ||
489 | #if 0 | ||
490 | rb->memcpy(&hdr, (uint8_t *)(FLASH_ENTRYPOINT), sizeof(struct flash_header)); | ||
491 | rb->snprintf(buf, sizeof(buf), "Magic: 0x%03x", hdr.magic); | ||
492 | rb->lcd_puts(0, 3, buf); | ||
493 | rb->snprintf(buf, sizeof(buf), "Size: 0x%03x", hdr.length); | ||
494 | rb->lcd_puts(0, 4, buf); | ||
495 | rb->lcd_update(); | ||
496 | rb->sleep(HZ*10); | ||
497 | 622 | ||
498 | rb->memcpy(&hdr, (uint8_t *)(FLASH_ENTRYPOINT/2), sizeof(struct flash_header)); | 623 | rb->splash(HZ*2, true, "Success"); |
499 | rb->snprintf(buf, sizeof(buf), "Magic: 0x%03x", hdr.magic); | 624 | |
500 | rb->lcd_puts(0, 3, buf); | 625 | return 0; |
501 | rb->snprintf(buf, sizeof(buf), "Size: 0x%03x", hdr.length); | 626 | } |
502 | rb->lcd_puts(0, 4, buf); | 627 | |
503 | rb->lcd_update(); | 628 | int load_original_bin(const char *filename) |
504 | rb->sleep(HZ*10); | 629 | { |
505 | #endif | 630 | unsigned long magic[2]; |
631 | int len, rc; | ||
632 | int fd; | ||
506 | 633 | ||
507 | /* Restore? */ | 634 | fd = rb->open(filename, O_RDONLY); |
508 | #if 0 | ||
509 | fd = rb->open("/internal_rom_000000-1FFFFF.bin", O_RDONLY); | ||
510 | if (fd < 0) | 635 | if (fd < 0) |
511 | return ; | 636 | return -1; |
512 | len = rb->filesize(fd); | ||
513 | 637 | ||
514 | /* Erase the program flash. */ | 638 | len = rb->filesize(fd) - 0x228; |
515 | for (i = 1; i < 0x1EF; i++) | 639 | rb->lseek(fd, 0x220, SEEK_SET); |
640 | rb->read(fd, magic, 8); | ||
641 | if (magic[1] != 0x00000008 || len <= 0 || len > audiobuf_size) | ||
516 | { | 642 | { |
517 | rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i); | 643 | rb->splash(HZ*2, true, "Not an original firmware file"); |
518 | rb->snprintf(buf, sizeof(buf), "Erase: 0x%03x (%d)", i, rc); | 644 | rb->close(fd); |
519 | rb->lcd_puts(0, 3, buf); | 645 | return -1; |
520 | rb->lcd_update(); | ||
521 | } | 646 | } |
522 | 647 | ||
523 | i = FLASH_ENTRYPOINT/2; | 648 | rc = rb->read(fd, audiobuf, len); |
524 | rb->lseek(fd, i*2, SEEK_SET); | 649 | rb->close(fd); |
525 | len -= i*2 - 0xffff; | 650 | |
526 | for (; len > 0 && i < (0x1F0000/2); i++) | 651 | if (rc != len) |
527 | { | 652 | { |
528 | rb->read(fd, bytes, 2); | 653 | rb->splash(HZ*2, true, "Read error"); |
529 | cfi_program_word(FB + i, (bytes[0] << 8) | bytes[1]); | 654 | return -2; |
530 | len -= 2; | ||
531 | } | 655 | } |
532 | 656 | ||
657 | if (len % 2) | ||
658 | len++; | ||
659 | |||
660 | if (!confirm("Restore original firmware (bootloader will be kept)?")) | ||
661 | return -2; | ||
662 | |||
663 | return flash_original_fw(len); | ||
664 | } | ||
665 | |||
666 | int load_romdump(const char *filename) | ||
667 | { | ||
668 | int len, rc; | ||
669 | int fd; | ||
670 | |||
671 | fd = rb->open(filename, O_RDONLY); | ||
672 | if (fd < 0) | ||
673 | return -1; | ||
674 | |||
675 | len = rb->filesize(fd) - 8; | ||
676 | if (len <= 0) | ||
677 | return -1; | ||
678 | |||
679 | rb->lseek(fd, 8, SEEK_SET); | ||
680 | rc = rb->read(fd, audiobuf, len); | ||
533 | rb->close(fd); | 681 | rb->close(fd); |
534 | return ; | ||
535 | #endif | ||
536 | 682 | ||
683 | if (rc != len) | ||
684 | { | ||
685 | rb->splash(HZ*2, true, "Read error"); | ||
686 | return -2; | ||
687 | } | ||
688 | |||
689 | if (len % 2) | ||
690 | len++; | ||
691 | |||
692 | if (len > BOOTLOADER_ENTRYPOINT - 8) | ||
693 | len = BOOTLOADER_ENTRYPOINT - 8; | ||
694 | |||
695 | if (!confirm("Restore firmware section (bootloader will be kept)?")) | ||
696 | return -2; | ||
697 | |||
698 | return flash_original_fw(len); | ||
699 | } | ||
700 | |||
701 | /* Kind of our main function, defines the application flow. */ | ||
702 | void DoUserDialog(char* filename) | ||
703 | { | ||
704 | /* this can only work if Rockbox runs in DRAM, not flash ROM */ | ||
705 | if ((uint16_t*)rb >= FB && (uint16_t*)rb < FB + 4096*1024) /* 4 MB max */ | ||
706 | { /* we're running from flash */ | ||
707 | rb->splash(HZ*3, true, "Not from ROM"); | ||
708 | return; /* exit */ | ||
709 | } | ||
710 | |||
711 | /* refuse to work if the power may fail meanwhile */ | ||
712 | if (!rb->battery_level_safe()) | ||
713 | { | ||
714 | rb->splash(HZ*3, true, "Battery too low!"); | ||
715 | return; /* exit */ | ||
716 | } | ||
717 | |||
718 | rb->lcd_setfont(FONT_SYSFIXED); | ||
719 | if (!show_info()) | ||
720 | return ; | ||
721 | |||
537 | if (filename == NULL) | 722 | if (filename == NULL) |
538 | { | 723 | { |
539 | rb->splash(HZ*3, true, "Please use this plugin with \"Open with...\""); | 724 | rb->splash(HZ*3, true, "Please use this plugin with \"Open with...\""); |
@@ -546,6 +731,10 @@ void DoUserDialog(char* filename) | |||
546 | flash_rockbox(filename); | 731 | flash_rockbox(filename); |
547 | else if (rb->strcasestr(filename, "/bootloader.iriver")) | 732 | else if (rb->strcasestr(filename, "/bootloader.iriver")) |
548 | flash_bootloader(filename); | 733 | flash_bootloader(filename); |
734 | else if (rb->strcasestr(filename, "/ihp_120.bin")) | ||
735 | load_original_bin(filename); | ||
736 | else if (rb->strcasestr(filename, "/internal_rom_000000-1FFFFF.bin")) | ||
737 | load_romdump(filename); | ||
549 | else | 738 | else |
550 | rb->splash(HZ*3, true, "Unknown file type"); | 739 | rb->splash(HZ*3, true, "Unknown file type"); |
551 | } | 740 | } |