/* SliMP3 server for network bootloader Copyright (C) 2001 Sean Adams 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 program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* usage: bootserv IP_ADDRESS FILENAME [FILENAME]... Usually this is run via the bootload.pl script. It will automatically choose (or create) the right .HEX files and place the necessary static entry in your ARP cache. This program will load the PIC's program and configuration EEPROM with the specified .HEX file(s). If more than one .HEX file is specified, then they will be "overlaid" in the order given. This allows easy patches for localization, user configuration, etc. Three address ranges are programmed: 0x0004 - 0x001F Interrupt service routine 0x0600 - 0x1FFF Main program 0x2100 - 0x21DF User configuration EEPROM (will wipe to default settings unless "patched") The hex file will contain code outside of these ranges - this is just initialization code, so we can run it on the ICE without having to include the boot loader firmware. These sections of the .HEX file are ignored. The bootloader client will protect any invalid ranges, with the exception of 0x21E0 - 0x21FF. This area is reserved for "factory settings" - i.e. the MAC address. If you *really* wanted to monkey with this area, you could. Note that the HEX file is addressed in bytes, but the PIC is 14-bit words on 16-bit boundaries. We only refer to the byte addresses when reading the HEX file. The client does not verify writes, we do. Each chunk of up to 64 words is written once and verified twice. If verification fails, we attempt to re-write it up to ten times before giving up. */ #define VERSION 3 /* more meaningful as absolute addresses instead of offset/length */ #define PIC_MAXADDR 0x21FF #define PIC_BEGIN_ISR 0x0004 #define PIC_END_ISR 0x001F #define PIC_BEGIN_MAIN 0x0600 #define PIC_END_MAIN 0x1FFF #define PIC_BEGIN_CONFIG 0x2100 #define PIC_END_CONFIG 0x21dF // factory area starts at 21e0 /* we send small chunks because EEPROM write is slow, and we don't want to have to wait a long timeout if a packet is lost */ #define MAXCHUNK 64 #define NUMVERIFY 2 #define GIVEUPAFTER 10 #define TIMEOUT 2000 /* milliseconds */ #define BOOTLOAD_PORT 69 /*---*/ #include #include #include #include #include #include #include #include #include #define PF_WRITE 0x01 /* as opposed to read */ #define PF_PROG 0x02 /* program memory, as opposed to configuration EEPROM */ #define PF_PASSOK 0x04 /* client indicating that we are sending the correct password */ #define P_HLEN 18 /* heaader is 18 bytes, max is 18+2*64=146 bytes */ typedef struct { char op; /* only two operations: e = eeprom read/write, l=lcd */ unsigned char flags; unsigned char version; /* version of the client/server, whoever originates the packet */ unsigned char reserved; unsigned short password; /* must be sent with each request */ unsigned short addr; /* address to read/write */ unsigned short len; /* number of bytes to read/write */ char reserved2[8]; char data[MAXCHUNK*2]; } packet; unsigned short image[PIC_MAXADDR+1]; unsigned char clientversion; unsigned short password; int sockfd; struct sockaddr_in *client_addr; struct sockaddr_in *recv_addr; /* reverse byte order (16-bit) of a range of memory this is irrespective of the host's byte order. The intel hex file is always little-endian, and we always communicate in network order len is length of mem, in 16-bit words */ void reversemem(unsigned short *mem, unsigned int len) { int i; char temp, *memc; memc = (char *)mem; for (i=0; i < len*2; i+=2) { temp = memc[i]; memc[i]=memc[i+1]; memc[i+1]=memc[i]; } } /* sends the packet that tells the client to start running it's main program */ void sendrun() { packet pkt; pkt.op='r'; if (sendto(sockfd,&pkt,P_HLEN,0,(struct sockaddr *)client_addr, sizeof(struct sockaddr))==-1) { perror("sendto"); exit(1); } } /* sends a read request, does not wait for a reply */ void sendreadchunk(unsigned short addr, unsigned short len) { packet pkt; pkt.op='e'; pkt.flags = 0; pkt.version = VERSION; pkt.password = password; pkt.addr = htons(addr); pkt.len = htons(len); if (sendto(sockfd,&pkt,P_HLEN,0,(struct sockaddr *)client_addr, sizeof(struct sockaddr))==-1) { perror("sendto"); exit(1); } } /* writes a chunk, does not wait for the reply */ void sendwritechunk(unsigned short addr, unsigned short len) { packet pkt; pkt.op='e'; pkt.flags = PF_WRITE | PF_PROG; pkt.version = VERSION; pkt.password = password; pkt.addr = htons(addr); pkt.len = htons(len); memcpy(pkt.data, image+addr, len*2); if (sendto(sockfd,&pkt,P_HLEN+2*len,0,(struct sockaddr *)client_addr, sizeof(struct sockaddr))==-1) { perror("sendto"); exit(1); } } /* receives the reply from a read/write request, returns 1 on success, 0 on timeout */ /* the recevied data is put in buf and offset is set to the received offset. Max recv */ /* size is passed in len, actual recevied size returned in len */ int recvreadchunk(unsigned short *buf, unsigned short *offset, unsigned short *len) { fd_set readfds; int nready, addr_len; struct timeval timeout; packet recvpkt; FD_ZERO(&readfds); FD_SET(sockfd, &readfds); timeout.tv_sec=TIMEOUT/1000; timeout.tv_usec=TIMEOUT%1000*1000; nready=select(sockfd+1, &readfds, NULL, NULL, &timeout); if (nready==-1) { perror("select"); exit(1); } if (nready!=1) /* timeout */ return(0); printf("sockfd readable\n"); addr_len=sizeof(struct sockaddr); if (recvfrom(sockfd, &recvpkt, (*len * 2 + P_HLEN), 0, (struct sockaddr *)recv_addr, &addr_len)==-1) { perror("recvfrom"); exit(1); } *offset=ntohs(recvpkt.addr); *len=ntohs(recvpkt.len); memcpy(buf, &(recvpkt.data), (*len * 2)); return(1); } /* writes and verifies a chunk. 1=success, 0=failure */ int writeverifychunk(unsigned short addr, unsigned short len) { unsigned short recvbuf[MAXCHUNK]; unsigned short recvaddr; unsigned short recvlen; int verify, verifyfailed, verifyok, attempt; printf("Writing %d words at 0x0%0x\n", len, addr); for (attempt=1; attempt<=GIVEUPAFTER; attempt++) { if (attempt>1) printf(" Writing again, attempt %d\n",attempt); sendwritechunk(addr, len); verifyfailed=0; verifyok=0; for (verify=1; verify<=NUMVERIFY && !verifyfailed; verify++) { if (verify>1) /* client automatically replies to the initial write */ sendreadchunk(addr,len); recvlen=MAXCHUNK; if (recvreadchunk(recvbuf, &recvaddr, &recvlen)) { printf("recvbuf begins: %0x %0x %0x %0x %0x %0x %0x %0x\n",recvbuf[0],recvbuf[1],recvbuf[2],recvbuf[3], recvbuf[4],recvbuf[5],recvbuf[6],recvbuf[7]); if (recvaddr==addr && recvlen==len && bcmp(image+addr, recvbuf, recvlen*2)==0) { printf(" Verify %d, ok\n", verify); verifyok++; } else { printf(" Verify %d, FAIL!\n", verify); verifyfailed=0; // temporarily don't verify verifyok++; } } else { printf(" Verify %d, timeout\n", verify); /* if any of the verification passes times out, we write again */ verifyfailed=1; } if (verifyok==NUMVERIFY) { printf( " OK\n"); return(1); } } } printf("FAILED: Too many attempts!\n"); return(0); } /* writes and verifies each chunk in the specified range (inclusive). 1=success, 0=failure */ int writerange(unsigned int beginaddr, unsigned int endaddr) { int addr, len, failed; for (addr = beginaddr; addr<=endaddr; addr+=MAXCHUNK) { len = endaddr-addr+1 >= MAXCHUNK ? MAXCHUNK : endaddr-addr+1; if (!writeverifychunk(addr, len)) failed++; } } /* convert n-bits ascii hex (0 padded) to an int. Returns -1 on failure */ inline int hextoint (char *hexbegin, int bits) { char *ptr; int n, ret; int nibbles=bits/4; n=0; ret=0; for (ptr=hexbegin; ptr < hexbegin+nibbles; ptr++) { if (*ptr >= '0' && *ptr <= '9') n = *ptr - '0'; else { if (*ptr >='A' && *ptr<='F') n = *ptr - 'A' + 10; else { return(-1); } } ret = ret * 16 + n; } return ret; } /* Read filename (Intel Hex format) into *image; accept addresses ranging from 0 to maxaddr "holes" in the hex file will be left all-ones in the resulting image file format is: len addr ty [----data-------] sum : 06 0000 00 08 30 8A 00 00 28 10 (example line, spaces added) */ #define ri_assert(e, s) { if (!(e)) { \ printf("OOPS! Error reading Intel hex format %s, line %d - %s\n", filename, linenum, (s)); \ exit(1); \ }} void readintel(char *filename, unsigned int maxaddr) { FILE *hexfile; char line[256]; int startaddr, addr, len, rectype, i, d, linenum; unsigned char sum, filechecksum, ourchecksum; int numbytes=0; printf("Loading image: %s\n", filename); hexfile=fopen(filename, "r"); ri_assert((hexfile), "Can't open hex file"); linenum=0; rectype=0; while (fgets(line, 256, hexfile) && rectype!=0x01) { /* 01 is the EOF record */ linenum++; sum=0; ri_assert(line[0] == ':', "line did not begin with ':'"); len = hextoint(line+1, 8); ri_assert(strlen(line) == 1+4+2+len*2+4+1, "incorrect length"); startaddr = hextoint(line+3, 16); ri_assert(startaddr>=0 && startaddr=0, "invalid character in data portion"); ri_assert(addr<=maxaddr, "address out of range"); ((char *)image)[addr%2==0? addr+1: addr-1]=d; /* do pointer arithmetic as a char, and reverse byte order */ sum+=d; addr++; numbytes++; } ourchecksum = 1 + ~(sum + startaddr/256 + startaddr%256 + len + rectype); filechecksum = hextoint(line+9+i*2, 8); //ri_assert(ourchecksum == filechecksum, "bad checksum"); } ri_assert(rectype==0x01, "encountered premature EOF"); } struct sockaddr_in *setupaddr(char *address, int port) { struct in_addr ip; struct sockaddr_in *addr; addr=malloc(sizeof(struct sockaddr)); if (!address || !(inet_aton(address, &ip))) { printf("OOPS! Bad IP address: %s\n", address); exit(1); } addr->sin_family = AF_INET; addr->sin_port = htons(port); addr->sin_addr = ip; return addr; } int main(int argv, char *argc[]) { int i; printf("This program is deprecated. Please use the new Perl version, \"update_firmware.pl\"\n"); exit(1); /* initialize image to all-ones */ /* 16F8777 is 14-bit, so we set to 3fff, network order */ for (i=0; i<=PIC_MAXADDR * 2; i+=2) { ((char *)image)[i] = 0x3f; ((char *)image)[i+1] = 0xff; } /* read in the HEX files */ readintel("MAIN.HEX", PIC_MAXADDR*2+1); printf("image[4]==0x%0x\n",image[5]); if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(1); } client_addr = setupaddr(argc[1], BOOTLOAD_PORT); writerange(PIC_BEGIN_ISR, PIC_END_ISR); writerange(PIC_BEGIN_MAIN, PIC_END_MAIN); writerange(PIC_BEGIN_CONFIG, PIC_END_CONFIG); sendrun(); }