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
This commit is contained in:
parent
e9e0cf5908
commit
91ce4b2a60
7 changed files with 222 additions and 37 deletions
|
@ -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))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
176
tools/mkboot.c
176
tools/mkboot.c
|
@ -26,42 +26,46 @@
|
|||
#ifndef RBUTIL
|
||||
static void usage(void)
|
||||
{
|
||||
printf("usage: mkboot [-h300] <firmware file> <boot file> <output file>\n");
|
||||
printf("usage: mkboot <target> <firmware file> <boot file> <output file>\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;
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue