From 91ce4b2a60c4cbe8e3568f79c3a73572461ff40d Mon Sep 17 00:00:00 2001 From: Jens Arnold Date: Sun, 19 Jun 2011 17:23:18 +0000 Subject: [PATCH] Optional dual-boot support in iAudio X5 and M5 bootloader, based on FS#5289. In order to enable it, #define HAVE_DUALBOOT when building the bootloader. Do not use the automatically created x5_fw.bin or m5_fw.bin, but use mkboot to create a new firmware file from an OF x5_fw.bin resp. m5_fw.bin and bootloader.bin. The dual-boot bootloader boots the OF when pressing Play (main or remote) for more than 3 seconds. Hold it a bit longer because the OF also checks buttons. Short press boots rockbox. As a bonus, the Play button read (for hold check) is done a bit earlier for single-boot mode as well. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30018 a1c6a512-1295-4272-9138-f99709370657 --- bootloader/bootloader.make | 3 +- bootloader/iaudio_coldfire.c | 16 +- firmware/target/coldfire/crt0.S | 53 +++++- firmware/target/coldfire/iaudio/boot.lds | 6 +- rbutil/rbutilqt/base/bootloaderinstallhex.cpp | 2 +- tools/mkboot.c | 176 +++++++++++++++--- tools/mkboot.h | 3 +- 7 files changed, 222 insertions(+), 37 deletions(-) diff --git a/bootloader/bootloader.make b/bootloader/bootloader.make index 0118235bb7..78b8b438c4 100644 --- a/bootloader/bootloader.make +++ b/bootloader/bootloader.make @@ -10,6 +10,7 @@ INCLUDES += -I$(APPSDIR) SRC += $(call preprocess, $(APPSDIR)/SOURCES) +CONFIGFILE := $(FIRMDIR)/export/config/$(MODELNAME).h BOOTLDS := $(FIRMDIR)/target/$(CPU)/$(MANUFACTURER)/boot.lds BOOTLINK := $(BUILDDIR)/boot.link @@ -17,7 +18,7 @@ CLEANOBJS += $(BUILDDIR)/bootloader.* .SECONDEXPANSION: -$(BOOTLINK): $(BOOTLDS) +$(BOOTLINK): $(BOOTLDS) $(CONFIGFILE) $(call PRINTS,PP $(@F)) $(call preprocess2file,$<,$@,-DLOADADDRESS=$(LOADADDRESS)) diff --git a/bootloader/iaudio_coldfire.c b/bootloader/iaudio_coldfire.c index 5639a0e59b..9c829c4a34 100644 --- a/bootloader/iaudio_coldfire.c +++ b/bootloader/iaudio_coldfire.c @@ -123,6 +123,10 @@ void check_battery(void) } } +#if defined(IAUDIO_M5) || defined(IAUDIO_X5) +int initial_gpio_read; +#endif + void main(void) { int i; @@ -144,17 +148,13 @@ void main(void) if ((GPIO_READ & 0x80000000) == 0) rc_on_button = true; #elif defined(IAUDIO_M5) || defined(IAUDIO_X5) - int data; - or_l(0x0e000000, &GPIO_FUNCTION); /* main Hold & Power, remote Play */ and_l(~0x0e000000, &GPIO_ENABLE); - - data = GPIO_READ; - - if ((data & 0x04000000) == 0) + + if ((initial_gpio_read & 0x04000000) == 0) on_button = true; - - if ((data & 0x02000000) == 0) + + if ((initial_gpio_read & 0x02000000) == 0) rc_on_button = true; #endif diff --git a/firmware/target/coldfire/crt0.S b/firmware/target/coldfire/crt0.S index e6717710b1..881fcf908f 100644 --- a/firmware/target/coldfire/crt0.S +++ b/firmware/target/coldfire/crt0.S @@ -26,11 +26,51 @@ .global start start: +#if defined(BOOTLOADER) && defined(HAVE_DUALBOOT) \ + && (defined(IAUDIO_X5) || defined(IAUDIO_M5)) + + /* 8 byte dualboot signature */ + bra.b 1f /* 0x6006 */ + .short 0x4442 /* DB */ +#if defined(IAUDIO_X5) + .long 0x69617835 /* iax5 */ +#elif defined(IAUDIO_M5) + .long 0x69616d35 /* iam5 */ +#else +#error Dualboot signature not defined +#endif +1: + /* As the control registers are write-only, we're relying on MBAR2 being */ + /* set up correctly by the preloader for button check */ + /* Only use scratch regs until we're sure that we will boot rockbox */ + lea MBAR2, %a1 + move.l (%a1), %a0 /* store GPIO_READ result for button check in main() */ + + /* Wait ~3 seconds for ON-button release. We need roughly 300ns per + iteration, so we check 10000000 times to reach the desired delay */ + move.l #10000000, %d0 +.on_button_test: + move.l (%a1), %d1 /* GPIO_READ */ + and.l #0x06000000, %d1 /* Check main (bit 25=0) and remote (bit 26=0) */ + cmp.l #0x06000000, %d1 /* ON buttons simultaneously */ + beq.b .loadrockbox + subq.l #1, %d0 + bne.b .on_button_test + +.loadoriginal: + jmp 0x10010 + +.loadrockbox: + move.l %a0, %d7 /* keep initial GPIO_READ value in %d7 for now */ + +#endif /* defined(BOOTLOADER && defined(HAVE_DUALBOOT) + && (defined(IAUDIO_X5) || defined(IAUDIO_M5)) */ + move.w #0x2700,%sr move.l #vectors,%d0 movec.l %d0,%vbr - + move.l #MBAR+1,%d0 movec.l %d0,%mbar @@ -39,7 +79,12 @@ start: lea MBAR,%a0 lea MBAR2,%a1 - + +#if defined(BOOTLOADER) && !defined(HAVE_DUALBOOT) \ + && (defined(IAUDIO_X5) || defined(IAUDIO_M5)) + move.l (%a1), %d7 /* store GPIO_READ result for button check in main() */ +#endif + clr.l (0x180,%a1) /* PLLCR = 0 */ /* 64K DMA-capable SRAM at 0x10000000 @@ -318,6 +363,10 @@ start: move.l %d0,(%a2)+ cmp.l %a2,%a4 bhi.b .mungeloop + +#if defined(BOOTLOADER) && (defined(IAUDIO_X5) || defined(IAUDIO_M5)) + move.l %d7, initial_gpio_read +#endif jsr main .hoo: diff --git a/firmware/target/coldfire/iaudio/boot.lds b/firmware/target/coldfire/iaudio/boot.lds index 4cd0e8eaab..9288a6c18b 100644 --- a/firmware/target/coldfire/iaudio/boot.lds +++ b/firmware/target/coldfire/iaudio/boot.lds @@ -14,8 +14,12 @@ STARTUP(target/coldfire/crt0.o) #define IRAMSIZE 0x18000 #endif #define DRAMORIG 0x31000000 +#ifdef HAVE_DUALBOOT +#define FLASHORIG 0x00150000 +#else #define FLASHORIG 0x00010000 -#define FLASHSIZE 4M +#endif +#define FLASHSIZE 4M - FLASHORIG MEMORY { diff --git a/rbutil/rbutilqt/base/bootloaderinstallhex.cpp b/rbutil/rbutilqt/base/bootloaderinstallhex.cpp index 84d60eda86..5118aab52c 100644 --- a/rbutil/rbutilqt/base/bootloaderinstallhex.cpp +++ b/rbutil/rbutilqt/base/bootloaderinstallhex.cpp @@ -162,7 +162,7 @@ void BootloaderInstallHex::installStage2(void) // iriver decode already done in stage 1 int result; - if((result = mkboot(descrambledName.toLocal8Bit().constData(), + if((result = mkboot_iriver(descrambledName.toLocal8Bit().constData(), tempfileName.toLocal8Bit().constData(), tempbinName.toLocal8Bit().constData(), origin)) < 0) { diff --git a/tools/mkboot.c b/tools/mkboot.c index 49d6452b4a..f7c7b4668d 100644 --- a/tools/mkboot.c +++ b/tools/mkboot.c @@ -26,42 +26,46 @@ #ifndef RBUTIL static void usage(void) { - printf("usage: mkboot [-h300] \n"); + printf("usage: mkboot \n"); + printf("available targets:\n" + "\t-h100 Iriver H1x0\n" + "\t-h300 Iriver H3x0\n" + "\t-iax5 iAudio X5\n" + "\t-iam5 iAudio M5\n"); exit(1); } #endif -static unsigned char image[0x400000 + 0x220 + 0x400000/0x200]; - #ifndef RBUTIL int main(int argc, char *argv[]) { - char *infile, *bootfile, *outfile; - int origin = 0x1f0000; /* H1x0 bootloader address */ - - if(argc < 3) { - usage(); - } - - if(!strcmp(argv[1], "-h300")) { - infile = argv[2]; - bootfile = argv[3]; - outfile = argv[4]; - - origin = 0x3f0000; /* H3x0 bootloader address */ - } - else + if(argc != 5) { - infile = argv[1]; - bootfile = argv[2]; - outfile = argv[3]; + usage(); + return 1; } - return mkboot(infile, bootfile, outfile, origin); + + if ( ! strcmp(argv[1], "-h100")) + return mkboot_iriver(argv[2], argv[3], argv[4], 0x1f0000); + + if ( ! strcmp(argv[1], "-h300")) + return mkboot_iriver(argv[2], argv[3], argv[4], 0x3f0000); + + if ( ! strcmp(argv[1], "-iax5")) + return mkboot_iaudio(argv[2], argv[3], argv[4], 0); + + if ( ! strcmp(argv[1], "-iam5")) + return mkboot_iaudio(argv[2], argv[3], argv[4], 1); + + usage(); + return 1; } #endif -int mkboot(const char* infile, const char* bootfile, const char* outfile, int origin) +static unsigned char image[0x400000 + 0x220 + 0x400000/0x200]; + +int mkboot_iriver(const char* infile, const char* bootfile, const char* outfile, int origin) { FILE *f; int i; @@ -187,3 +191,129 @@ int mkboot(const char* infile, const char* bootfile, const char* outfile, int or return 0; } + +/* iAudio firmware update file header size */ +#define HEADER_SIZE 0x1030 +/* Address of flash contents that get overwritten by a firmware update. + * Contents before this address contain the preloader and are not affected + * by a firmware update. + * -> Firmware update file contents starting at offset HEADER_SIZE end up + * in flash at address FLASH_START + */ +#define FLASH_START 0x00010000 +/* Start of unused space in original firmware (flash address, not file + * offset!) where we patch in the Rockbox loader */ +#define ROCKBOX_BOOTLOADER 0x00150000 +/* End of unused space in original firmware */ +#define BOOTLOADER_LIMIT 0x00170000 + +/* Patch the Rockbox bootloader into free space in the original firmware + * (starting at 0x150000). The preloader starts execution of the OF at + * 0x10000 which normally contains a jsr 0x10010. We also patch this to + * do a jsr 0x150000 to the Rockbox dual boot loader instead. If it then + * decides to start the OF instead of Rockbox, it simply does a jmp + * 0x10010 instead of loading Rockbox from disk. + */ +int mkboot_iaudio(const char* infile, const char* bootfile, const char* outfile, int model_nr) +{ + size_t flength, blength; + unsigned char *bbuf, *fbuf, *p; + const unsigned char fsig[] = { + 0x4e, 0xb9, 0x00, 0x01, 0x00, 0x10 }; /* jsr 0x10010 */ + unsigned char bsig[2][8] = { + /* dualboot signatures */ + { 0x60, 0x06, 0x44, 0x42, 0x69, 0x61, 0x78, 0x35 }, /* X5 */ + { 0x60, 0x06, 0x44, 0x42, 0x69, 0x61, 0x6d, 0x35 }, /* M5 */ }; + FILE *ffile, *bfile, *ofile; + unsigned char sum = 0; + int i; + + /* read input files */ + if ((bfile = fopen(bootfile, "rb")) == NULL) { + perror("Cannot open Rockbox bootloader file.\n"); + return 1; + } + + fseek(bfile, 0, SEEK_END); + blength = ftell(bfile); + fseek(bfile, 0, SEEK_SET); + + if (blength + ROCKBOX_BOOTLOADER >= BOOTLOADER_LIMIT) { + fprintf(stderr, "Rockbox bootloader is too big.\n"); + return 1; + } + + if ((ffile = fopen(infile, "rb")) == NULL) { + perror("Cannot open original firmware file."); + return 1; + } + + fseek(ffile, 0, SEEK_END); + flength = ftell(ffile); + fseek(ffile, 0, SEEK_SET); + + bbuf = malloc(blength); + fbuf = malloc(flength); + + if (!bbuf || !fbuf) { + fprintf(stderr, "Out of memory.\n"); + return 1; + } + + if ( fread(bbuf, 1, blength, bfile) < blength + || fread(fbuf, 1, flength, ffile) < flength) { + fprintf(stderr, "Read error.\n"); + return 1; + } + fclose(bfile); + fclose(ffile); + + /* verify format of input files */ + if (blength < 0x10 || memcmp(bbuf, bsig[model_nr], sizeof(bsig[0]))) { + fprintf(stderr, "Rockbox bootloader format error (is it bootloader.bin?).\n"); + return 1; + } + if (flength < HEADER_SIZE-FLASH_START+BOOTLOADER_LIMIT + || memcmp(fbuf+HEADER_SIZE, fsig, sizeof(fsig))) { + fprintf(stderr, "Original firmware format error.\n"); + return 1; + } + + /* verify firmware is not overrun */ + for (i = ROCKBOX_BOOTLOADER; i < BOOTLOADER_LIMIT; i++) { + if (fbuf[HEADER_SIZE-FLASH_START+i] != 0xff) { + fprintf(stderr, "Original firmware has grown too much.\n"); + return 1; + } + } + + /* change jsr 0x10010 to jsr DUAL_BOOTLOADER */ + p = fbuf + HEADER_SIZE + 2; + *p++ = (ROCKBOX_BOOTLOADER >> 24) & 0xff; + *p++ = (ROCKBOX_BOOTLOADER >> 16) & 0xff; + *p++ = (ROCKBOX_BOOTLOADER >> 8) & 0xff; + *p++ = (ROCKBOX_BOOTLOADER ) & 0xff; + + p = fbuf + HEADER_SIZE + ROCKBOX_BOOTLOADER - FLASH_START; + memcpy(p, bbuf, blength); + + /* recalc checksum */ + for (i = HEADER_SIZE; (size_t)i < flength; i++) + sum += fbuf[i]; + fbuf[0x102b] = sum; + + /* write output */ + if ((ofile = fopen(outfile, "wb")) == NULL) { + perror("Cannot open output file"); + return 1; + } + if (fwrite(fbuf, 1, flength, ofile) < flength) { + fprintf(stderr, "Write error.\n"); + return 1; + } + fclose(ofile); + free(bbuf); + free(fbuf); + + return 0; +} \ No newline at end of file diff --git a/tools/mkboot.h b/tools/mkboot.h index 980e469b87..ba12d1b473 100644 --- a/tools/mkboot.h +++ b/tools/mkboot.h @@ -26,7 +26,8 @@ extern "C" { #endif -int mkboot(const char* infile, const char* bootfile, const char* outfile, int origin); +int mkboot_iriver(const char* infile, const char* bootfile, const char* outfile, int origin); +int mkboot_iaudio(const char* infile, const char* bootfile, const char* outfile, int model_nr); #ifdef __cplusplus }