diff options
Diffstat (limited to 'xs/src/avrdude/fileio.c')
-rw-r--r-- | xs/src/avrdude/fileio.c | 1663 |
1 files changed, 1663 insertions, 0 deletions
diff --git a/xs/src/avrdude/fileio.c b/xs/src/avrdude/fileio.c new file mode 100644 index 000000000..708159295 --- /dev/null +++ b/xs/src/avrdude/fileio.c @@ -0,0 +1,1663 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com> + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* $Id$ */ + +#include "ac_cfg.h" + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> + +#ifdef HAVE_LIBELF +#ifdef HAVE_LIBELF_H +#include <libelf.h> +#elif defined(HAVE_LIBELF_LIBELF_H) +#include <libelf/libelf.h> +#endif +#define EM_AVR32 0x18ad /* inofficial */ +#endif + +#include "avrdude.h" +#include "libavrdude.h" + + +#define IHEX_MAXDATA 256 + +#define MAX_LINE_LEN 256 /* max line length for ASCII format input files */ + +#define MAX_MODE_LEN 32 // For fopen_and_seek() + + +struct ihexrec { + unsigned char reclen; + unsigned int loadofs; + unsigned char rectyp; + unsigned char data[IHEX_MAXDATA]; + unsigned char cksum; +}; + + +static int b2ihex(unsigned char * inbuf, int bufsize, + int recsize, int startaddr, + char * outfile, FILE * outf); + +static int ihex2b(char * infile, FILE * inf, + AVRMEM * mem, int bufsize, unsigned int fileoffset); + +static int b2srec(unsigned char * inbuf, int bufsize, + int recsize, int startaddr, + char * outfile, FILE * outf); + +static int srec2b(char * infile, FILE * inf, + AVRMEM * mem, int bufsize, unsigned int fileoffset); + +static int ihex_readrec(struct ihexrec * ihex, char * rec); + +static int srec_readrec(struct ihexrec * srec, char * rec); + +static int fileio_rbin(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, int size); + +static int fileio_ihex(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, int size); + +static int fileio_srec(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, int size); + +#ifdef HAVE_LIBELF +static int elf2b(char * infile, FILE * inf, + AVRMEM * mem, struct avrpart * p, + int bufsize, unsigned int fileoffset); + +static int fileio_elf(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, + struct avrpart * p, int size); +#endif + +static int fileio_num(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, int size, + FILEFMT fmt); + +static int fmt_autodetect(char * fname, unsigned section); + + + +static FILE *fopen_and_seek(const char *filename, const char *mode, unsigned section) +{ + FILE *file; + // On Windows we need to convert the filename to UTF-16 +#if defined(WIN32NATIVE) + static wchar_t fname_buffer[PATH_MAX]; + static wchar_t mode_buffer[MAX_MODE_LEN]; + + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname_buffer, PATH_MAX) == 0) { return NULL; } + if (MultiByteToWideChar(CP_ACP, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; } + + file = _wfopen(fname_buffer, mode_buffer); +#else + file = fopen(filename, mode); +#endif + + if (file == NULL) { + return NULL; + } + + // Seek to the specified 'section' + static const char *hex_terminator = ":00000001FF\r"; + unsigned terms_seen = 0; + char buffer[MAX_LINE_LEN + 1]; + + while (terms_seen < section && fgets(buffer, MAX_LINE_LEN, file) != NULL) { + size_t len = strlen(buffer); + + if (buffer[len - 1] == '\n') { + len--; + buffer[len] = 0; + } + if (buffer[len - 1] != '\r') { + buffer[len] = '\r'; + len++; + buffer[len] = 0; + } + + if (strcmp(buffer, hex_terminator) == 0) { + // Found a section terminator + terms_seen++; + } + } + + if (feof(file)) { + // Section not found + fclose(file); + return NULL; + } + + return file; +} + + +char * fmtstr(FILEFMT format) +{ + switch (format) { + case FMT_AUTO : return "auto-detect"; break; + case FMT_SREC : return "Motorola S-Record"; break; + case FMT_IHEX : return "Intel Hex"; break; + case FMT_RBIN : return "raw binary"; break; + case FMT_ELF : return "ELF"; break; + default : return "invalid format"; break; + }; +} + + + +static int b2ihex(unsigned char * inbuf, int bufsize, + int recsize, int startaddr, + char * outfile, FILE * outf) +{ + unsigned char * buf; + unsigned int nextaddr; + int n, nbytes, n_64k; + int i; + unsigned char cksum; + + if (recsize > 255) { + avrdude_message(MSG_INFO, "%s: recsize=%d, must be < 256\n", + progname, recsize); + return -1; + } + + n_64k = 0; + nextaddr = startaddr; + buf = inbuf; + nbytes = 0; + + while (bufsize) { + n = recsize; + if (n > bufsize) + n = bufsize; + + if ((nextaddr + n) > 0x10000) + n = 0x10000 - nextaddr; + + if (n) { + cksum = 0; + fprintf(outf, ":%02X%04X00", n, nextaddr); + cksum += n + ((nextaddr >> 8) & 0x0ff) + (nextaddr & 0x0ff); + for (i=0; i<n; i++) { + fprintf(outf, "%02X", buf[i]); + cksum += buf[i]; + } + cksum = -cksum; + fprintf(outf, "%02X\n", cksum); + + nextaddr += n; + nbytes += n; + } + + if (nextaddr >= 0x10000) { + int lo, hi; + /* output an extended address record */ + n_64k++; + lo = n_64k & 0xff; + hi = (n_64k >> 8) & 0xff; + cksum = 0; + fprintf(outf, ":02000004%02X%02X", hi, lo); + cksum += 2 + 0 + 4 + hi + lo; + cksum = -cksum; + fprintf(outf, "%02X\n", cksum); + nextaddr = 0; + } + + /* advance to next 'recsize' bytes */ + buf += n; + bufsize -= n; + } + + /*----------------------------------------------------------------- + add the end of record data line + -----------------------------------------------------------------*/ + cksum = 0; + n = 0; + nextaddr = 0; + fprintf(outf, ":%02X%04X01", n, nextaddr); + cksum += n + ((nextaddr >> 8) & 0x0ff) + (nextaddr & 0x0ff) + 1; + cksum = -cksum; + fprintf(outf, "%02X\n", cksum); + + return nbytes; +} + + +static int ihex_readrec(struct ihexrec * ihex, char * rec) +{ + int i, j; + char buf[8]; + int offset, len; + char * e; + unsigned char cksum; + int rc; + + len = strlen(rec); + offset = 1; + cksum = 0; + + /* reclen */ + if (offset + 2 > len) + return -1; + for (i=0; i<2; i++) + buf[i] = rec[offset++]; + buf[i] = 0; + ihex->reclen = strtoul(buf, &e, 16); + if (e == buf || *e != 0) + return -1; + + /* load offset */ + if (offset + 4 > len) + return -1; + for (i=0; i<4; i++) + buf[i] = rec[offset++]; + buf[i] = 0; + ihex->loadofs = strtoul(buf, &e, 16); + if (e == buf || *e != 0) + return -1; + + /* record type */ + if (offset + 2 > len) + return -1; + for (i=0; i<2; i++) + buf[i] = rec[offset++]; + buf[i] = 0; + ihex->rectyp = strtoul(buf, &e, 16); + if (e == buf || *e != 0) + return -1; + + cksum = ihex->reclen + ((ihex->loadofs >> 8) & 0x0ff) + + (ihex->loadofs & 0x0ff) + ihex->rectyp; + + /* data */ + for (j=0; j<ihex->reclen; j++) { + if (offset + 2 > len) + return -1; + for (i=0; i<2; i++) + buf[i] = rec[offset++]; + buf[i] = 0; + ihex->data[j] = strtoul(buf, &e, 16); + if (e == buf || *e != 0) + return -1; + cksum += ihex->data[j]; + } + + /* cksum */ + if (offset + 2 > len) + return -1; + for (i=0; i<2; i++) + buf[i] = rec[offset++]; + buf[i] = 0; + ihex->cksum = strtoul(buf, &e, 16); + if (e == buf || *e != 0) + return -1; + + rc = -cksum & 0x000000ff; + + return rc; +} + + + +/* + * Intel Hex to binary buffer + * + * Given an open file 'inf' which contains Intel Hex formated data, + * parse the file and lay it out within the memory buffer pointed to + * by outbuf. The size of outbuf, 'bufsize' is honored; if data would + * fall outsize of the memory buffer outbuf, an error is generated. + * + * Return the maximum memory address within 'outbuf' that was written. + * If an error occurs, return -1. + * + * */ +static int ihex2b(char * infile, FILE * inf, + AVRMEM * mem, int bufsize, unsigned int fileoffset) +{ + char buffer [ MAX_LINE_LEN ]; + unsigned int nextaddr, baseaddr, maxaddr; + int i; + int lineno; + int len; + struct ihexrec ihex; + int rc; + + lineno = 0; + baseaddr = 0; + maxaddr = 0; + nextaddr = 0; + + while (fgets((char *)buffer,MAX_LINE_LEN,inf)!=NULL) { + lineno++; + len = strlen(buffer); + if (buffer[len-1] == '\n') + buffer[--len] = 0; + if (buffer[0] != ':') + continue; + rc = ihex_readrec(&ihex, buffer); + if (rc < 0) { + avrdude_message(MSG_INFO, "%s: invalid record at line %d of \"%s\"\n", + progname, lineno, infile); + return -1; + } + else if (rc != ihex.cksum) { + avrdude_message(MSG_INFO, "%s: ERROR: checksum mismatch at line %d of \"%s\"\n", + progname, lineno, infile); + avrdude_message(MSG_INFO, "%s: checksum=0x%02x, computed checksum=0x%02x\n", + progname, ihex.cksum, rc); + return -1; + } + + switch (ihex.rectyp) { + case 0: /* data record */ + if (fileoffset != 0 && baseaddr < fileoffset) { + avrdude_message(MSG_INFO, "%s: ERROR: address 0x%04x out of range (below fileoffset 0x%x) at line %d of %s\n", + progname, baseaddr, fileoffset, lineno, infile); + return -1; + } + nextaddr = ihex.loadofs + baseaddr - fileoffset; + if (nextaddr + ihex.reclen > bufsize) { + avrdude_message(MSG_INFO, "%s: ERROR: address 0x%04x out of range at line %d of %s\n", + progname, nextaddr+ihex.reclen, lineno, infile); + return -1; + } + for (i=0; i<ihex.reclen; i++) { + mem->buf[nextaddr+i] = ihex.data[i]; + mem->tags[nextaddr+i] = TAG_ALLOCATED; + } + if (nextaddr+ihex.reclen > maxaddr) + maxaddr = nextaddr+ihex.reclen; + break; + + case 1: /* end of file record */ + return maxaddr; + break; + + case 2: /* extended segment address record */ + baseaddr = (ihex.data[0] << 8 | ihex.data[1]) << 4; + break; + + case 3: /* start segment address record */ + /* we don't do anything with the start address */ + break; + + case 4: /* extended linear address record */ + baseaddr = (ihex.data[0] << 8 | ihex.data[1]) << 16; + break; + + case 5: /* start linear address record */ + /* we don't do anything with the start address */ + break; + + default: + avrdude_message(MSG_INFO, "%s: don't know how to deal with rectype=%d " + "at line %d of %s\n", + progname, ihex.rectyp, lineno, infile); + return -1; + break; + } + + } /* while */ + + if (maxaddr == 0) { + avrdude_message(MSG_INFO, "%s: ERROR: No valid record found in Intel Hex " + "file \"%s\"\n", + progname, infile); + + return -1; + } + else { + avrdude_message(MSG_INFO, "%s: WARNING: no end of file record found for Intel Hex " + "file \"%s\"\n", + progname, infile); + + return maxaddr; + } +} + +static int b2srec(unsigned char * inbuf, int bufsize, + int recsize, int startaddr, + char * outfile, FILE * outf) +{ + unsigned char * buf; + unsigned int nextaddr; + int n, nbytes, addr_width; + int i; + unsigned char cksum; + + char * tmpl=0; + + if (recsize > 255) { + avrdude_message(MSG_INFO, "%s: ERROR: recsize=%d, must be < 256\n", + progname, recsize); + return -1; + } + + nextaddr = startaddr; + buf = inbuf; + nbytes = 0; + + addr_width = 0; + + while (bufsize) { + + n = recsize; + + if (n > bufsize) + n = bufsize; + + if (n) { + cksum = 0; + if (nextaddr + n <= 0xffff) { + addr_width = 2; + tmpl="S1%02X%04X"; + } + else if (nextaddr + n <= 0xffffff) { + addr_width = 3; + tmpl="S2%02X%06X"; + } + else if (nextaddr + n <= 0xffffffff) { + addr_width = 4; + tmpl="S3%02X%08X"; + } + else { + avrdude_message(MSG_INFO, "%s: ERROR: address=%d, out of range\n", + progname, nextaddr); + return -1; + } + + fprintf(outf, tmpl, n + addr_width + 1, nextaddr); + + cksum += n + addr_width + 1; + + for (i=addr_width; i>0; i--) + cksum += (nextaddr >> (i-1) * 8) & 0xff; + + for (i=nextaddr; i<nextaddr + n; i++) { + fprintf(outf, "%02X", buf[i]); + cksum += buf[i]; + } + + cksum = 0xff - cksum; + fprintf(outf, "%02X\n", cksum); + + nextaddr += n; + nbytes +=n; + } + + /* advance to next 'recsize' bytes */ + bufsize -= n; + } + + /*----------------------------------------------------------------- + add the end of record data line + -----------------------------------------------------------------*/ + cksum = 0; + n = 0; + nextaddr = 0; + + if (startaddr <= 0xffff) { + addr_width = 2; + tmpl="S9%02X%04X"; + } + else if (startaddr <= 0xffffff) { + addr_width = 3; + tmpl="S9%02X%06X"; + } + else if (startaddr <= 0xffffffff) { + addr_width = 4; + tmpl="S9%02X%08X"; + } + + fprintf(outf, tmpl, n + addr_width + 1, nextaddr); + + cksum += n + addr_width +1; + for (i=addr_width; i>0; i--) + cksum += (nextaddr >> (i - 1) * 8) & 0xff; + cksum = 0xff - cksum; + fprintf(outf, "%02X\n", cksum); + + return nbytes; +} + + +static int srec_readrec(struct ihexrec * srec, char * rec) +{ + int i, j; + char buf[8]; + int offset, len, addr_width; + char * e; + unsigned char cksum; + int rc; + + len = strlen(rec); + offset = 1; + cksum = 0; + addr_width = 2; + + /* record type */ + if (offset + 1 > len) + return -1; + srec->rectyp = rec[offset++]; + if (srec->rectyp == 0x32 || srec->rectyp == 0x38) + addr_width = 3; /* S2,S8-record */ + else if (srec->rectyp == 0x33 || srec->rectyp == 0x37) + addr_width = 4; /* S3,S7-record */ + + /* reclen */ + if (offset + 2 > len) + return -1; + for (i=0; i<2; i++) + buf[i] = rec[offset++]; + buf[i] = 0; + srec->reclen = strtoul(buf, &e, 16); + cksum += srec->reclen; + srec->reclen -= (addr_width+1); + if (e == buf || *e != 0) + return -1; + + /* load offset */ + if (offset + addr_width > len) + return -1; + for (i=0; i<addr_width*2; i++) + buf[i] = rec[offset++]; + buf[i] = 0; + srec->loadofs = strtoull(buf, &e, 16); + if (e == buf || *e != 0) + return -1; + + for (i=addr_width; i>0; i--) + cksum += (srec->loadofs >> (i - 1) * 8) & 0xff; + + /* data */ + for (j=0; j<srec->reclen; j++) { + if (offset+2 > len) + return -1; + for (i=0; i<2; i++) + buf[i] = rec[offset++]; + buf[i] = 0; + srec->data[j] = strtoul(buf, &e, 16); + if (e == buf || *e != 0) + return -1; + cksum += srec->data[j]; + } + + /* cksum */ + if (offset + 2 > len) + return -1; + for (i=0; i<2; i++) + buf[i] = rec[offset++]; + buf[i] = 0; + srec->cksum = strtoul(buf, &e, 16); + if (e == buf || *e != 0) + return -1; + + rc = 0xff - cksum; + return rc; +} + + +static int srec2b(char * infile, FILE * inf, + AVRMEM * mem, int bufsize, unsigned int fileoffset) +{ + char buffer [ MAX_LINE_LEN ]; + unsigned int nextaddr, maxaddr; + int i; + int lineno; + int len; + struct ihexrec srec; + int rc; + int reccount; + unsigned char datarec; + + char * msg = 0; + + lineno = 0; + maxaddr = 0; + reccount = 0; + + while (fgets((char *)buffer,MAX_LINE_LEN,inf)!=NULL) { + lineno++; + len = strlen(buffer); + if (buffer[len-1] == '\n') + buffer[--len] = 0; + if (buffer[0] != 0x53) + continue; + rc = srec_readrec(&srec, buffer); + + if (rc < 0) { + avrdude_message(MSG_INFO, "%s: ERROR: invalid record at line %d of \"%s\"\n", + progname, lineno, infile); + return -1; + } + else if (rc != srec.cksum) { + avrdude_message(MSG_INFO, "%s: ERROR: checksum mismatch at line %d of \"%s\"\n", + progname, lineno, infile); + avrdude_message(MSG_INFO, "%s: checksum=0x%02x, computed checksum=0x%02x\n", + progname, srec.cksum, rc); + return -1; + } + + datarec=0; + switch (srec.rectyp) { + case 0x30: /* S0 - header record*/ + /* skip */ + break; + + case 0x31: /* S1 - 16 bit address data record */ + datarec=1; + msg="%s: ERROR: address 0x%04x out of range %sat line %d of %s\n"; + break; + + case 0x32: /* S2 - 24 bit address data record */ + datarec=1; + msg="%s: ERROR: address 0x%06x out of range %sat line %d of %s\n"; + break; + + case 0x33: /* S3 - 32 bit address data record */ + datarec=1; + msg="%s: ERROR: address 0x%08x out of range %sat line %d of %s\n"; + break; + + case 0x34: /* S4 - symbol record (LSI extension) */ + avrdude_message(MSG_INFO, "%s: ERROR: not supported record at line %d of %s\n", + progname, lineno, infile); + return -1; + + case 0x35: /* S5 - count of S1,S2 and S3 records previously tx'd */ + if (srec.loadofs != reccount){ + avrdude_message(MSG_INFO, "%s: ERROR: count of transmitted data records mismatch " + "at line %d of \"%s\"\n", + progname, lineno, infile); + avrdude_message(MSG_INFO, "%s: transmitted data records= %d, expected " + "value= %d\n", + progname, reccount, srec.loadofs); + return -1; + } + break; + + case 0x37: /* S7 Record - end record for 32 bit address data */ + case 0x38: /* S8 Record - end record for 24 bit address data */ + case 0x39: /* S9 Record - end record for 16 bit address data */ + return maxaddr; + + default: + avrdude_message(MSG_INFO, "%s: ERROR: don't know how to deal with rectype S%d " + "at line %d of %s\n", + progname, srec.rectyp, lineno, infile); + return -1; + } + + if (datarec == 1) { + nextaddr = srec.loadofs; + if (nextaddr < fileoffset) { + avrdude_message(MSG_INFO, msg, progname, nextaddr, + "(below fileoffset) ", + lineno, infile); + return -1; + } + nextaddr -= fileoffset; + if (nextaddr + srec.reclen > bufsize) { + avrdude_message(MSG_INFO, msg, progname, nextaddr+srec.reclen, "", + lineno, infile); + return -1; + } + for (i=0; i<srec.reclen; i++) { + mem->buf[nextaddr+i] = srec.data[i]; + mem->tags[nextaddr+i] = TAG_ALLOCATED; + } + if (nextaddr+srec.reclen > maxaddr) + maxaddr = nextaddr+srec.reclen; + reccount++; + } + + } + + avrdude_message(MSG_INFO, "%s: WARNING: no end of file record found for Motorola S-Records " + "file \"%s\"\n", + progname, infile); + + return maxaddr; +} + +#ifdef HAVE_LIBELF +/* + * Determine whether the ELF file section pointed to by `sh' fits + * completely into the program header segment pointed to by `ph'. + * + * Assumes the section has been checked already before to actually + * contain data (SHF_ALLOC, SHT_PROGBITS, sh_size > 0). + * + * Sometimes, program header segments might be larger than the actual + * file sections. On VM architectures, this is used to allow mmapping + * the entire ELF file "as is" (including things like the program + * header table itself). + */ +static inline +int is_section_in_segment(Elf32_Shdr *sh, Elf32_Phdr *ph) +{ + if (sh->sh_offset < ph->p_offset) + return 0; + if (sh->sh_offset + sh->sh_size > ph->p_offset + ph->p_filesz) + return 0; + return 1; +} + +/* + * Return the ELF section descriptor that corresponds to program + * header `ph'. The program header is expected to be of p_type + * PT_LOAD, and to have a nonzero p_filesz. (PT_LOAD sections with a + * zero p_filesz are typically RAM sections that are not initialized + * by file data, e.g. ".bss".) + */ +static Elf_Scn *elf_get_scn(Elf *e, Elf32_Phdr *ph, Elf32_Shdr **shptr) +{ + Elf_Scn *s = NULL; + + while ((s = elf_nextscn(e, s)) != NULL) { + Elf32_Shdr *sh; + size_t ndx = elf_ndxscn(s); + if ((sh = elf32_getshdr(s)) == NULL) { + avrdude_message(MSG_INFO, "%s: ERROR: Error reading section #%u header: %s\n", + progname, (unsigned int)ndx, elf_errmsg(-1)); + continue; + } + if ((sh->sh_flags & SHF_ALLOC) == 0 || + sh->sh_type != SHT_PROGBITS) + /* we are only interested in PROGBITS, ALLOC sections */ + continue; + if (sh->sh_size == 0) + /* we are not interested in empty sections */ + continue; + if (is_section_in_segment(sh, ph)) { + /* yeah, we found it */ + *shptr = sh; + return s; + } + } + + avrdude_message(MSG_INFO, "%s: ERROR: Cannot find a matching section for " + "program header entry @p_vaddr 0x%x\n", + progname, ph->p_vaddr); + return NULL; +} + +static int elf_mem_limits(AVRMEM *mem, struct avrpart * p, + unsigned int *lowbound, + unsigned int *highbound, + unsigned int *fileoff) +{ + int rv = 0; + + if (p->flags & AVRPART_AVR32) { + if (strcmp(mem->desc, "flash") == 0) { + *lowbound = 0x80000000; + *highbound = 0xffffffff; + *fileoff = 0; + } else { + rv = -1; + } + } else { + if (strcmp(mem->desc, "flash") == 0 || + strcmp(mem->desc, "boot") == 0 || + strcmp(mem->desc, "application") == 0 || + strcmp(mem->desc, "apptable") == 0) { + *lowbound = 0; + *highbound = 0x7ffff; /* max 8 MiB */ + *fileoff = 0; + } else if (strcmp(mem->desc, "eeprom") == 0) { + *lowbound = 0x810000; + *highbound = 0x81ffff; /* max 64 KiB */ + *fileoff = 0; + } else if (strcmp(mem->desc, "lfuse") == 0) { + *lowbound = 0x820000; + *highbound = 0x82ffff; + *fileoff = 0; + } else if (strcmp(mem->desc, "hfuse") == 0) { + *lowbound = 0x820000; + *highbound = 0x82ffff; + *fileoff = 1; + } else if (strcmp(mem->desc, "efuse") == 0) { + *lowbound = 0x820000; + *highbound = 0x82ffff; + *fileoff = 2; + } else if (strncmp(mem->desc, "fuse", 4) == 0 && + (mem->desc[4] >= '0' && mem->desc[4] <= '9')) { + /* Xmega fuseN */ + *lowbound = 0x820000; + *highbound = 0x82ffff; + *fileoff = mem->desc[4] - '0'; + } else if (strncmp(mem->desc, "lock", 4) == 0) { + *lowbound = 0x830000; + *highbound = 0x83ffff; + *fileoff = 0; + } else { + rv = -1; + } + } + + return rv; +} + + +static int elf2b(char * infile, FILE * inf, + AVRMEM * mem, struct avrpart * p, + int bufsize, unsigned int fileoffset) +{ + Elf *e; + int rv = -1; + unsigned int low, high, foff; + + if (elf_mem_limits(mem, p, &low, &high, &foff) != 0) { + avrdude_message(MSG_INFO, "%s: ERROR: Cannot handle \"%s\" memory region from ELF file\n", + progname, mem->desc); + return -1; + } + + /* + * The Xmega memory regions for "boot", "application", and + * "apptable" are actually sub-regions of "flash". Refine the + * applicable limits. This allows to select only the appropriate + * sections out of an ELF file that contains section data for more + * than one sub-segment. + */ + if ((p->flags & AVRPART_HAS_PDI) != 0 && + (strcmp(mem->desc, "boot") == 0 || + strcmp(mem->desc, "application") == 0 || + strcmp(mem->desc, "apptable") == 0)) { + AVRMEM *flashmem = avr_locate_mem(p, "flash"); + if (flashmem == NULL) { + avrdude_message(MSG_INFO, "%s: ERROR: No \"flash\" memory region found, " + "cannot compute bounds of \"%s\" sub-region.\n", + progname, mem->desc); + return -1; + } + /* The config file offsets are PDI offsets, rebase to 0. */ + low = mem->offset - flashmem->offset; + high = low + mem->size - 1; + } + + if (elf_version(EV_CURRENT) == EV_NONE) { + avrdude_message(MSG_INFO, "%s: ERROR: ELF library initialization failed: %s\n", + progname, elf_errmsg(-1)); + return -1; + } + if ((e = elf_begin(fileno(inf), ELF_C_READ, NULL)) == NULL) { + avrdude_message(MSG_INFO, "%s: ERROR: Cannot open \"%s\" as an ELF file: %s\n", + progname, infile, elf_errmsg(-1)); + return -1; + } + if (elf_kind(e) != ELF_K_ELF) { + avrdude_message(MSG_INFO, "%s: ERROR: Cannot use \"%s\" as an ELF input file\n", + progname, infile); + goto done; + } + + size_t i, isize; + const char *id = elf_getident(e, &isize); + + if (id == NULL) { + avrdude_message(MSG_INFO, "%s: ERROR: Error reading ident area of \"%s\": %s\n", + progname, infile, elf_errmsg(-1)); + goto done; + } + + const char *endianname; + unsigned char endianess; + if (p->flags & AVRPART_AVR32) { + endianess = ELFDATA2MSB; + endianname = "little"; + } else { + endianess = ELFDATA2LSB; + endianname = "big"; + } + if (id[EI_CLASS] != ELFCLASS32 || + id[EI_DATA] != endianess) { + avrdude_message(MSG_INFO, "%s: ERROR: ELF file \"%s\" is not a " + "32-bit, %s-endian file that was expected\n", + progname, infile, endianname); + goto done; + } + + Elf32_Ehdr *eh; + if ((eh = elf32_getehdr(e)) == NULL) { + avrdude_message(MSG_INFO, "%s: ERROR: Error reading ehdr of \"%s\": %s\n", + progname, infile, elf_errmsg(-1)); + goto done; + } + + if (eh->e_type != ET_EXEC) { + avrdude_message(MSG_INFO, "%s: ERROR: ELF file \"%s\" is not an executable file\n", + progname, infile); + goto done; + } + + const char *mname; + uint16_t machine; + if (p->flags & AVRPART_AVR32) { + machine = EM_AVR32; + mname = "AVR32"; + } else { + machine = EM_AVR; + mname = "AVR"; + } + if (eh->e_machine != machine) { + avrdude_message(MSG_INFO, "%s: ERROR: ELF file \"%s\" is not for machine %s\n", + progname, infile, mname); + goto done; + } + if (eh->e_phnum == 0xffff /* PN_XNUM */) { + avrdude_message(MSG_INFO, "%s: ERROR: ELF file \"%s\" uses extended " + "program header numbers which are not expected\n", + progname, infile); + goto done; + } + + Elf32_Phdr *ph; + if ((ph = elf32_getphdr(e)) == NULL) { + avrdude_message(MSG_INFO, "%s: ERROR: Error reading program header table of \"%s\": %s\n", + progname, infile, elf_errmsg(-1)); + goto done; + } + + size_t sndx; + if (elf_getshdrstrndx(e, &sndx) != 0) { + avrdude_message(MSG_INFO, "%s: ERROR: Error obtaining section name string table: %s\n", + progname, elf_errmsg(-1)); + sndx = 0; + } + + /* + * Walk the program header table, pick up entries that are of type + * PT_LOAD, and have a non-zero p_filesz. + */ + for (i = 0; i < eh->e_phnum; i++) { + if (ph[i].p_type != PT_LOAD || + ph[i].p_filesz == 0) + continue; + + avrdude_message(MSG_NOTICE2, "%s: Considering PT_LOAD program header entry #%d:\n" + " p_vaddr 0x%x, p_paddr 0x%x, p_filesz %d\n", + progname, i, ph[i].p_vaddr, ph[i].p_paddr, ph[i].p_filesz); + + Elf32_Shdr *sh; + Elf_Scn *s = elf_get_scn(e, ph + i, &sh); + if (s == NULL) + continue; + + if ((sh->sh_flags & SHF_ALLOC) && sh->sh_size) { + const char *sname; + + if (sndx != 0) { + sname = elf_strptr(e, sndx, sh->sh_name); + } else { + sname = "*unknown*"; + } + + unsigned int lma; + lma = ph[i].p_paddr + sh->sh_offset - ph[i].p_offset; + + avrdude_message(MSG_NOTICE2, "%s: Found section \"%s\", LMA 0x%x, sh_size %u\n", + progname, sname, lma, sh->sh_size); + + if (lma >= low && + lma + sh->sh_size < high) { + /* OK */ + } else { + avrdude_message(MSG_NOTICE2, " => skipping, inappropriate for \"%s\" memory region\n", + mem->desc); + continue; + } + /* + * 1-byte sized memory regions are special: they are used for fuse + * bits, where multiple regions (in the config file) map to a + * single, larger region in the ELF file (e.g. "lfuse", "hfuse", + * and "efuse" all map to ".fuse"). We silently accept a larger + * ELF file region for these, and extract the actual byte to write + * from it, using the "foff" offset obtained above. + */ + if (mem->size != 1 && + sh->sh_size > mem->size) { + avrdude_message(MSG_INFO, "%s: ERROR: section \"%s\" does not fit into \"%s\" memory:\n" + " 0x%x + %u > %u\n", + progname, sname, mem->desc, + lma, sh->sh_size, mem->size); + continue; + } + + Elf_Data *d = NULL; + while ((d = elf_getdata(s, d)) != NULL) { + avrdude_message(MSG_NOTICE2, " Data block: d_buf %p, d_off 0x%x, d_size %d\n", + d->d_buf, (unsigned int)d->d_off, d->d_size); + if (mem->size == 1) { + if (d->d_off != 0) { + avrdude_message(MSG_INFO, "%s: ERROR: unexpected data block at offset != 0\n", + progname); + } else if (foff >= d->d_size) { + avrdude_message(MSG_INFO, "%s: ERROR: ELF file section does not contain byte at offset %d\n", + progname, foff); + } else { + avrdude_message(MSG_NOTICE2, " Extracting one byte from file offset %d\n", + foff); + mem->buf[0] = ((unsigned char *)d->d_buf)[foff]; + mem->tags[0] = TAG_ALLOCATED; + rv = 1; + } + } else { + unsigned int idx; + + idx = lma - low + d->d_off; + if ((int)(idx + d->d_size) > rv) + rv = idx + d->d_size; + avrdude_message(MSG_DEBUG, " Writing %d bytes to mem offset 0x%x\n", + d->d_size, idx); + memcpy(mem->buf + idx, d->d_buf, d->d_size); + memset(mem->tags + idx, TAG_ALLOCATED, d->d_size); + } + } + } + } +done: + (void)elf_end(e); + return rv; +} +#endif /* HAVE_LIBELF */ + +/* + * Simple itoa() implementation. Caller needs to allocate enough + * space in buf. Only positive integers are handled. + */ +static char *itoa_simple(int n, char *buf, int base) +{ + div_t q; + char c, *cp, *cp2; + + cp = buf; + /* + * Divide by base until the number disappeared, but ensure at least + * one digit will be emitted. + */ + do { + q = div(n, base); + n = q.quot; + if (q.rem >= 10) + c = q.rem - 10 + 'a'; + else + c = q.rem + '0'; + *cp++ = c; + } while (q.quot != 0); + + /* Terminate the string. */ + *cp-- = '\0'; + + /* Now revert the result string. */ + cp2 = buf; + while (cp > cp2) { + c = *cp; + *cp-- = *cp2; + *cp2++ = c; + } + + return buf; +} + + + +static int fileio_rbin(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, int size) +{ + int rc; + unsigned char *buf = mem->buf; + + switch (fio->op) { + case FIO_READ: + rc = fread(buf, 1, size, f); + if (rc > 0) + memset(mem->tags, TAG_ALLOCATED, rc); + break; + case FIO_WRITE: + rc = fwrite(buf, 1, size, f); + break; + default: + avrdude_message(MSG_INFO, "%s: fileio: invalid operation=%d\n", + progname, fio->op); + return -1; + } + + if (rc < 0 || (fio->op == FIO_WRITE && rc < size)) { + avrdude_message(MSG_INFO, "%s: %s error %s %s: %s; %s %d of the expected %d bytes\n", + progname, fio->iodesc, fio->dir, filename, strerror(errno), + fio->rw, rc, size); + return -1; + } + + return rc; +} + + +static int fileio_imm(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, int size) +{ + int rc = 0; + char * e, * p; + unsigned long b; + int loc; + + switch (fio->op) { + case FIO_READ: + loc = 0; + p = strtok(filename, " ,"); + while (p != NULL && loc < size) { + b = strtoul(p, &e, 0); + /* check for binary formated (0b10101001) strings */ + b = (strncmp (p, "0b", 2))? + strtoul (p, &e, 0): + strtoul (p + 2, &e, 2); + if (*e != 0) { + avrdude_message(MSG_INFO, "%s: invalid byte value (%s) specified for immediate mode\n", + progname, p); + return -1; + } + mem->buf[loc] = b; + mem->tags[loc++] = TAG_ALLOCATED; + p = strtok(NULL, " ,"); + rc = loc; + } + break; + default: + avrdude_message(MSG_INFO, "%s: fileio: invalid operation=%d\n", + progname, fio->op); + return -1; + } + + if (rc < 0 || (fio->op == FIO_WRITE && rc < size)) { + avrdude_message(MSG_INFO, "%s: %s error %s %s: %s; %s %d of the expected %d bytes\n", + progname, fio->iodesc, fio->dir, filename, strerror(errno), + fio->rw, rc, size); + return -1; + } + + return rc; +} + + +static int fileio_ihex(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, int size) +{ + int rc; + + switch (fio->op) { + case FIO_WRITE: + rc = b2ihex(mem->buf, size, 32, fio->fileoffset, filename, f); + if (rc < 0) { + return -1; + } + break; + + case FIO_READ: + rc = ihex2b(filename, f, mem, size, fio->fileoffset); + if (rc < 0) + return -1; + break; + + default: + avrdude_message(MSG_INFO, "%s: invalid Intex Hex file I/O operation=%d\n", + progname, fio->op); + return -1; + break; + } + + return rc; +} + + +static int fileio_srec(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, int size) +{ + int rc; + + switch (fio->op) { + case FIO_WRITE: + rc = b2srec(mem->buf, size, 32, fio->fileoffset, filename, f); + if (rc < 0) { + return -1; + } + break; + + case FIO_READ: + rc = srec2b(filename, f, mem, size, fio->fileoffset); + if (rc < 0) + return -1; + break; + + default: + avrdude_message(MSG_INFO, "%s: ERROR: invalid Motorola S-Records file I/O " + "operation=%d\n", + progname, fio->op); + return -1; + break; + } + + return rc; +} + + +#ifdef HAVE_LIBELF +static int fileio_elf(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, + struct avrpart * p, int size) +{ + int rc; + + switch (fio->op) { + case FIO_WRITE: + avrdude_message(MSG_INFO, "%s: ERROR: write operation not (yet) " + "supported for ELF\n", + progname); + return -1; + break; + + case FIO_READ: + rc = elf2b(filename, f, mem, p, size, fio->fileoffset); + return rc; + + default: + avrdude_message(MSG_INFO, "%s: ERROR: invalid ELF file I/O " + "operation=%d\n", + progname, fio->op); + return -1; + break; + } +} + +#endif + +static int fileio_num(struct fioparms * fio, + char * filename, FILE * f, AVRMEM * mem, int size, + FILEFMT fmt) +{ + const char *prefix; + char cbuf[20]; + int base, i, num; + + switch (fmt) { + case FMT_HEX: + prefix = "0x"; + base = 16; + break; + + default: + case FMT_DEC: + prefix = ""; + base = 10; + break; + + case FMT_OCT: + prefix = "0"; + base = 8; + break; + + case FMT_BIN: + prefix = "0b"; + base = 2; + break; + + } + + switch (fio->op) { + case FIO_WRITE: + break; + default: + avrdude_message(MSG_INFO, "%s: fileio: invalid operation=%d\n", + progname, fio->op); + return -1; + } + + for (i = 0; i < size; i++) { + if (i > 0) { + if (putc(',', f) == EOF) + goto writeerr; + } + num = (unsigned int)(mem->buf[i]); + /* + * For a base of 8 and a value < 8 to convert, don't write the + * prefix. The conversion will be indistinguishable from a + * decimal one then. + */ + if (prefix[0] != '\0' && !(base == 8 && num < 8)) { + if (fputs(prefix, f) == EOF) + goto writeerr; + } + itoa_simple(num, cbuf, base); + if (fputs(cbuf, f) == EOF) + goto writeerr; + } + if (putc('\n', f) == EOF) + goto writeerr; + + return 0; + + writeerr: + avrdude_message(MSG_INFO, "%s: error writing to %s: %s\n", + progname, filename, strerror(errno)); + return -1; +} + + +int fileio_setparms(int op, struct fioparms * fp, + struct avrpart * p, AVRMEM * m) +{ + fp->op = op; + + switch (op) { + case FIO_READ: + fp->mode = "r"; + fp->iodesc = "input"; + fp->dir = "from"; + fp->rw = "read"; + break; + + case FIO_WRITE: + fp->mode = "w"; + fp->iodesc = "output"; + fp->dir = "to"; + fp->rw = "wrote"; + break; + + default: + avrdude_message(MSG_INFO, "%s: invalid I/O operation %d\n", + progname, op); + return -1; + break; + } + + /* + * AVR32 devices maintain their load offset within the file itself, + * but AVRDUDE maintains all memory images 0-based. + */ + if ((p->flags & AVRPART_AVR32) != 0) + { + fp->fileoffset = m->offset; + } + else + { + fp->fileoffset = 0; + } + + return 0; +} + + + +static int fmt_autodetect(char * fname, unsigned section) +{ + FILE * f; + unsigned char buf[MAX_LINE_LEN]; + int i; + int len; + int found; + int first = 1; + +#if defined(WIN32NATIVE) + f = fopen_and_seek(fname, "r", section); +#else + f = fopen_and_seek(fname, "rb", section); +#endif + + if (f == NULL) { + avrdude_message(MSG_INFO, "%s: error opening %s: %s\n", + progname, fname, strerror(errno)); + return -1; + } + + while (fgets((char *)buf, MAX_LINE_LEN, f)!=NULL) { + /* check for ELF file */ + if (first && + (buf[0] == 0177 && buf[1] == 'E' && + buf[2] == 'L' && buf[3] == 'F')) { + fclose(f); + return FMT_ELF; + } + + buf[MAX_LINE_LEN-1] = 0; + len = strlen((char *)buf); + if (buf[len-1] == '\n') + buf[--len] = 0; + + /* check for binary data */ + found = 0; + for (i=0; i<len; i++) { + if (buf[i] > 127) { + found = 1; + break; + } + } + if (found) { + fclose(f); + return FMT_RBIN; + } + + /* check for lines that look like intel hex */ + if ((buf[0] == ':') && (len >= 11)) { + found = 1; + for (i=1; i<len; i++) { + if (!isxdigit(buf[1])) { + found = 0; + break; + } + } + if (found) { + fclose(f); + return FMT_IHEX; + } + } + + /* check for lines that look like motorola s-record */ + if ((buf[0] == 'S') && (len >= 10) && isdigit(buf[1])) { + found = 1; + for (i=1; i<len; i++) { + if (!isxdigit(buf[1])) { + found = 0; + break; + } + } + if (found) { + fclose(f); + return FMT_SREC; + } + } + + first = 0; + } + + fclose(f); + return -1; +} + + + +int fileio(int op, char * filename, FILEFMT format, + struct avrpart * p, char * memtype, int size, unsigned section) +{ + int rc; + FILE * f; + char * fname; + struct fioparms fio; + AVRMEM * mem; + int using_stdio; + + mem = avr_locate_mem(p, memtype); + if (mem == NULL) { + avrdude_message(MSG_INFO, "fileio(): memory type \"%s\" not configured for device \"%s\"\n", + memtype, p->desc); + return -1; + } + + rc = fileio_setparms(op, &fio, p, mem); + if (rc < 0) + return -1; + + if (fio.op == FIO_READ) + size = mem->size; + + if (fio.op == FIO_READ) { + /* 0xff fill unspecified memory */ + memset(mem->buf, 0xff, size); + } + memset(mem->tags, 0, size); + + using_stdio = 0; + + if (strcmp(filename, "-")==0) { + return -1; + // Note: we don't want to read stdin or write to stdout as part of Slic3r + // if (fio.op == FIO_READ) { + // fname = "<stdin>"; + // f = stdin; + // } + // else { + // fname = "<stdout>"; + // f = stdout; + // } + // using_stdio = 1; + } + else { + fname = filename; + f = NULL; + } + + if (format == FMT_AUTO) { + int format_detect; + + if (using_stdio) { + avrdude_message(MSG_INFO, "%s: can't auto detect file format when using stdin/out.\n" + "%s Please specify a file format and try again.\n", + progname, progbuf); + return -1; + } + + format_detect = fmt_autodetect(fname, section); + if (format_detect < 0) { + avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n", + progname, fname); + return -1; + } + format = format_detect; + + if (quell_progress < 2) { + avrdude_message(MSG_INFO, "%s: %s file %s auto detected as %s\n", + progname, fio.iodesc, fname, fmtstr(format)); + } + } + +#if defined(WIN32NATIVE) + /* Open Raw Binary and ELF format in binary mode on Windows.*/ + if(format == FMT_RBIN || format == FMT_ELF) + { + if(fio.op == FIO_READ) + { + fio.mode = "rb"; + } + if(fio.op == FIO_WRITE) + { + fio.mode = "wb"; + } + } +#endif + + if (format != FMT_IMM) { + if (!using_stdio) { + f = fopen_and_seek(fname, fio.mode, section); + if (f == NULL) { + avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n", + progname, fio.iodesc, fname, strerror(errno)); + return -1; + } + } + } + + switch (format) { + case FMT_IHEX: + rc = fileio_ihex(&fio, fname, f, mem, size); + break; + + case FMT_SREC: + rc = fileio_srec(&fio, fname, f, mem, size); + break; + + case FMT_RBIN: + rc = fileio_rbin(&fio, fname, f, mem, size); + break; + + case FMT_ELF: +#ifdef HAVE_LIBELF + rc = fileio_elf(&fio, fname, f, mem, p, size); +#else + avrdude_message(MSG_INFO, "%s: can't handle ELF file %s, " + "ELF file support was not compiled in\n", + progname, fname); + rc = -1; +#endif + break; + + case FMT_IMM: + rc = fileio_imm(&fio, fname, f, mem, size); + break; + + case FMT_HEX: + case FMT_DEC: + case FMT_OCT: + case FMT_BIN: + rc = fileio_num(&fio, fname, f, mem, size, format); + break; + + default: + avrdude_message(MSG_INFO, "%s: invalid %s file format: %d\n", + progname, fio.iodesc, format); + return -1; + } + + if (rc > 0) { + if ((op == FIO_READ) && (strcasecmp(mem->desc, "flash") == 0 || + strcasecmp(mem->desc, "application") == 0 || + strcasecmp(mem->desc, "apptable") == 0 || + strcasecmp(mem->desc, "boot") == 0)) { + /* + * if we are reading flash, just mark the size as being the + * highest non-0xff byte + */ + rc = avr_mem_hiaddr(mem); + } + } + if (format != FMT_IMM && !using_stdio) { + fclose(f); + } + + return rc; +} + |