/*-------------------------------------------------------------------------------------*\ * bootstraploader für MSP430 * Version 0.1 * Haiko Schiller 09.02.2007 * Guenther Diesing 02.03.2007 Anpassung fuer Linux Kernel \*-------------------------------------------------------------------------------------*/ #include #include #include "k_debug.h" #include "k_file.h" #include "msp_bsl.h" #include "bsl.h" #include "bslcomm.h" #define BSL_VERSION "0.1" struct Version { unsigned short bsl_version; unsigned short fw_version; unsigned short fw_checksum; }; struct toDoList { unsigned MassErase : 1; unsigned EraseCheck: 1; unsigned Erase : 1; unsigned Program : 1; unsigned Verify : 1; unsigned Reset : 1; unsigned Wait : 1; /*--- Wait for at end of program (0: no; 1: yes): ---*/ unsigned OnePass : 1; /*--- Do EraseCheck, Program and Verify ---*/ /*--- in one pass (TI TXT file is read only once) ---*/ } toDo; unsigned char blkin [MAX_DATA_BYTES]; /* Receive buffer */ unsigned char blkout[MAX_DATA_BYTES]; /* Transmit buffer */ int try_sync = 10; /*--- 10 trys ---*/ int bsl_timeout = 10; /*--- 1000ms ---*/ void *errData= NULL; int byteCtr= 0; /*-------------------------------------------------------------------------------------*\ dummy for Signalhandler \*-------------------------------------------------------------------------------------*/ #if 0 void Dummy(int Sig) { gExit = TRUE; signal( SIGINT, Dummy ); Sig = Sig; } #endif /*-------------------------------------------------------------------------------------*\ * Load PC with 0x0220. This will invoke the patched bootstrap loader subroutines. \*-------------------------------------------------------------------------------------*/ #ifdef WORKAROUND int preparePatch(void) { int error= ERR_NONE; /*--- Command: Load PC, Address to load into PC, No additional data! ---*/ error= bslTxRx(BSL_LOADPC, 0x0220, 0, NULL, blkin); if (error != ERR_NONE) return(error); BSLMemAccessWarning= 0; /* Error is removed within workaround code */ return(error); } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ void postPatch(void) { BSLMemAccessWarning= 1; /* Turn warning back on. */ } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ int PatchTxRx(unsigned char cmd, unsigned short addr, unsigned short len, unsigned char* blkout, unsigned char* blkin, int patched) { int error= ERR_NONE; if( patched && patchloaded ) { error= preparePatch(); if (error != ERR_NONE) return(error); } error= bslTxRx(BSL_CMD_RXBLK, addr, len, NULL, blkin); if( patched ) { postPatch(); } return(error); } #else int PatchTxRx(unsigned char cmd, unsigned short addr, unsigned short len, unsigned char* blkout, unsigned char* blkin, int patched) { patched=patched; return bslTxRx(cmd, addr, len, blkout, blkin); } #endif /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ int erasecheckBLK(unsigned short addr, unsigned short len) { int i= 0; int error= ERR_NONE; _D("Erase Check starting at %x, %i bytes...\n", addr, len); error= PatchTxRx(BSL_CMD_RXBLK, addr, len, NULL, blkin,1); if (error != ERR_NONE) { _D("Erase Check Error: %i\n", error); return(error); /* Cancel, if read error */ } for (i= 0; i < len; i++) { if (blkin[i] != 0xff) { _E("Erase Check failed at %x (%x)\n", addr+i, blkin[i]); return(ERR_BSL_ERASE_CHECK); /* Erase Check failed! */ } } return(error); } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ int verifyBlk(unsigned short addr, unsigned short len) { int i= 0; int error= ERR_NONE; _D("Verify starting at %x, %i bytes...\n", addr, len); error= PatchTxRx(BSL_CMD_RXBLK, addr, len, NULL, blkin,1); if (error != ERR_NONE) { _D("Verify Error: %i\n", error); return(error); /* Cancel, if read error */ } for (i= 0; i < len; i++) { if (blkin[i] != blkout[i]) { _E("Verification failed at %x (%x, %x)\n", addr+i, blkin[i], blkout[i]); return(ERR_BSL_VERIFY); /* Verify failed! */ } } return(error); } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ #define FLASHSEGMASK 0xFE00 /* segsize 0x0200 */ int eraseBlk(unsigned short addr, unsigned short len) { int error = ERR_NONE; unsigned short addr2 = addr+len-1; error = bslTxRx(BSL_CMD_ERASE, addr, BSL_LEN_ERASE_SEGMENT, NULL, blkin); if( error!=ERR_NONE ) return error; if( (addr & FLASHSEGMASK)!=(addr2 & FLASHSEGMASK) ) { _D("erase crosses segment border\n"); error = bslTxRx(BSL_CMD_ERASE, addr2, BSL_LEN_ERASE_SEGMENT, NULL, blkin); } return error; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ int programBlk(unsigned short addr, unsigned short len, unsigned action) { int i= 0; int error= ERR_NONE; if( action & ACTION_PASSWD ) { return( bslTxRx(BSL_CMD_TXPWORD, addr, len, blkout, blkin) ); } /* Check, if specified range is erased: */ if( action & ACTION_ERASE_CHECK ) { error= erasecheckBLK(addr, len); if( error==ERR_NONE ) { /* skip erase stage */ action &= ~ACTION_ERASE; } else { _I("EraseCheck Failed -> %s\n", (action & ACTION_ERASE) ? "erase" : ""); } } if (action & ACTION_ERASE) { _D("Erase starting at %x, %i bytes...\n", addr, len); error= eraseBlk(addr, len); } /* erae failed, skip the rest */ if (error != ERR_NONE) { _D("Erase Error: %i\n", error); return(error); } if( action & ACTION_PROGRAM ) { _D("Program starting at %x, %i bytes...\n", addr, len); /* Program block: */ error= PatchTxRx(BSL_CMD_TXBLK, addr, len, blkout, blkin,1); if (error != ERR_NONE) { _D("Program Error: %i\n", error); return(error); /* Cancel, if error (ACTION_VERIFY is skipped!) */ } } if( action & ACTION_VERIFY ) { error= verifyBlk(addr, len); } return(error); } /*-------------------------------------------------------------------------------------*\ GD: 01.03.2007 \*-------------------------------------------------------------------------------------*/ #define MAX_FWDATALEN (1024*8) /* Flashsize of msp430 */ #define MAX_FWSECTIONS 128 /* a guess */ static unsigned char fwdata[MAX_FWDATALEN]; struct s_section { unsigned short addr; unsigned short addrvalid; unsigned int datapos; unsigned short length; }; static struct s_section section[MAX_FWSECTIONS]; static unsigned int sectioncnt = 0; #define IS_HEXCHAR(c) (((c)>='0'&&(c)<='9')||((c)>='a'&&(c)<='f')||((c)>='A'&&(c)<='F')) static int char2hex( char c ) { switch(c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return( c-'0' ); case 'a': case 'A': return 0x0a; case 'b': case 'B': return 0x0b; case 'c': case 'C': return 0x0c; case 'd': case 'D': return 0x0d; case 'e': case 'E': return 0x0e; case 'f': case 'F': return 0x0f; } _E("ERROR: not a hex char '%c'\n",c); return 0; } /*-------------------------------------------------------------------------------------*\ GD: 01.03.2007 \*-------------------------------------------------------------------------------------*/ static int SectionOK( struct s_section *s, unsigned short *version ) { if( !s->addrvalid ) return ERR_FWFILE_INVALID; if( !s->length ) return ERR_FWFILE_INVALID; /* leave interrupts and reset and bootloader unchanged */ /* leave bootloader unchanged */ if( s->addr + s->length >= 0xFA00 ) s->addrvalid=0; if( s->addr >= 0xFA00 ) s->addrvalid=0; if( s->addr==0xE000 && s->length==2 ) { unsigned char *data = fwdata+s->datapos; *version=(data[1]<<8)+data[0]; } return ERR_NONE; } /*-------------------------------------------------------------------------------------*\ GD: 01.03.2007 \*-------------------------------------------------------------------------------------*/ static int LoadTextfile( char *filename,unsigned short *version) { struct k_file *kfp = NULL; char c; int fwpos = 0; int goon=1; int error = ERR_NONE; struct s_section *actsection=NULL; unsigned int val=0; int digits=0; kfp = K_OPEN( filename, O_RDONLY, 0 ); if( !kfp ) { errData = filename; return ERR_FILE_OPEN; } sectioncnt = 0; while( goon ) { if( (K_READ( kfp, &c, 1)!=1) || (c=='q') ) { // end of file goon=0; continue; } switch(c) { case '@': // new section actsection = §ion[sectioncnt++]; if( sectioncnt>=MAX_FWSECTIONS ) { _E("too many sections (MAX_FWSECTIONS=%d)\n",MAX_FWSECTIONS); error=ERR_FWFILE_INVALID; goon=0; } else { // read sectionaddr actsection->addr = 0; actsection->addrvalid = 0; actsection->datapos = fwpos; actsection->length = 0; } break; default: if( !actsection ) continue; // we have a section, try to get a value if( IS_HEXCHAR(c) ) { val = (val<<4) + char2hex(c); digits++; } else { if( digits ) { // value parsed if( actsection->addrvalid && digits==2 ) { fwdata[fwpos++] = val & 0xff; actsection->length++; if( fwpos>=MAX_FWDATALEN ) { _E("fw too large (MAX_FWDATALEN=%d)\n",MAX_FWDATALEN); error=ERR_FWFILE_INVALID; goon=0; } } else if( !actsection->addrvalid && digits==4 ) { actsection->addr = val & 0xffff; actsection->addrvalid++; } else { _E("invalid value %x (digits=%d)\n",val,digits); error=ERR_FWFILE_INVALID; goon=0; } } else if( !actsection->addrvalid ) { _E("address value expected\n"); error=ERR_FWFILE_INVALID; goon=0; } val=0; digits=0; } break; } // stop if any error occures if( error!=ERR_NONE ) goon=0; } // integrity check of all sections val=0; while( error==ERR_NONE && valaddr,actsection->length,actsection->addrvalid,*version); val++; } K_CLOSE( kfp ); return error; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ int programSection(unsigned short addr, unsigned char *data, unsigned int datalen, unsigned action) { int error = ERR_NONE; unsigned short dataframelen = 0; /* split section into frames */ while( datalen ) { int len = datalen > MAX_DATA_BYTES ? MAX_DATA_BYTES : datalen; memcpy( blkout, data, (size_t)len ); error = programBlk(addr, len, action); if( error!=ERR_NONE ) break; byteCtr += len; addr += len; data += len; datalen -= len; } return(error); } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ int programTIText(unsigned action) { unsigned int i; int error = ERR_NONE; struct s_section *act = §ion[0]; for( i=0 ; iaddrvalid ) continue; error = programSection(act->addr,fwdata+act->datapos,act->length,action); if( error!=ERR_NONE ) break; } return error; } /*-------------------------------------------------------------------------------------*\ * Address == FW_Start, len = FW_Len \*-------------------------------------------------------------------------------------*/ int readChipVersion(struct Version *bsl_version, unsigned short FW_Addr, unsigned short FW_Len) { int error = ERR_NONE; error = bslTxRx(BSL_CMD_VERSION, FW_Addr, FW_Len, NULL, blkin); if (error == ERR_NONE) { memcpy((char *)bsl_version, blkin, sizeof(struct Version)); _I("BSL_Version 0x%x Firmware Version 0x%x Checksum 0x%x\n", bsl_version->bsl_version, bsl_version->fw_version, bsl_version->fw_checksum); } return error; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ int signOff(int error, int passwd) { #if 0 if (toDo.Reset) { bslReset(0); /* Reset MSP430 and start user program. */ } #endif switch (error) { case ERR_NONE: _E("Programming completed!\n"); break; case ERR_BSL_SYNC: _E("ERROR: Syncronization failed!\n"); _E("Device with boot loader connected?\n"); _E("try an other LoopCount!\n"); break; case ERR_BSL_VERIFY: _E("ERROR: Verification failed!\n"); break; case ERR_BSL_ERASE_CHECK: _E("ERROR: Erase check failed!\n"); break; case ERR_FILE_OPEN: _E("ERROR: Unable to open input file \"%s\"!\n", (char*)errData); break; case ERR_FWFILE_INVALID: _E("ERROR: firmware file corrupt !\n"); break; default: if ((passwd) && (error == ERR_RX_NAK)) /* If last command == transmit password && Error: */ _E("ERROR: Password not accepted!\n"); else _E("ERROR: Communication Error!\n"); } /* switch */ /*--- After having released the serial port, the target is no longer supplied via this port! ---*/ if (error == ERR_NONE) return(0); else return(1); } /*-------------------------------------------------------------------------------------*\ * called by main \*-------------------------------------------------------------------------------------*/ static unsigned int force=0; #define VERSION_MAJOR(x) ((x)&0xff) #define VERSION_MINOR(x) (((x)>>8)&0xff) int msp_bsl(char *firmware_file, char *devname) { int i, error = ERR_NONE; int max_buffer_size = 1024; int baud = 19200; int startaddr = 0xE002; /*--- default startaddress ---*/ struct Version bsl_version; unsigned short fwfile_version=0xffff; /* Default: all actions turned on: */ /* erase & erasecheck does not work with onepass */ toDo.MassErase = 0; toDo.EraseCheck= 1; toDo.Erase = 1; toDo.Program = 1; toDo.Verify = 1; toDo.Reset = 1; toDo.Wait = 0; /* obsolete */ toDo.OnePass = 0; /*--- Do program and verify sequential! ---*/ _I("BSL MSP430 Bootstrap Loader (%s)\n", BSL_VERSION); //signal( SIGINT, Dummy ); error = Init_Com(devname, baud); if( error ) goto exit; /* GD */ _I("loading FW from %s\n",firmware_file); error = LoadTextfile(firmware_file,&fwfile_version); if( error ) goto exit; if( fwfile_version==0xffff ) { _E("ERROR: no version section in fwfile found\n"); error = ERR_FWFILE_INVALID; goto exit; } /* GD */ error = readChipVersion(&bsl_version, 0xC000, 0x200); if (error != ERR_NONE) { goto exit; } /* check version */ if( fwfile_version==bsl_version.fw_version && !force ) { _I("MSP430 Firmware %d.%d is uptodate\n", VERSION_MAJOR(fwfile_version), VERSION_MINOR(fwfile_version) ); } else { _I("MSP430 Firmware update from %d.%d to %d.%d\n", VERSION_MAJOR(bsl_version.fw_version), VERSION_MINOR(bsl_version.fw_version), VERSION_MAJOR(fwfile_version), VERSION_MINOR(fwfile_version) ); if (toDo.EraseCheck || toDo.Erase) { int action=0; action |= toDo.EraseCheck ? ACTION_ERASE_CHECK : 0; action |= toDo.Erase ? ACTION_ERASE : 0; _I("Erase sections...\n"); if ((error= programTIText(action)) != ERR_NONE) { signOff(error, 0); } } if (!toDo.OnePass) { if (toDo.Program) { byteCtr= 0; _I("Program sections...\n"); if ((error= programTIText(ACTION_PROGRAM)) != ERR_NONE) { signOff(error, 0); } else { _I("%i bytes programmed.\n", byteCtr); } } if (toDo.Verify) { _I("Verify sections...\n"); if ((error= programTIText(ACTION_VERIFY)) != ERR_NONE) { signOff(error, 0); } } } else { _I("Erase Check Program Verify sections...\n"); if ((error= programTIText(ACTION_ONEPASS)) != ERR_NONE) { signOff(error, 0); } } } //TODO: load addr from section[0] ?? if( toDo.Reset ) { if ((error= bslTxRx(BSL_CMD_LOADPC, (unsigned short)startaddr, 0x0, NULL, blkin)) != ERR_NONE) _E("ERROR: loadpc\n"); else _I("LoadPC war erfolgreich 0x%04x\n",startaddr); } exit: CloseCom(); return error; } MODULE_PARM_DESC(force,"force=1 forces programming without version check (default is 0)"); module_param(force,uint,0);