/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2006 by Barry Wardell * * Based on Rockbox iriver bootloader by Linus Nielsen Feltzing * and the ipodlinux bootloader by Daniel Palffy and Bernard Leach * * 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 "config.h" #include "common.h" #include "cpu.h" #include "file.h" #include "system.h" #include "../kernel-internal.h" #include "lcd.h" #include "font.h" #include "storage.h" #include "adc.h" #include "button.h" #include "disk.h" #include "crc32-mi4.h" #include "mi4-loader.h" #include "loader_strerror.h" #include #include "power.h" #include "version.h" #if defined(SANSA_E200) || defined(PHILIPS_SA9200) #include "i2c.h" #include "backlight-target.h" #endif #include "usb.h" #if defined(SANSA_E200) || defined(SANSA_C200) || defined(PHILIPS_SA9200) #include "usb_drv.h" #endif #if defined(SAMSUNG_YH925) /* this function (in lcd-yh925.c) resets the screen orientation for the OF * for use with dualbooting */ void lcd_reset(void); #endif /* Show the Rockbox logo - in show_logo.c */ extern void show_logo(void); /* Button definitions */ #if CONFIG_KEYPAD == IRIVER_H10_PAD #define BOOTLOADER_BOOT_OF BUTTON_LEFT #elif CONFIG_KEYPAD == SANSA_E200_PAD #define BOOTLOADER_BOOT_OF BUTTON_LEFT #elif CONFIG_KEYPAD == SANSA_C200_PAD #define BOOTLOADER_BOOT_OF BUTTON_LEFT #elif CONFIG_KEYPAD == MROBE100_PAD #define BOOTLOADER_BOOT_OF BUTTON_POWER #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD #define BOOTLOADER_BOOT_OF BUTTON_VOL_UP #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD #define BOOTLOADER_BOOT_OF BUTTON_MENU #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD #define BOOTLOADER_BOOT_OF BUTTON_VOL_UP #elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \ (CONFIG_KEYPAD == SAMSUNG_YH920_PAD) #define BOOTLOADER_BOOT_OF BUTTON_LEFT #elif CONFIG_KEYPAD == SANSA_FUZE_PAD #define BOOTLOADER_BOOT_OF BUTTON_LEFT #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD #define BOOTLOADER_BOOT_OF BUTTON_OK #endif /* Maximum allowed firmware image size. 10MB is more than enough */ #define MAX_LOADSIZE (10*1024*1024) /* A buffer to load the original firmware or Rockbox into */ unsigned char *loadbuffer = (unsigned char *)DRAM_START; /* Locations and sizes in hidden partition on Sansa */ #if (CONFIG_STORAGE & STORAGE_SD) #define PPMI_SECTOR_OFFSET 1024 #define PPMI_SECTORS 1 #define MI4_HEADER_SECTORS 1 #define NUM_PARTITIONS 2 #else #define NUM_PARTITIONS 1 #endif #define MI4_HEADER_SIZE 0x200 /* PPMI header structure */ struct ppmi_header_t { unsigned char magic[4]; uint32_t length; uint32_t pad[126]; }; #if (CONFIG_STORAGE & STORAGE_SD) /* Load mi4 firmware from a hidden disk partition */ int load_mi4_part(unsigned char* buf, struct partinfo* pinfo, unsigned int buffer_size, bool disable_rebuild) { struct mi4header_t mi4header; struct ppmi_header_t ppmi_header; unsigned long sum; /* Read header to find out how long the mi4 file is. */ storage_read_sectors(pinfo->start + PPMI_SECTOR_OFFSET, PPMI_SECTORS, &ppmi_header); /* The first four characters at 0x80000 (sector 1024) should be PPMI*/ if( memcmp(ppmi_header.magic, "PPMI", 4) ) return EFILE_NOT_FOUND; printf("BL mi4 size: %x", ppmi_header.length); /* Read mi4 header of the OF */ storage_read_sectors(pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS + (ppmi_header.length/512), MI4_HEADER_SECTORS, &mi4header); /* We don't support encrypted mi4 files yet */ if( (mi4header.plaintext) != (mi4header.mi4size-MI4_HEADER_SIZE)) return EINVALID_FORMAT; /* MI4 file size */ printf("OF mi4 size: %x", mi4header.mi4size); if ((mi4header.mi4size-MI4_HEADER_SIZE) > buffer_size) return EFILE_TOO_BIG; /* CRC32 */ printf("CRC32: %x", mi4header.crc32); /* Rockbox model id */ printf("Model id: %.4s", mi4header.model); /* Read binary type (RBOS, RBBL) */ printf("Binary type: %.4s", mi4header.type); /* Load firmware */ storage_read_sectors(pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS + (ppmi_header.length/512) + MI4_HEADER_SECTORS, (mi4header.mi4size-MI4_HEADER_SIZE)/512, buf); /* Check CRC32 to see if we have a valid file */ sum = chksum_crc32 (buf,mi4header.mi4size-MI4_HEADER_SIZE); printf("Calculated CRC32: %x", sum); if(sum != mi4header.crc32) return EBAD_CHKSUM; #ifdef SANSA_E200 if (disable_rebuild) { char block[512]; printf("Disabling database rebuild"); storage_read_sectors(pinfo->start + 0x3c08, 1, block); block[0xe1] = 0; storage_write_sectors(pinfo->start + 0x3c08, 1, block); } #else (void) disable_rebuild; #endif return mi4header.mi4size-MI4_HEADER_SIZE; } #endif /* (CONFIG_STORAGE & STORAGE_SD) */ #ifdef HAVE_BOOTLOADER_USB_MODE /* Return USB_HANDLED if session took place else return USB_EXTRACTED */ static int handle_usb(int connect_timeout) { static struct event_queue q SHAREDBSS_ATTR; struct queue_event ev; int usb = USB_EXTRACTED; long end_tick = 0; if (!usb_plugged()) return USB_EXTRACTED; queue_init(&q, true); usb_init(); usb_start_monitoring(); printf("USB: Connecting"); if (connect_timeout != TIMEOUT_BLOCK) end_tick = current_tick + connect_timeout; while (1) { /* Sleep no longer than 1/2s */ queue_wait_w_tmo(&q, &ev, HZ/2); if (ev.id == SYS_USB_CONNECTED) { /* Switch to verbose mode if not in it so that the status updates * are shown */ verbose = true; /* Got the message - wait for disconnect */ printf("Bootloader USB mode"); usb = USB_HANDLED; usb_acknowledge(SYS_USB_CONNECTED_ACK); usb_wait_for_disconnect(&q); break; } if (connect_timeout != TIMEOUT_BLOCK && TIME_AFTER(current_tick, end_tick)) { /* Timed out waiting for the connect - will happen when connected * to a charger instead of a host port and the charging pin is * the same as the USB pin */ printf("USB: Timed out"); break; } if (!usb_plugged()) break; /* Cable pulled */ } usb_close(); queue_delete(&q); return usb; } #elif (defined(SANSA_E200) || defined(SANSA_C200) || defined(PHILIPS_SA9200) \ || defined (SANSA_VIEW)) && !defined(USE_ROCKBOX_USB) /* Return USB_INSERTED if cable present */ static int handle_usb(int connect_timeout) { int usb_retry = 0; int usb = USB_EXTRACTED; usb_init(); while (usb_drv_powered() && usb_retry < 5 && usb != USB_INSERTED) { usb_retry++; sleep(HZ/4); usb = usb_detect(); } if (usb != USB_INSERTED) usb = USB_EXTRACTED; return usb; (void)connect_timeout; } #else /* Ignore cable state */ static int handle_usb(int connect_timeout) { return USB_EXTRACTED; (void)connect_timeout; } #endif /* HAVE_BOOTLOADER_USB_MODE */ void* main(void) { char filename[MAX_PATH]; int i; int btn; int rc; int num_partitions; struct partinfo* pinfo; #if !(CONFIG_STORAGE & STORAGE_SD) char buf[256]; unsigned short* identify_info; #endif int usb = USB_EXTRACTED; system_init(); kernel_init(); #ifdef HAVE_BOOTLOADER_USB_MODE /* loader must service interrupts */ enable_interrupt(IRQ_FIQ_STATUS); #endif lcd_init(); font_init(); show_logo(); adc_init(); #ifdef HAVE_BOOTLOADER_USB_MODE button_init_device(); #else button_init(); #endif #if defined(SANSA_E200) || defined(PHILIPS_SA9200) i2c_init(); _backlight_on(); #endif if (button_hold()) { verbose = true; lcd_clear_display(); printf("Hold switch on"); printf("Shutting down..."); sleep(HZ); power_off(); } btn = button_read_device(); /* Enable bootloader messages if any button is pressed */ #ifdef HAVE_BOOTLOADER_USB_MODE lcd_clear_display(); if (btn) verbose = true; #else if (btn) { lcd_clear_display(); verbose = true; } #endif lcd_setfont(FONT_SYSFIXED); printf("Rockbox boot loader"); printf("Version: " RBVERSION); printf(MODEL_NAME); i=storage_init(); #if !(CONFIG_STORAGE & STORAGE_SD) if (i==0) { identify_info=ata_get_identify(); /* Show model */ for (i=0; i < 20; i++) { ((unsigned short*)buf)[i]=htobe16(identify_info[i+27]); } buf[40]=0; for (i=39; i && buf[i]==' '; i--) { buf[i]=0; } printf(buf); } else { error(EATA, i, true); } #endif disk_init(IF_MV(0)); num_partitions = disk_mount_all(); if (num_partitions<=0) { error(EDISK,num_partitions, true); } /* Just list the first 2 partitions since we don't have any devices yet that have more than that */ for(i=0; itype, pinfo->size / 2048); } /* Now that storage is initialized, check for USB connection */ if ((btn & BOOTLOADER_BOOT_OF) == 0) { usb_pin_init(); usb = handle_usb(HZ*2); if (usb == USB_INSERTED) btn |= BOOTLOADER_BOOT_OF; } /* Try loading Rockbox, if that fails, fall back to the OF */ if((btn & BOOTLOADER_BOOT_OF) == 0) { printf("Loading Rockbox..."); snprintf(filename,sizeof(filename), BOOTDIR "/%s", BOOTFILE); rc = load_mi4(loadbuffer, filename, MAX_LOADSIZE); if (rc <= EFILE_EMPTY) { bool old_verbose = verbose; verbose = true; printf("Can't load " BOOTFILE ": "); printf(loader_strerror(rc)); verbose = old_verbose; btn |= BOOTLOADER_BOOT_OF; sleep(5*HZ); } else goto main_exit; } if(btn & BOOTLOADER_BOOT_OF) { /* Load original mi4 firmware in to a memory buffer called loadbuffer. The rest of the loading is done in crt0.S. 1) First try reading from the hidden partition (on Sansa only). 2) Next try a decrypted mi4 file in /System/OF.mi4 3) Finally, try a raw firmware binary in /System/OF.bin. It should be a mi4 firmware decrypted and header stripped using mi4code. */ printf("Loading original firmware..."); #if (CONFIG_STORAGE & STORAGE_SD) /* First try a (hidden) firmware partition */ printf("Trying firmware partition"); pinfo = disk_partinfo(1); if(pinfo->type == PARTITION_TYPE_OS2_HIDDEN_C_DRIVE) { rc = load_mi4_part(loadbuffer, pinfo, MAX_LOADSIZE, usb == USB_INSERTED); if (rc <= EFILE_EMPTY) { printf("Can't load from partition"); printf(loader_strerror(rc)); } else { goto main_exit; } } else { printf("No hidden partition found."); } #endif #if defined(PHILIPS_HDD1630) || defined(PHILIPS_HDD6330) || defined(PHILIPS_SA9200) printf("Trying /System/OF.ebn"); rc=load_mi4(loadbuffer, "/System/OF.ebn", MAX_LOADSIZE); if (rc <= EFILE_EMPTY) { printf("Can't load /System/OF.ebn"); printf(loader_strerror(rc)); } else { goto main_exit; } #endif printf("Trying /System/OF.mi4"); rc=load_mi4(loadbuffer, "/System/OF.mi4", MAX_LOADSIZE); if (rc <= EFILE_EMPTY) { printf("Can't load /System/OF.mi4"); printf(loader_strerror(rc)); } else { #if defined(SAMSUNG_YH925) lcd_reset(); #endif goto main_exit; } printf("Trying /System/OF.bin"); rc=load_raw_firmware(loadbuffer, "/System/OF.bin", MAX_LOADSIZE); if (rc <= EFILE_EMPTY) { printf("Can't load /System/OF.bin"); printf(loader_strerror(rc)); } else { #if defined(SAMSUNG_YH925) lcd_reset(); #endif goto main_exit; } error(0, 0, true); } main_exit: #ifdef HAVE_BOOTLOADER_USB_MODE storage_close(); system_prepare_fw_start(); #endif return (void*)loadbuffer; }