diff options
Diffstat (limited to 'cfe/cfe/main/cfe_ldr_srec.c')
-rw-r--r-- | cfe/cfe/main/cfe_ldr_srec.c | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/cfe/cfe/main/cfe_ldr_srec.c b/cfe/cfe/main/cfe_ldr_srec.c new file mode 100644 index 0000000..b067f2b --- /dev/null +++ b/cfe/cfe/main/cfe_ldr_srec.c @@ -0,0 +1,557 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * SREC Program Loader File: cfe_ldr_srec.c + * + * This program reads Motorola S-record files into memory + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_error.h" +#include "cfe_devfuncs.h" + +#include "cfe.h" +#include "cfe_fileops.h" + +#include "cfe_boot.h" +#include "cfe_bootblock.h" + +#include "cfe_loader.h" + +#include "cfe_mem.h" + +/* ********************************************************************* + * Externs + ********************************************************************* */ + +static int cfe_srecload(cfe_loadargs_t *la); + +const cfe_loader_t srecloader = { + "srec", + cfe_srecload, + 0}; + + +typedef struct linebuf_s { + char linebuf[256]; + int curidx; + int buflen; + int eof; + void *ref; + fileio_ctx_t *fsctx; +} linebuf_t; + +#define initlinebuf(b,r,o) (b)->curidx = 0; (b)->buflen = 0; \ + (b)->eof = 0 ; (b)->ref = r; (b)->fsctx = o + + +/* ********************************************************************* + * readchar(file) + * + * Read a character from a file. It's kind of like getchar + * on "C" FILE devices, but not so fancy. + * + * Input parameters: + * file - pointer to a linebuf_t containing reader state + * + * Return value: + * character, or -1 if at EOF + ********************************************************************* */ + +static int readchar(linebuf_t *file) +{ + int ch; + int res; + + if (file->eof) return -1; + + if (file->curidx == file->buflen) { + for (;;) { + res = fs_read(file->fsctx,file->ref,file->linebuf,sizeof(file->linebuf)); + if (res < 0) { + file->eof = -1; + return -1; + } + if (res == 0) continue; + file->buflen = res; + file->curidx = 0; + break; + } + } + + ch = file->linebuf[file->curidx]; + file->curidx++; + return ch; +} + + +/* ********************************************************************* + * readline(file,buffer,maxlen) + * + * Read a line of text from a file using our crude file stream + * mechanism. + * + * Input parameters: + * file - pointer to a linebuf_t containing reader state + * buffer - will receive line of text + * maxlen - number of bytes that will fit in buffer + * + * Return value: + * 0 if ok, else <0 if at EOF + ********************************************************************* */ + +static int readline(linebuf_t *file,char *buffer,int maxlen) +{ + int ch; + char *ptr; + + ptr = buffer; + maxlen--; /* account for terminating null */ + + while (maxlen) { + ch = readchar(file); + if (ch == -1) return -1; + if (ch == 27) return -1; /* ESC */ + if ((ch == '\n') || (ch == '\r')) break; + *ptr++ = (char) ch; + maxlen--; + } + + *ptr = '\0'; + + return 0; +} + + +/* ********************************************************************* + * getxdigit(c) + * + * Convert a hex digit into its numeric equivalent + * + * Input parameters: + * c - character + * + * Return value: + * value + ********************************************************************* */ + +static int getxdigit(char c) +{ + if ((c >= '0') && (c <= '9')) return c - '0'; + if ((c >= 'A') && (c <= 'F')) return c - 'A' + 10; + if ((c >= 'a') && (c <= 'f')) return c - 'a' + 10; + return -1; +} + +/* ********************************************************************* + * getbyte(line) + * + * Process two hex digits and return the value + * + * Input parameters: + * line - pointer to pointer to characters (updated on exit) + * + * Return value: + * byte value, or <0 if bad hex digits + ********************************************************************* */ + +static int getbyte(char **line) +{ + int res; + int c1,c2; + + c1 = getxdigit(*(*(line)+0)); + if (c1 < 0) return -1; + + c2 = getxdigit(*(*(line)+1)); + if (c2 < 0) return -1; + + res = (c1*16+c2); + (*line) += 2; + + return res; +} + + +/* ********************************************************************* + * procsrec(line,loadaddr,blklen,data) + * + * Process an S-record, reading the data into a local buffer + * and returning the block's address. + * + * Input parameters: + * line - line of text (s-record line) + * loadaddr - will be filled with address where data should go + * blklen - will be filled in with size of record + * data - points to buffer to receive data + * + * Return value: + * <0 if error occured (not an s-record) + ********************************************************************* */ + +static int procsrec(char *line, + unsigned int *loadaddr, + unsigned int *blklen, + unsigned char *data) +{ + char rectype; + unsigned char b; + unsigned int len; + unsigned int minlen; + unsigned int linelen; + unsigned int addr; + unsigned int chksum; + + int idx; + int ret = 0; + + addr = 0; + + if (*line++ != 'S') + return -1; /* not an S record */ + + rectype = *line++; + + minlen = 3; /* type 1 record */ + switch (rectype) { + case '0': + break; + + /* + * data bytes + */ + case '3': + minlen++; + /* fall through */ + case '2': + minlen++; + /* fall through */ + case '1': + chksum = 0; + linelen = getbyte(&line); + if (linelen < minlen) { + xprintf("srec: line too short\n"); + return -1; + } + chksum += (unsigned int)linelen; + + /* + * There are two address bytes in a type 1 record, and three + * in a type 2 record. The high-order byte is first, then + * one or two lower-order bytes. Build up the adddress. + */ + b = getbyte(&line); + chksum += (unsigned int)b; + addr = b; + b = getbyte(&line); + chksum += (unsigned int)b; + addr <<= 8; + addr += b; + if (rectype == '2') { + b = getbyte(&line); + chksum += (unsigned int)b; + addr <<= 8; + addr += b; + } + if (rectype == '3') { + b = getbyte(&line); + chksum += (unsigned int)b; + addr <<= 8; + addr += b; + b = getbyte(&line); + chksum += (unsigned int)b; + addr <<= 8; + addr += b; + } + +#if VERBOSE + printf("Addr: %08X Len: %3u(0x%x)\n", addr , linelen - minlen, + linelen-minlen); +#endif + + *loadaddr = addr; + len = linelen - minlen; + *blklen = len; + + for (idx = 0; idx < len; idx++) { + b = getbyte(&line); + chksum += (unsigned int) b; + data[idx] = (unsigned char ) b; + } + + b = getbyte(&line); + chksum = (~chksum) & 0x000000FF; + if (chksum != b) { + xprintf("Checksum error in s-record file\n"); + return -1; + } + ret = 1; + break; + + case '9': + linelen = getbyte(&line); + b = getbyte(&line); + addr = b; + b = getbyte(&line); + addr <<= 8; + addr += b; + *loadaddr = addr; + ret = -2; + break; + + case '8': + linelen = getbyte(&line); + b = getbyte(&line); + addr = b; + b = getbyte(&line); + addr <<= 8; + addr += b; + b = getbyte(&line); + addr <<= 8; + addr += b; + *loadaddr = addr; + ret = -2; + break; + + case '7': + linelen = getbyte(&line); + b = getbyte(&line); + addr = b; + b = getbyte(&line); + addr <<= 8; + addr += b; + b = getbyte(&line); + addr <<= 8; + addr += b; + b = getbyte(&line); + addr <<= 8; + addr += b; + *loadaddr = addr; + ret = -2; + break; + + default: + xprintf("Unknown S-record type: %c\n",rectype); + return -1; + break; + } + + return ret; +} + + +/* ********************************************************************* + * cfe_srecload(la) + * + * Read an s-record file + * + * Input parameters: + * la - loader args + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ +static int cfe_srecload(cfe_loadargs_t *la) +{ + int res; + fileio_ctx_t *fsctx; + void *ref; + int devinfo; + int serial = FALSE; + unsigned int loadaddr; + unsigned int blklen; + linebuf_t lb; + char line[256]; + uint8_t data[256]; + int cnt; + unsigned int specaddr; /* current address if loading to a special address */ + int specflg; /* true if in "special address" mode */ + int firstrec = 1; /* true if we have not seen the first record */ + + /* + * We might want to know something about the boot device. + */ + + devinfo = la->la_device ? cfe_getdevinfo(la->la_device) : 0; + + /* + * Figure out if we're loading to a "special address". This lets + * us load S-records into a temporary buffer, ignoring the + * addresses in the records (but using them so we'll know where + * they go relative to each other + */ + + specflg = (la->la_flags & LOADFLG_SPECADDR) ? 1 : 0; + specaddr = 0; + + /* + * If the device is a serial port, we want to know that. + */ + + if ((devinfo >= 0) && + ((devinfo & CFE_DEV_MASK) == CFE_DEV_SERIAL)) { + serial = TRUE; + } + + /* + * Create a file system context + */ + + res = fs_init(la->la_filesys,&fsctx,la->la_device); + if (res != 0) { + return res; + } + + /* + * Turn on compression if we're doing that. + */ + + if (la->la_flags & LOADFLG_COMPRESSED) { + res = fs_hook(fsctx,"z"); + if (res != 0) { + return res; + } + } + + /* + * Open the boot device + */ + + res = fs_open(fsctx,&ref,la->la_filename,FILE_MODE_READ); + if (res != 0) { + fs_uninit(fsctx); + return res; + } + + /* + * Okay, go load the boot file. Process S-records until + * we get an entry point record (usually at the end). + * + * XXX what if it's *not* at the end? + */ + + initlinebuf(&lb,ref,fsctx); + + cnt = 0; + for (;;) { + /* + * Read a line of text + */ + res = readline(&lb,line,sizeof(line)); + if (res < 0) break; /* reached EOF */ + + /* + * Process the S-record. If at EOF, procsrec returns 0. + * Invalid s-records returns -1. + */ + + if (line[0] == 0) continue; + + res = procsrec(line,&loadaddr,&blklen,data); + +#if 0 + if (res == -2 || res >= 0) + xprintf("."); +#endif + if (res < 0) break; + + /* + * Handle "special address" mode. All S-records will be + * loaded into a buffer passed by the caller to the loader. + * We use the addresses in the S-records to determine + * relative placement of the data, keying on the first + * S-record in the file. + */ + + if ((res == 1) && (specflg)) { + if (firstrec) { + /* First S-record seen sets the base for all that follow */ + specaddr = loadaddr; + firstrec = 0; + } + loadaddr = la->la_address + (intptr_t) (loadaddr - specaddr); + } + + cnt++; + + if (res == 1) { + if (!cfe_arena_loadcheck((intptr_t) loadaddr,blklen)) { + xprintf("Invalid address: %P\n",loadaddr); + res = -1; + break; + } + memcpy((uint8_t *) (intptr_t) (signed)loadaddr,data,blklen); + } + } + + /* + * We're done with the file. + */ + + fs_close(fsctx,ref); + fs_uninit(fsctx); + + /* + * Say something cute on the LEDs. + * Don't do this for every s-record, because if logging the LED + * changes out the serial port, that can take a Long Time. Just + * goes to show: too much cuteness is a _very_ bad thing. + */ + xsprintf(line,"%04d",cnt); + cfe_ledstr(line); + + if (res == -2) { + la->la_entrypt = (intptr_t) (signed) loadaddr; + res = 0; + } + + return res; +} + + |