| /*************************************************************************** |
| * __________ __ ___. |
| * 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 <stdlib.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <string.h> |
| |
| #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 "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"); |
| } |