/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2009 by Dave Chapman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include #include #include #include "config.h" #include "inttypes.h" #include "cpu.h" #include "system.h" #include "lcd.h" #include "i2c-s5l8700.h" #include "../kernel-internal.h" #include "file_internal.h" #include "storage.h" #include "fat.h" #include "disk.h" #include "font.h" #include "backlight.h" #include "backlight-target.h" #include "button.h" #include "panic.h" #include "power.h" #include "file.h" #include "common.h" #include "rb-loader.h" #include "loader_strerror.h" #include "version.h" /* Safety measure - maximum allowed firmware image size. The largest known current (October 2009) firmware is about 6.2MB so we set this to 8MB. */ #define MAX_LOADSIZE (8*1024*1024) /* The buffer to load the firmware into - use an uncached alias of 0x08000000 */ unsigned char *loadbuffer = (unsigned char *)0x48000000; extern int line; void fatal_error(void) { extern int line; bool holdstatus=false; /* System font is 6 pixels wide */ printf("Hold MENU+SELECT to"); printf("reboot then SELECT+PLAY"); printf("for disk mode"); lcd_update(); while (1) { if (button_hold() != holdstatus) { if (button_hold()) { holdstatus=true; lcd_puts(0, line, "Hold switch on!"); } else { holdstatus=false; lcd_puts(0, line, " "); } lcd_update(); } } } static void aes_decrypt(void* data, uint32_t size) { uint32_t ptr, i; uint32_t go = 1; PWRCONEXT &= ~0x400; AESTYPE = 1; AESUNKREG0 = 1; AESUNKREG0 = 0; AESCONTROL = 1; AESKEYLEN = 8; AESOUTSIZE = size; AESAUXSIZE = 0x10; AESINSIZE = 0x10; AESSIZE3 = 0x10; for (ptr = (size >> 2) - 4; ; ptr -= 4) { AESOUTADDR = (uint32_t)data + (ptr << 2); AESINADDR = (uint32_t)data + (ptr << 2); AESAUXADDR = (uint32_t)data + (ptr << 2); AESSTATUS = 6; AESGO = go; go = 3; while ((AESSTATUS & 6) == 0); if (ptr == 0) break; for (i = 0; i < 4; i++) ((uint32_t*)data)[ptr + i] ^= ((uint32_t*)data)[ptr + i - 4]; } AESCONTROL = 0; PWRCONEXT |= 0x400; } static int readfw(char* filename, void* address, int* size) { int i; uint32_t startsector = 0; uint32_t buffer[0x200]; if (nand_read_sectors(0, 1, buffer) != 0) return -1; if (*((uint16_t*)((uint32_t)buffer + 0x1FE)) != 0xAA55) return -2; for (i = 0x1C2; i < 0x200; i += 0x10) { if (((uint8_t*)buffer)[i] == 0) { startsector = *((uint16_t*)((uint32_t)buffer + i + 4)) | (*((uint16_t*)((uint32_t)buffer + i + 6)) << 16); break; } } if (startsector == 0) return -3; if (nand_read_sectors(startsector, 1, buffer) != 0) return -4; if (buffer[0x40] != 0x5B68695D) return -5; if (nand_read_sectors(startsector + 1 + (buffer[0x41] >> 11), 1, buffer) != 0) return -6; for (i = 0; i < 0x1FE; i += 10) { if (memcmp(&buffer[i], filename, 8) == 0) { uint32_t filesector = startsector + 1 + (buffer[i + 3] >> 11); *size = buffer[i + 4]; if (nand_read_sectors(filesector, ((*size + 0x7FF) >> 11), address) != 0) return -7; /* Success! */ return 0; } } /* Nothing found */ return -8; } void main(void) { int i; int btn; int size; int rc; bool button_was_held; /* Check the button hold status as soon as possible - to give the user maximum chance to turn it on in order to reset the settings in rockbox. */ button_was_held = button_hold(); system_init(); kernel_init(); i2c_init(); enable_irq(); backlight_init(); /* Turns on the backlight */ lcd_init(); font_init(); lcd_set_foreground(LCD_WHITE); lcd_set_background(LCD_BLACK); lcd_clear_display(); button_init(); btn = button_status(); /* Enable bootloader messages */ if (btn==BUTTON_RIGHT) verbose = true; lcd_setfont(FONT_SYSFIXED); printf("Rockbox boot loader"); printf("Version: %s", rbversion); i = storage_init(); if (i != 0) { printf("ATA error: %d", i); fatal_error(); } filesystem_init(); rc = disk_mount_all(); if (rc<=0) { printf("No partition found"); fatal_error(); } if (button_was_held || (btn==BUTTON_MENU)) { /* If either the hold switch was on, or the Menu button was held, then try the Apple firmware */ printf("Loading original firmware..."); if ((rc = readfw("DNANkbso", loadbuffer, &size)) < 0) { printf("readfw error %d",rc); fatal_error(); } /* Now we need to decrypt it */ printf("Decrypting %d bytes...",size); aes_decrypt(loadbuffer, size); } else { printf("Loading Rockbox..."); rc=load_firmware(loadbuffer, BOOTFILE, MAX_LOADSIZE); if (rc <= EFILE_EMPTY) { printf("Error!"); printf("Can't load " BOOTFILE ": "); printf(loader_strerror(rc)); fatal_error(); } printf("Rockbox loaded."); } /* If we get here, we have a new firmware image at 0x08000000, run it */ printf("Executing..."); disable_irq(); /* Remap the bootrom back to zero - that's how the NOR bootloader leaves it. */ MIUCON &= ~1; /* Disable caches and protection unit */ asm volatile( "mrc 15, 0, r0, c1, c0, 0 \n" "bic r0, r0, #0x1000 \n" "bic r0, r0, #0x5 \n" "mcr 15, 0, r0, c1, c0, 0 \n" ); /* Branch to start of DRAM */ asm volatile("ldr pc, =0x08000000"); }