From 60589066dd9d3b97f8413050e832e0ffbfc07e84 Mon Sep 17 00:00:00 2001 From: Jörg Hohensohn Date: Tue, 9 Sep 2003 07:02:53 +0000 Subject: I release this plugin to cvs, now that I released new firmware images (see http://joerg.hohensohn.bei.t-online.de/archos/flash) with my new CRC check. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@3953 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/firmware_flash.c | 854 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 854 insertions(+) create mode 100644 apps/plugins/firmware_flash.c (limited to 'apps') diff --git a/apps/plugins/firmware_flash.c b/apps/plugins/firmware_flash.c new file mode 100644 index 0000000000..d24a174c12 --- /dev/null +++ b/apps/plugins/firmware_flash.c @@ -0,0 +1,854 @@ +/*************************************************************************** +* __________ __ ___. +* Open \______ \ ____ ____ | | _\_ |__ _______ ___ +* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +* \/ \/ \/ \/ \/ +* $Id$ +* +* Plugin for reprogramming the whole Flash ROM chip with a new content. +* !!! DON'T MESS WITH THIS CODE UNLESS YOU'RE ABSOLUTELY SHURE WHAT YOU DO !!! +* +* Copyright (C) 2003 Jörg Hohensohn [IDC]Dragon +* +* All files in this archive are subject to the GNU General Public License. +* See the file COPYING in the source tree root for full license agreement. +* +* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +* KIND, either express or implied. +* +****************************************************************************/ +#include "plugin.h" + +#ifndef SIMULATOR + +/* define DUMMY if you only want to "play" with the UI, does no harm */ +/* #define DUMMY */ + +#ifndef UINT8 +#define UINT8 unsigned char +#endif + +#ifndef UINT16 +#define UINT16 unsigned short +#endif + +#ifndef UINT32 +#define UINT32 unsigned long +#endif + +#if defined(ARCHOS_PLAYER) +#define FILENAME "/firmware_play.bin" +#define KEEP VERSION_ADR /* keep the firmware version */ +#elif defined(ARCHOS_RECORDER) +#define FILENAME "/firmware_rec.bin" +#define KEEP MASK_ADR /* keep the mask value */ +#elif defined(ARCHOS_FMRECORDER) +#define FILENAME "/firmware_fm.bin" +#define KEEP MASK_ADR /* keep the mask value */ +#else +#error ("No known platform given!") +#endif + +/* result of the CheckFirmwareFile() function */ +typedef enum +{ + eOK = 0, + eFileNotFound, /* errors from here on */ + eTooBig, + eTooSmall, + eReadErr, + eBadContent, + eCrcErr, +} tCheckResult; + +typedef struct +{ + UINT8 manufacturer; + UINT8 id; + int size; + char name[32]; +} tFlashInfo; + +static struct plugin_api* rb; /* here is a global api struct pointer */ + +#define MASK_ADR 0xFC /* position of hardware mask value in Flash */ +#define VERSION_ADR 0xFE /* position of firmware version value in Flash */ +#define SEC_SIZE 4096 /* size of one flash sector */ +static UINT8* sector; /* better not place this on the stack... */ +static volatile UINT8* FB = (UINT8*)0x02000000; /* Flash base address */ + + +/***************** Flash Functions *****************/ + + +/* read the manufacturer and device ID */ +bool ReadID(volatile UINT8* pBase, UINT8* pManufacturerID, UINT8* pDeviceID) +{ + UINT8 not_manu, not_id; /* read values before switching to ID mode */ + UINT8 manu, id; /* read values when in ID mode */ + + pBase = (UINT8*)((UINT32)pBase & 0xFFF80000); /* down to 512k align */ + + /* read the normal content */ + not_manu = pBase[0]; /* should be 'A' (0x41) and 'R' (0x52) */ + not_id = pBase[1]; /* from the "ARCH" marker */ + + pBase[0x5555] = 0xAA; /* enter command mode */ + pBase[0x2AAA] = 0x55; + pBase[0x5555] = 0x90; /* ID command */ + rb->sleep(HZ/50); /* Atmel wants 20ms pause here */ + + manu = pBase[0]; + id = pBase[1]; + + pBase[0] = 0xF0; /* reset flash (back to normal read mode) */ + rb->sleep(HZ/50); /* Atmel wants 20ms pause here */ + + /* I assume success if the obtained values are different from + the normal flash content. This is not perfectly bulletproof, they + could theoretically be the same by chance, causing us to fail. */ + if (not_manu != manu || not_id != id) /* a value has changed */ + { + *pManufacturerID = manu; /* return the results */ + *pDeviceID = id; + return true; /* success */ + } + return false; /* fail */ +} + + +/* erase the sector which contains the given address */ +bool EraseSector(volatile UINT8* pAddr) +{ +#ifdef DUMMY + (void)pAddr; /* prevents warning */ + return true; +#else + volatile UINT8* pBase = (UINT8*)((UINT32)pAddr & 0xFFF80000); /* round down to 512k align */ + unsigned timeout = 43000; /* the timeout loop should be no less than 25ms */ + + pBase[0x5555] = 0xAA; /* enter command mode */ + pBase[0x2AAA] = 0x55; + pBase[0x5555] = 0x80; /* erase command */ + pBase[0x5555] = 0xAA; /* enter command mode */ + pBase[0x2AAA] = 0x55; + *pAddr = 0x30; /* erase the sector */ + + /* I counted 7 instructions for this loop -> min. 0.58 us per round */ + /* Plus memory waitstates it will be much more, gives margin */ + while (*pAddr != 0xFF && --timeout); /* poll for erased */ + + return (timeout != 0); +#endif +} + + +/* address must be in an erased location */ +inline bool ProgramByte(volatile UINT8* pAddr, UINT8 data) +{ +#ifdef DUMMY + (void)pAddr; /* prevents warnings */ + (void)data; + return true; +#else + unsigned timeout = 35; /* the timeout loop should be no less than 20us */ + + if (~*pAddr & data) /* just a safety feature, not really necessary */ + return false; /* can't set any bit from 0 to 1 */ + + FB[0x5555] = 0xAA; /* enter command mode */ + FB[0x2AAA] = 0x55; + FB[0x5555] = 0xA0; /* byte program command */ + + *pAddr = data; + + /* I counted 7 instructions for this loop -> min. 0.58 us per round */ + /* Plus memory waitstates it will be much more, gives margin */ + while (*pAddr != data && --timeout); /* poll for programmed */ + + return (timeout != 0); +#endif +} + + +/* this returns true if supported and fills the info struct */ +bool GetFlashInfo(tFlashInfo* pInfo) +{ + rb->memset(pInfo, 0, sizeof(tFlashInfo)); + + if (!ReadID(FB, &pInfo->manufacturer, &pInfo->id)) + return false; + + if (pInfo->manufacturer == 0xBF) /* SST */ + { + if (pInfo->id == 0xD6) + { + pInfo->size = 256* 1024; /* 256k */ + rb->strcpy(pInfo->name, "SST39VF020"); + return true; + } + else if (pInfo->id == 0xD7) + { + pInfo->size = 512* 1024; /* 512k */ + rb->strcpy(pInfo->name, "SST39VF040"); + return true; + } + else + return false; + } + return false; +} + + +/*********** Utility Functions ************/ + + +/* Tool function to calculate a CRC32 across some buffer */ +/* third argument is either 0xFFFFFFFF to start or value from last piece */ +unsigned crc_32(unsigned char* buf, unsigned len, unsigned crc32) +{ + /* CCITT standard polynomial 0x04C11DB7 */ + static const unsigned crc32_lookup[16] = + { /* lookup table for 4 bits at a time is affordable */ + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, + 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, + 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD + }; + + unsigned char byte; + unsigned t; + + while (len--) + { + byte = *buf++; /* get one byte of data */ + + /* upper nibble of our data */ + t = crc32 >> 28; /* extract the 4 most significant bits */ + t ^= byte >> 4; /* XOR in 4 bits of data into the extracted bits */ + crc32 <<= 4; /* shift the CRC register left 4 bits */ + crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */ + + /* lower nibble of our data */ + t = crc32 >> 28; /* extract the 4 most significant bits */ + t ^= byte & 0x0F; /* XOR in 4 bits of data into the extracted bits */ + crc32 <<= 4; /* shift the CRC register left 4 bits */ + crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */ + } + + return crc32; +} + + +/*********** Firmware File Functions ************/ + +tCheckResult CheckFirmwareFile(char* filename, int chipsize) +{ + int i; + int fd; + int fileleft; /* size info, how many left for reading */ + int fileread = 0; /* total size as read from the file */ + int read_now; /* how many to read for this sector */ + int got_now; /* how many gotten for this sector */ + unsigned crc32 = 0xFFFFFFFF; /* CCITT init value */ + unsigned file_crc; /* CRC value read from file */ + bool has_crc; + + fd = rb->open(filename, O_RDONLY); + if (fd < 0) + return eFileNotFound; + + fileleft = rb->filesize(fd); + if (fileleft > chipsize) + { + rb->close(fd); + return eTooBig; + } + else if (fileleft < 50000) /* give it some reasonable lower limit */ + { + rb->close(fd); + return eTooSmall; + } + + if (fileleft == 256*1024) + { // original dumped firmware file has no CRC + has_crc = false; + } + else + { + has_crc = true; + fileleft -= sizeof(unsigned); // exclude the last 4 bytes + } + + /* do some sanity checks */ + + got_now = rb->read(fd, sector, SEC_SIZE); /* read first sector */ + fileread += got_now; + fileleft -= got_now; + if (got_now != SEC_SIZE) + { + rb->close(fd); + return eReadErr; + } + + if (has_crc) + crc32 = crc_32(sector, SEC_SIZE, crc32); /* checksum */ + + /* compare some bytes which have to be identical */ + if (*(UINT32*)sector != 0x41524348) /* "ARCH" */ + { + rb->close(fd); + return eBadContent; + } + + for (i = 0x30; iclose(fd); + return eBadContent; + } + } + + /* check if we can read the whole file, and do checksum */ + do + { + read_now = MIN(SEC_SIZE, fileleft); + got_now = rb->read(fd, sector, read_now); + fileread += got_now; + fileleft -= got_now; + + if (read_now != got_now) + { + rb->close(fd); + return eReadErr; + } + + if (has_crc) + { + crc32 = crc_32(sector, got_now, crc32); /* checksum */ + } + } while (fileleft); + + if (has_crc) + { + got_now = rb->read(fd, &file_crc, sizeof(file_crc)); + if (got_now != sizeof(file_crc)) + { + rb->close(fd); + return eReadErr; + } + } + + /* must be EOF now */ + got_now = rb->read(fd, sector, SEC_SIZE); + rb->close(fd); + if (got_now != 0) + return eReadErr; + + if (has_crc && file_crc != crc32) + return eCrcErr; + + return eOK; +} + + +/* returns the # of failures, 0 on success */ +unsigned ProgramFirmwareFile(char* filename, int chipsize) +{ + int i, j; + int fd; + int read = SEC_SIZE; /* how many for this sector */ + UINT16 keep = *(UINT16*)(FB + KEEP); /* we must keep this! */ + unsigned failures = 0; + + fd = rb->open(filename, O_RDONLY); + if (fd < 0) + return false; + + for (i=0; iread(fd, sector, SEC_SIZE); + if (i==0) + { /* put original value back in */ + *(UINT16*)(sector + KEEP) = keep; + } + + for (j=0; jclose(fd); + + return failures; +} + +/* returns the # of failures, 0 on success */ +unsigned VerifyFirmwareFile(char* filename) +{ + int i=0, j; + int fd; + int read = SEC_SIZE; /* how many for this sector */ + unsigned failures = 0; + + fd = rb->open(filename, O_RDONLY); + if (fd < 0) + return false; + + do + { + read = rb->read(fd, sector, SEC_SIZE); + + for (j=0; jclose(fd); + + return failures; +} + + +/***************** User Interface Functions *****************/ + +int WaitForButton(void) +{ + int button; + + do + { + button = rb->button_get(true); + } while (button & BUTTON_REL); + + return button; +} + +#ifdef HAVE_LCD_BITMAP +/* Recorder implementation */ + +/* helper for DoUserDialog() */ +void ShowFlashInfo(tFlashInfo* pInfo) +{ + char buf[32]; + + if (!pInfo->manufacturer) + { + rb->lcd_puts(0, 0, "Flash: M=?? D=??"); + rb->lcd_puts(0, 1, "Impossible to program"); + } + else + { + rb->snprintf(buf, sizeof(buf), "Flash: M=%02x D=%02x", + pInfo->manufacturer, pInfo->id); + rb->lcd_puts(0, 0, buf); + + + if (pInfo->size) + { + rb->lcd_puts(0, 1, pInfo->name); + rb->snprintf(buf, sizeof(buf), "Size: %d KB", pInfo->size / 1024); + rb->lcd_puts(0, 2, buf); + } + else + { + rb->lcd_puts(0, 1, "Unsupported chip"); + } + + } + + rb->lcd_update(); +} + + +/* Kind of our main function, defines the application flow. */ +void DoUserDialog(void) +{ + tFlashInfo FlashInfo; + char buf[32]; + int button; + int rc; /* generic return code */ + int memleft; + unsigned boot_crc; + + rb->lcd_setfont(FONT_SYSFIXED); + + /* check boot ROM */ + boot_crc = crc_32((unsigned char*)0x0, 64*1024, 0xFFFFFFFF); + if (boot_crc != 0x56DBA4EE) /* Version 1 */ + { /* no support for any other yet */ + rb->splash(HZ*3, 0, true, "Wrong boot ROM"); + return; /* exit */ + } + + /* "allocate" memory */ + sector = rb->plugin_get_buffer(&memleft); + if (memleft < SEC_SIZE) /* need buffer for a flash sector */ + { + rb->splash(HZ*3, 0, true, "Out of memory"); + return; /* exit */ + } + + rc = GetFlashInfo(&FlashInfo); + ShowFlashInfo(&FlashInfo); + if (FlashInfo.size == 0) /* no valid chip */ + { + rb->splash(HZ*3, 0, true, "Sorry!"); + return; /* exit */ + } + + rb->lcd_puts(0, 3, "using file:"); + rb->lcd_puts(0, 4, FILENAME); + rb->lcd_puts(0, 6, "[F1] to check file"); + rb->lcd_puts(0, 7, "other key to exit"); + rb->lcd_update(); + + button = WaitForButton(); + if (button != BUTTON_F1) + { + return; + } + + rb->lcd_clear_display(); + rb->lcd_puts(0, 0, "checking..."); + rb->lcd_update(); + + rc = CheckFirmwareFile(FILENAME, FlashInfo.size); + rb->lcd_puts(0, 0, "checked:"); + switch (rc) + { + case eOK: + rb->lcd_puts(0, 1, "File OK."); + break; + case eFileNotFound: + rb->lcd_puts(0, 1, "File not found."); + rb->lcd_puts(0, 2, "Put this in root:"); + rb->lcd_puts(0, 4, FILENAME); + break; + case eTooBig: + rb->lcd_puts(0, 1, "File too big,"); + rb->lcd_puts(0, 2, "larger than chip."); + break; + case eTooSmall: + rb->lcd_puts(0, 1, "File too small."); + rb->lcd_puts(0, 2, "Incomplete?"); + break; + case eReadErr: + rb->lcd_puts(0, 1, "Read error."); + break; + case eBadContent: + rb->lcd_puts(0, 1, "File invalid."); + rb->lcd_puts(0, 2, "Sanity check fail."); + break; + case eCrcErr: + rb->lcd_puts(0, 1, "File invalid."); + rb->lcd_puts(0, 2, "CRC check failed,"); + rb->lcd_puts(0, 3, "checksum mismatch."); + break; + default: + rb->lcd_puts(0, 1, "Check failed."); + break; + } + + if (rc == eOK) + { + rb->lcd_puts(0, 6, "[F2] to program"); + rb->lcd_puts(0, 7, "other key to exit"); + } + else + { /* error occured */ + rb->lcd_puts(0, 6, "Any key to exit"); + } + + rb->lcd_update(); + + button = WaitForButton(); + if (button != BUTTON_F2 || rc != eOK) + { + return; + } + + rb->lcd_clear_display(); + rb->lcd_puts(0, 0, "Program all Flash?"); + rb->lcd_puts(0, 1, "Are you shure?"); + rb->lcd_puts(0, 2, "If it goes wrong,"); + rb->lcd_puts(0, 3, "it kills your box!"); + rb->lcd_puts(0, 4, "See documentation."); + + rb->lcd_puts(0, 6, "[F3] to proceed"); + rb->lcd_puts(0, 7, "other key to exit"); + rb->lcd_update(); + + button = WaitForButton(); + if (button != BUTTON_F3) + { + return; + } + + rb->lcd_clear_display(); + rb->lcd_puts(0, 0, "Programming..."); + rb->lcd_update(); + + rc = ProgramFirmwareFile(FILENAME, FlashInfo.size); + if (rc) + { /* errors */ + rb->lcd_clear_display(); + rb->lcd_puts(0, 0, "Panic:"); + rb->lcd_puts(0, 1, "Programming fail!"); + rb->snprintf(buf, sizeof(buf), "%d errors", rc); + rb->lcd_puts(0, 2, buf); + rb->lcd_update(); + button = WaitForButton(); + } + + rb->lcd_clear_display(); + rb->lcd_puts(0, 0, "Verifying..."); + rb->lcd_update(); + + rc = VerifyFirmwareFile(FILENAME); + + rb->lcd_clear_display(); + if (rc == 0) + { + rb->lcd_puts(0, 0, "Verify OK."); + } + else + { + rb->lcd_puts(0, 0, "Panic:"); + rb->lcd_puts(0, 1, "Verify fail!"); + rb->snprintf(buf, sizeof(buf), "%d errors", rc); + rb->lcd_puts(0, 2, buf); + } + rb->lcd_puts(0, 7, "Any key to exit"); + rb->lcd_update(); + + button = WaitForButton(); +} + +#else /* HAVE_LCD_BITMAP */ +/* Player implementation */ + +/* helper for DoUserDialog() */ +void ShowFlashInfo(tFlashInfo* pInfo) +{ + char buf[32]; + + if (!pInfo->manufacturer) + { + rb->lcd_puts_scroll(0, 0, "Flash: M=? D=?"); + rb->lcd_puts_scroll(0, 1, "Impossible to program"); + WaitForButton(); + } + else + { + rb->snprintf(buf, sizeof(buf), "Flash: M=%02x D=%02x", + pInfo->manufacturer, pInfo->id); + rb->lcd_puts_scroll(0, 0, buf); + + if (pInfo->size) + { + rb->snprintf(buf, sizeof(buf), "Size: %d KB", pInfo->size / 1024); + rb->lcd_puts_scroll(0, 1, buf); + } + else + { + rb->lcd_puts_scroll(0, 1, "Unsupported chip"); + WaitForButton(); + } + } +} + + +void DoUserDialog(void) +{ + tFlashInfo FlashInfo; + char buf[32]; + int button; + int rc; /* generic return code */ + int memleft; + unsigned boot_crc; + + /* check boot ROM */ + boot_crc = crc_32((unsigned char*)0x0, 64*1024, 0xFFFFFFFF); + if (boot_crc != 0x56DBA4EE) /* Version 1 */ + { /* no support for any other yet */ + rb->splash(HZ*3, 0, true, "Wrong boot ROM"); + return; /* exit */ + } + + /* "allocate" memory */ + sector = rb->plugin_get_buffer(&memleft); + if (memleft < SEC_SIZE) /* need buffer for a flash sector */ + { + rb->splash(HZ*3, 0, true, "Out of memory"); + return; /* exit */ + } + + rc = GetFlashInfo(&FlashInfo); + ShowFlashInfo(&FlashInfo); + + if (FlashInfo.size == 0) /* no valid chip */ + { + return; /* exit */ + } + + rb->lcd_puts_scroll(0, 0, FILENAME); + rb->lcd_puts_scroll(0, 1, "[Menu] to check"); + + button = WaitForButton(); + if (button != BUTTON_MENU) + { + return; + } + + rb->lcd_clear_display(); + rb->lcd_puts(0, 0, "Checking..."); + + rc = CheckFirmwareFile(FILENAME, FlashInfo.size); + rb->lcd_puts(0, 0, "Checked:"); + switch (rc) + { + case eOK: + rb->lcd_puts(0, 1, "File OK."); + break; + case eFileNotFound: + rb->lcd_puts_scroll(0, 0, "File not found:"); + rb->lcd_puts_scroll(0, 1, FILENAME); + break; + case eTooBig: + rb->lcd_puts_scroll(0, 0, "File too big,"); + rb->lcd_puts_scroll(0, 1, "larger than chip."); + break; + case eTooSmall: + rb->lcd_puts_scroll(0, 0, "File too small."); + rb->lcd_puts_scroll(0, 1, "Incomplete?"); + break; + case eReadErr: + rb->lcd_puts_scroll(0, 0, "Read error."); + break; + case eBadContent: + rb->lcd_puts_scroll(0, 0, "File invalid."); + rb->lcd_puts_scroll(0, 1, "Sanity check failed."); + break; + case eCrcErr: + rb->lcd_puts_scroll(0, 0, "File invalid."); + rb->lcd_puts_scroll(0, 1, "CRC check failed."); + break; + default: + rb->lcd_puts_scroll(0, 0, "Check failed."); + break; + } + + rb->sleep(HZ*3); + + if (rc == eOK) + { + rb->lcd_puts_scroll(0, 0, "[On] to program,"); + rb->lcd_puts_scroll(0, 1, "other key to exit."); + } + else + { /* error occured */ + return; + } + + button = WaitForButton(); + + if (button != BUTTON_ON) + { + return; + } + + rb->lcd_clear_display(); + rb->lcd_puts_scroll(0, 0, "Are you sure?"); + rb->lcd_puts_scroll(0, 1, "[+] to proceed."); + + button = WaitForButton(); + + if (button != BUTTON_RIGHT) + { + return; + } + + rb->lcd_clear_display(); + rb->lcd_puts_scroll(0, 0, "Programming..."); + + rc = ProgramFirmwareFile(FILENAME, FlashInfo.size); + + if (rc) + { /* errors */ + rb->lcd_clear_display(); + rb->lcd_puts_scroll(0, 0, "Programming failed!"); + rb->snprintf(buf, sizeof(buf), "%d errors", rc); + rb->lcd_puts_scroll(0, 1, buf); + WaitForButton(); + } + + rb->lcd_clear_display(); + rb->lcd_puts_scroll(0, 0, "Verifying..."); + + rc = VerifyFirmwareFile(FILENAME); + + rb->lcd_clear_display(); + + if (rc == 0) + { + rb->lcd_puts_scroll(0, 0, "Verify OK."); + } + else + { + rb->snprintf(buf, sizeof(buf), "Verify failed! %d errors", rc); + rb->lcd_puts_scroll(0, 0, buf); + } + + rb->lcd_puts_scroll(0, 1, "Press any key to exit."); + WaitForButton(); +} + +#endif /* not HAVE_LCD_BITMAP */ + + +/***************** Plugin Entry Point *****************/ + +enum plugin_status plugin_start(struct plugin_api* api, void* parameter) +{ + /* this macro should be called as the first thing you do in the plugin. + it test that the api version and model the plugin was compiled for + matches the machine it is running on */ + TEST_PLUGIN_API(api); + + /* if you don't use the parameter, you can do like + this to avoid the compiler warning about it */ + (void)parameter; + + rb = api; /* copy to global api pointer */ + + /* now go ahead and have fun! */ + DoUserDialog(); + + return PLUGIN_OK; +} + +#endif /* #ifndef SIMULATOR */ + -- cgit v1.2.3