📄 firmware_flash.c
字号:
/**************************************************************************** __________ __ ___.* Open \______ \ ____ ____ | | _\_ |__ _______ ___* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \* \/ \/ \/ \/ \/* $Id: firmware_flash.c,v 1.10 2004/01/08 09:58:58 bagder Exp $** 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鰎g 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 /* only for target *//* 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/* platform IDs as I have used them in my firmware templates */#define ID_RECORDER 0#define ID_FM 1#define ID_PLAYER 2#define ID_REC_V2 3/* Here I have to check for ARCHOS_* defines in source code, which is generally strongly discouraged. But here I'm not checking for a certain feature, I'm checking for the model itself. */#if defined(ARCHOS_PLAYER)#define FILE_TYPE "player"#define KEEP VERSION_ADR /* keep the firmware version */#define PLATFORM_ID ID_PLAYER#elif defined(ARCHOS_RECORDER)#define FILE_TYPE "rec"#define KEEP MASK_ADR /* keep the mask value */#define PLATFORM_ID ID_RECORDER#elif defined(ARCHOS_RECORDERV2)#define FILE_TYPE "v2"#define KEEP MASK_ADR /* keep the mask value */#define PLATFORM_ID ID_REC_V2#elif defined(ARCHOS_FMRECORDER)#define FILE_TYPE "fm"#define KEEP MASK_ADR /* keep the mask value */#define PLATFORM_ID ID_FM#else#undef PLATFORM_ID /* this platform is not (yet) flashable */#endif#ifdef PLATFORM_ID/* result of the CheckFirmwareFile() function */typedef enum{ eOK = 0, eFileNotFound, /* errors from here on */ eTooBig, eTooSmall, eReadErr, eBadContent, eCrcErr, eBadPlatform,} tCheckResult;/* result of the CheckBootROM() function */typedef enum{ eBootROM, /* the supported boot ROM */ eUnknown, /* unknown boot ROM */ eROMless, /* flash mapped to zero */} tCheckROM;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 PLATFORM_ADR 0xFB /* position of my platform ID 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 + helpers ************//* test if the version number is consistent with the platform */bool CheckPlatform(int platform_id, UINT16 version){ if (version == 200) { /* for my very first firmwares, I foolishly changed it to 200 */ return (platform_id == ID_RECORDER || platform_id == ID_FM); } else if (version == 123) { /* it can be a FM or V2 recorder */ return (platform_id == ID_FM || platform_id == ID_REC_V2); } else if (version == 132) { /* seen on a V2 recorder */ return (platform_id == ID_REC_V2); } else if (version >= 115 && version <= 129) { /* the range of Recorders seen so far */ return (platform_id == ID_RECORDER); } else if (version == 0 || (version >= 300 && version <= 506)) { /* for very old players, I've seen zero */ return (platform_id == ID_PLAYER); } return false; /* unknown */}tCheckResult CheckFirmwareFile(char* filename, int chipsize, bool is_romless){ 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 nor platform ID 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; } /* version number in file plausible with this hardware? */ if (!CheckPlatform(PLATFORM_ID, *(UINT16*)(sector + VERSION_ADR))) { rb->close(fd); return eBadPlatform; } if (has_crc) { crc32 = crc_32(sector, SEC_SIZE, crc32); /* checksum */ /* in addition to the CRC, my files also have a platform ID */ if (sector[PLATFORM_ADR] != PLATFORM_ID) /* for our hardware? */ { rb->close(fd); return eBadPlatform; } } if (is_romless) { /* in this case, there is not much we can check */ if (*(UINT32*)sector != 0x00000200) /* reset vector */ { rb->close(fd); return eBadContent; } } else { /* compare some bytes which have to be identical */ if (*(UINT32*)sector != 0x41524348) /* "ARCH" */ { rb->close(fd); return eBadContent; } for (i = 0x30; i<MASK_ADR-1; i++) /* leave one byte for me */ { if (sector[i] != FB[i]) { rb->close(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; i<chipsize; i+=SEC_SIZE) { if (!EraseSector(FB + i)) { /* nothing we can do, let the programming count the errors */ } if (read == SEC_SIZE) /* not EOF yet */ { read = rb->read(fd, sector, SEC_SIZE); if (i==0) { /* put original value back in */ *(UINT16*)(sector + KEEP) = keep; } for (j=0; j<read; j++) { if (!ProgramByte(FB + i + j, sector[j])) { failures++; } } } } rb->close(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; j<read; j++) { /* position of keep value is no error */ if (FB[i] != sector[j] && i != KEEP && i != (KEEP+1)) { failures++; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -