Extend .hex fileio() to multiple-memory input files

This commit is contained in:
Stefan Rueger
2024-06-16 13:04:25 +01:00
parent dd6fea2e3d
commit 61a130dc64
4 changed files with 139 additions and 49 deletions

View File

@@ -2,6 +2,8 @@
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2000-2004 Brian S. Dean <bsd@bdmicro.com>
* Copyright (C) 2006 Joerg Wunsch <j@uriah.heep.sax.de>
* Copyright (C) 2022- Stefan Rueger <stefan.rueger@urclocks.com>
* Copyright (C) 2023- Hans Eirik Bull
*
* 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
@@ -304,6 +306,20 @@ AVRMEM *avr_new_mem(void) {
return m;
}
// Create memory from name and size
AVRMEM *avr_new_memory(const char *name, int size) {
AVRMEM *m = (AVRMEM *) mmt_malloc(sizeof(*m));
m->desc = cache_string(name);
m->page_size = 1; // Ensure not 0
m->size = size;
m->buf = mmt_malloc(size);
m->tags = mmt_malloc(size);
m->initval = -1; // Unknown value represented as -1
m->bitmask = -1; // Default to -1
return m;
}
AVRMEM_ALIAS *avr_new_memalias(void) {
AVRMEM_ALIAS *m = (AVRMEM_ALIAS *) mmt_malloc(sizeof *m);
m->desc = cache_string("");

View File

@@ -367,6 +367,8 @@ part_def :
m->num_pages = m->size / m->page_size;
}
if(fileio_mem_offset(current_part, m) == -1U)
yywarning("revise fileio_mem_offset(), avrdude.conf entry or memory type assignment");
}
existing_part = locate_part(part_list, current_part->id);

View File

@@ -327,31 +327,98 @@ static int ihex_readrec(struct ihexsrec *ihex, char * rec) {
}
// Extends where memory is put in flat address space of .elf files
unsigned fileio_mem_offset(const AVRPART *p, const AVRMEM *mem) {
AVRMEM *base;
unsigned location =
mem_is_in_flash(mem) && (base = avr_locate_flash(p))? mem->offset - base->offset:
mem_is_io(mem) || mem_is_sram(mem)? MAX_FLASH_SIZE + mem->offset:
mem_is_eeprom(mem)? 0x810000:
mem_is_in_fuses(mem)? 0x820000 + mem_fuse_offset(mem):
mem_is_lock(mem)? 0x830000:
// Classic parts intersperse signature and calibration bytes, this code places them together
!(p->prog_modes & (PM_PDI|PM_UPDI)) && mem_is_signature(mem)? 0x840000:
!(p->prog_modes & (PM_PDI|PM_UPDI)) && mem_is_calibration(mem)? 0x840003:
!(p->prog_modes & (PM_PDI|PM_UPDI)) && mem_is_sigrow(mem)? 0x840010: // Few parts, eg m328pb
// XMEGA parts have signature separate from prodsig, place prodsig at +0x10 as above
(p->prog_modes & PM_PDI) && mem_is_signature(mem)? 0x840000:
(p->prog_modes & PM_PDI) && mem_is_in_sigrow(mem) && (base = avr_locate_sigrow(p))?
0x840010 + mem->offset - base->offset:
mem_is_in_sigrow(mem) && (base = avr_locate_sigrow(p))? 0x840000 + mem->offset - base->offset:
mem_is_sib(mem)? 0x841000: // Arbitrary 0x1000 offset in signature section for sib
mem_is_userrow(mem)? 0x850000:
mem_is_bootrow(mem)? 0x860000:
-1U;
if(location == -1U)
pmsg_error("unable to locate %s's %s in multi-memory address space\n", p->desc, mem->desc);
else if(location >= ANY_MEM_SIZE || location + mem->size > ANY_MEM_SIZE) { // Consider overflow
pmsg_error("%s's %s location [0x%06x, 0x%06x] outside flat address space [0, 0x%06x]\n",
p->desc, mem->desc, location, location + mem->size-1, ANY_MEM_SIZE-1);
location = -1U;
} else if(location < MAX_FLASH_SIZE && location + mem->size > MAX_FLASH_SIZE) {
pmsg_error("%s's %s location [0x%06x, 0x%06x] straddles flash section boundary 0x%06x\n",
p->desc, mem->desc, location, location + mem->size-1, MAX_FLASH_SIZE);
location = -1U;
} else if(location >= MAX_FLASH_SIZE && location/0x10000 != (location + mem->size-1)/0x10000) {
pmsg_error("%s's %s memory location [0x%06x, 0x%06x] straddles memory section boundary 0x%02x0000\n",
p->desc, mem->desc, location, location + mem->size-1, 1+location/0x10000);
location = -1U;
}
return location;
}
// Extract correct memory from large any memory assuming multi-memory model
static int any2mem(const AVRPART *p, const AVRMEM *mem, const Segment *segp,
const AVRMEM *any, unsigned maxsize) {
// Compute location for multi-memory file input
unsigned location = maxsize > MAX_FLASH_SIZE? fileio_mem_offset(p, mem): 0;
if(location == -1U)
return -1;
unsigned ret = 0;
// Copy over memory to right place and return highest written address plus one
for(unsigned i = segp->addr, end = segp->addr + segp->len; i < end; i++)
if(any->tags[location + i]) {
mem->buf[i] = any->buf[location + i];
mem->tags[i] = any->tags[location + i];
ret = i+1;
}
return ret;
}
/*
* Intel Hex to binary buffer
*
* Given an open file 'inf' which contains Intel Hex formatted data,
* parse the file and lay it out within the memory buffer pointed to
* by mem->buf. The segment within buf, segp, is honoured; if data
* were to fall outside of the memory segment, an error is generated.
* Given an open file 'inf' which contains Intel Hex formatted data, parse
* the file, which potentially contains many AVR memories, and lay it out
* in a temporary AVR "any memory". This also determines whether inf
* contains the AVR memory mem to write to. Only the segment within
* mem->buf, segp, is written to.
*
* Return the maximum memory address within mem->buf that was written
* plus one. If an error occurs, return -1.
* Return 0 if nothing was written, otherwise the maximum memory address
* within mem->buf that was written plus one. On error, return -1.
*/
static int ihex2b(const char *infile, FILE *inf, const AVRMEM *mem,
static int ihex2b(const char *infile, FILE *inf, const AVRPART *p, const AVRMEM *mem,
const Segment *segp, unsigned int fileoffset, FILEFMT ffmt) {
const char *errstr;
unsigned int nextaddr, baseaddr, maxaddr;
int lineno, rc, digits;
int lineno, rc;
struct ihexsrec ihex;
lineno = 0;
baseaddr = 0;
maxaddr = 0;
nextaddr = 0;
digits = mem->size > 0x10000? 5: 4;
rewind(inf);
AVRMEM *any = avr_new_memory("any", ANY_MEM_SIZE);
for(char *buffer; (buffer = str_fgets(inf, &errstr)); mmt_free(buffer)) {
lineno++;
@@ -364,21 +431,21 @@ static int ihex2b(const char *infile, FILE *inf, const AVRMEM *mem,
if(rc < 0) {
pmsg_error("invalid record at line %d of %s\n", lineno, infile);
mmt_free(buffer);
return -1;
goto error;
}
if(rc != ihex.cksum) {
if(ffmt == FMT_IHEX) {
pmsg_error("checksum mismatch at line %d of %s\n", lineno, infile);
imsg_error("checksum=0x%02x, computed checksum=0x%02x\n", ihex.cksum, rc);
mmt_free(buffer);
return -1;
goto error;
}
// Just warn with more permissive format FMT_IHXC
pmsg_notice("checksum mismatch at line %d of %s\n", lineno, infile);
imsg_notice("checksum=0x%02x, computed checksum=0x%02x\n", ihex.cksum, rc);
}
unsigned below = 0;
unsigned below = 0, anysize = any->size;
switch (ihex.rectyp) {
case 0: /* data record */
if(ihex.loadofs + baseaddr < fileoffset) {
@@ -387,9 +454,9 @@ static int ihex2b(const char *infile, FILE *inf, const AVRMEM *mem,
ihex.loadofs + baseaddr, fileoffset, lineno, infile);
imsg_error("use -F to skip this check\n");
mmt_free(buffer);
return -1;
goto error;
}
pmsg_warning("address 0x%04x out of range (below fileoffset 0x%x) at line %d of %s\n",
pmsg_warning("address 0x%04x below fileoffset 0x%x at line %d of %s: ",
ihex.loadofs + baseaddr, fileoffset, lineno, infile);
below = fileoffset - baseaddr - ihex.loadofs;
if(below < ihex.reclen) { // Clip record
@@ -398,39 +465,28 @@ static int ihex2b(const char *infile, FILE *inf, const AVRMEM *mem,
} else { // Nothing to write
ihex.reclen = 0;
}
imsg_warning("%s record\n", ihex.reclen? "clipping": "ignoring");
msg_warning("%s record\n", ihex.reclen? "clipping": "ignoring");
}
nextaddr = ihex.loadofs + baseaddr - fileoffset;
unsigned int beg = segp->addr, end = segp->addr + segp->len-1;
if(ihex.reclen && (nextaddr < beg || nextaddr + ihex.reclen-1 > end)) {
if(ihex.reclen && nextaddr + ihex.reclen > anysize) {
if(!ovsigck) {
pmsg_error("Intel Hex record [0x%0*x, 0x%0*x] out of range [0x%0*x, 0x%0*x]\n",
digits, nextaddr, digits, nextaddr+ihex.reclen-1, digits, beg, digits, end);
pmsg_error("Intel Hex record [0x%06x, 0x%06x] out of range [0, 0x%06x]\n",
nextaddr, nextaddr+ihex.reclen-1, anysize-1);
imsg_error("at line %d of %s; use -F to skip this check\n", lineno, infile);
mmt_free(buffer);
return -1;
goto error;
}
pmsg_warning("Intel Hex record [0x%0*x, 0x%0*x] out of range [0x%0*x, 0x%0*x]\n",
digits, nextaddr, digits, nextaddr+ihex.reclen-1, digits, beg, digits, end);
if(nextaddr < beg) {
unsigned low = beg - nextaddr;
if(low < ihex.reclen) { // Clip record
ihex.reclen -= low;
nextaddr += low;
below += low;
} else { // Nothing to write
ihex.reclen = 0;
}
}
if(ihex.reclen && nextaddr + ihex.reclen-1 > end) {
unsigned above = nextaddr + ihex.reclen-1 - end;
pmsg_warning("Intel Hex record [0x%06x, 0x%06x] out of range [0, 0x%06x]: ",
nextaddr, nextaddr+ihex.reclen-1, anysize-1);
if(ihex.reclen && nextaddr + ihex.reclen > anysize) {
unsigned above = nextaddr + ihex.reclen - anysize;
ihex.reclen = above < ihex.reclen? ihex.reclen - above: 0; // Clip or zap
}
imsg_warning("at line %d of %s; %s record\n", lineno, infile, ihex.reclen? "clipping": "ignoring");
msg_warning("%s it\n", ihex.reclen? "clipping": "ignoring");
}
for(int i=0; i<ihex.reclen; i++) {
mem->buf[nextaddr+i] = ihex.data[below + i];
mem->tags[nextaddr+i] = TAG_ALLOCATED;
any->buf[nextaddr+i] = ihex.data[below + i];
any->tags[nextaddr+i] = TAG_ALLOCATED;
}
if(ihex.reclen && nextaddr+ihex.reclen > maxaddr)
maxaddr = nextaddr+ihex.reclen;
@@ -438,7 +494,7 @@ static int ihex2b(const char *infile, FILE *inf, const AVRMEM *mem,
case 1: /* end of file record */
mmt_free(buffer);
return maxaddr;
goto done;
case 2: /* extended segment address record */
baseaddr = (ihex.data[0] << 8 | ihex.data[1]) << 4;
@@ -460,22 +516,32 @@ static int ihex2b(const char *infile, FILE *inf, const AVRMEM *mem,
pmsg_error("do not know how to deal with rectype=%d "
"at line %d of %s\n", ihex.rectyp, lineno, infile);
mmt_free(buffer);
return -1;
goto error;
}
}
if(errstr) {
pmsg_error("read error in Intel Hex file %s: %s\n", infile, errstr);
return -1;
goto error;
}
if (maxaddr == 0) {
pmsg_error("no valid record found in Intel Hex file %s\n", infile);
return -1;
goto error;
}
pmsg_warning("no end of file record found for Intel Hex file %s\n", infile);
return maxaddr;
done:
rc = any2mem(p, mem, segp, any, maxaddr);
avr_free_mem(any);
if(!rc)
pmsg_warning("no %s data found in Intel Hex file %s\n", mem->desc, infile);
return rc;
error:
avr_free_mem(any);
return -1;
}
static unsigned int cksum_srec(const unsigned char *buf, int n, unsigned addr, int addr_width) {
@@ -642,7 +708,7 @@ static int srec_readrec(struct ihexsrec *srec, char *rec) {
}
// Motorola S-Record to binary
static int srec2b(const char *infile, FILE * inf,
static int srec2b(const char *infile, FILE * inf, const AVRPART *p,
const AVRMEM *mem, const Segment *segp, unsigned int fileoffset) {
const char *errstr;
@@ -1150,7 +1216,7 @@ static int fileio_imm(struct fioparms *fio, const char *fname, FILE *f_unused,
static int fileio_ihex(struct fioparms *fio, const char *filename, FILE *f,
const AVRMEM *mem, const Segment *segp, FILEFMT ffmt, Segorder where) {
const AVRPART *p, const AVRMEM *mem, const Segment *segp, FILEFMT ffmt, Segorder where) {
int rc;
@@ -1160,7 +1226,7 @@ static int fileio_ihex(struct fioparms *fio, const char *filename, FILE *f,
break;
case FIO_READ:
rc = ihex2b(filename, f, mem, segp, fio->fileoffset, ffmt);
rc = ihex2b(filename, f, p, mem, segp, fio->fileoffset, ffmt);
break;
default:
@@ -1173,7 +1239,7 @@ static int fileio_ihex(struct fioparms *fio, const char *filename, FILE *f,
static int fileio_srec(struct fioparms *fio, const char *filename, FILE *f,
const AVRMEM *mem, const Segment *segp, Segorder where) {
const AVRPART* p, const AVRMEM *mem, const Segment *segp, Segorder where) {
int rc;
@@ -1183,7 +1249,7 @@ static int fileio_srec(struct fioparms *fio, const char *filename, FILE *f,
break;
case FIO_READ:
rc = srec2b(filename, f, mem, segp, fio->fileoffset);
rc = srec2b(filename, f, p, mem, segp, fio->fileoffset);
break;
default:
@@ -1620,11 +1686,11 @@ static int fileio_segments_normalise(int oprwv, const char *filename, FILEFMT fo
switch(format) {
case FMT_IHEX:
case FMT_IHXC:
thisrc = fileio_ihex(&fio, fname, f, mem, seglist+i, format, where);
thisrc = fileio_ihex(&fio, fname, f, p, mem, seglist+i, format, where);
break;
case FMT_SREC:
thisrc = fileio_srec(&fio, fname, f, mem, seglist+i, where);
thisrc = fileio_srec(&fio, fname, f, p, mem, seglist+i, where);
break;
case FMT_RBIN:

View File

@@ -554,6 +554,7 @@ char *opcode2str(const OPCODE *op, int opnum, int detailed);
/* Functions for AVRMEM structures */
AVRMEM * avr_new_mem(void);
AVRMEM *avr_new_memory(const char *name, int size);
AVRMEM_ALIAS * avr_new_memalias(void);
int avr_initmem(const AVRPART *p);
AVRMEM * avr_dup_mem(const AVRMEM *m);
@@ -1216,6 +1217,9 @@ enum {
FIO_READ_FOR_VERIFY,
};
#define ANY_MEM_SIZE 0x870000 // Max size of multi-memory input file, see fileio_mem_offset()
#define MAX_FLASH_SIZE 0x800000 // rjmp/call can only address 8 MiB in AVR8 architectures
#ifdef __cplusplus
extern "C" {
#endif
@@ -1226,6 +1230,8 @@ char *fileio_fmtstr(FILEFMT format);
int fileio_fmtchr(FILEFMT format);
unsigned fileio_mem_offset(const AVRPART *p, const AVRMEM *mem);
FILE *fileio_fopenr(const char *fname);
int fileio_fmt_autodetect_fp(FILE *f);