From 02027ab766e0654d1c5c73dc09e5cddcd4fd1525 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Tue, 2 Aug 2022 23:26:01 +0100 Subject: [PATCH 1/5] Enable stdin verification and display correct number of bytes written/verified Counting the number of bytes written to a memory and/or verified is not trivial owing to potential holes in the input file and to potential trailing 0xff bytes in flash memory that are not written per default (but see -A). The new function memstats(), which is best called just after an input file has been read into mem->buf/mem->tags, computes the right number of bytes written and allows easy computation of the number of bytes verified. This commit also changes the strategy for the default verification after writing to a chip memory, so that the input file only needs reading once thus enabling successful verification of stdin input files. Other, minor changes: - Improving the grammar of AVRDUDE output, eg, 1 byte written instead of 1 bytes written - Better description of the input file structure in terms of its sections, the interval it spans, the number of pages, the number of padding bytes in pages, and the number of actually cut off trailing 0xff bytes for flash - Printing or instead of - in the -U routines - Option -V no longer needs to be specified before option -U in order to work As an aside this commit also provides useful helper functions for printing plural(), inname(), outname() and interval() all of which return strings fit for printing. $ avrdude -qp ATmega2560 -c usbtiny -U blink-mega2560+lext-test.hex avrdude: AVR device initialized and ready to accept instructions avrdude: Device signature = 0x1e9801 (probably m2560) avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed To disable this feature, specify the -D option. avrdude: erasing chip avrdude: input file blink-mega2560+lext-test.hex auto detected as Intel Hex avrdude: reading input file blink-mega2560+lext-test.hex for flash with 1346 bytes in 4 sections within [0, 0x3106d] using 7 pages and 446 pad bytes avrdude: writing 1346 bytes flash ... avrdude: 1346 bytes of flash written avrdude: verifying flash memory against blink-mega2560+lext-test.hex avrdude: 1346 bytes of flash verified avrdude done. Thank you. $ avrdude -qp ATmega328P -c usb-bub-ii -U sketch-ending-in-ff.hex avrdude: AVR device initialized and ready to accept instructions avrdude: Device signature = 0x1e950f (probably m328p) avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed To disable this feature, specify the -D option. avrdude: erasing chip avrdude: input file sketch-ending-in-ff.hex auto detected as Intel Hex avrdude: reading input file sketch-ending-in-ff.hex for flash with 2160 bytes in 1 section within [0, 0x888] using 17 pages and 16 pad bytes, cutting off 25 trailing 0xff bytes avrdude: writing 2160 bytes flash ... avrdude: 2160 bytes of flash written avrdude: verifying flash memory against sketch-ending-in-ff.hex avrdude: 2185 bytes of flash verified avrdude done. Thank you. $ echo "Hello, world..." | avrdude -qp ATmega328P -c ... -U eeprom:w:-:r avrdude: AVR device initialized and ready to accept instructions avrdude: Device signature = 0x1e950f (probably m328p) avrdude: reading input file for eeprom avrdude: writing 16 bytes eeprom ... avrdude: 16 bytes of eeprom written avrdude: verifying eeprom memory against avrdude: 16 bytes of eeprom verified avrdude done. Thank you. --- src/libavrdude.h | 20 +++ src/main.c | 14 +- src/update.c | 330 ++++++++++++++++++++++++++++++++--------------- 3 files changed, 252 insertions(+), 112 deletions(-) diff --git a/src/libavrdude.h b/src/libavrdude.h index dad47922..95e08d18 100644 --- a/src/libavrdude.h +++ b/src/libavrdude.h @@ -888,6 +888,7 @@ enum updateflags { UF_NONE = 0, UF_NOWRITE = 1, UF_AUTO_ERASE = 2, + UF_VERIFY = 4, }; @@ -898,6 +899,17 @@ typedef struct update_t { int format; } UPDATE; +typedef struct { // File reads for flash can exclude trailing 0xff, which are cut off + int nbytes, // Number of bytes set including 0xff but excluding cut off, trailing 0xff + nsections, // Number of consecutive sections in source excluding cut off, trailing 0xff + npages, // Number of memory pages needed excluding pages solely with trailing 0xff + nfill, // Number of fill bytes to make up full pages that are needed + ntrailing, // Number of trailing 0xff in source + firstaddr, // First address set in [0, mem->size-1] + lastaddr; // Highest address set by input file +} Filestats; + + #ifdef __cplusplus extern "C" { #endif @@ -910,6 +922,14 @@ extern void free_update(UPDATE * upd); extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags flags); +extern int memstats(struct avrpart *p, char *memtype, int size, Filestats *fsp); + +// Convenience functions for printing +const char *plural(int x); +const char *inname(const char *fn); +const char *outname(const char *fn); +const char *interval(int a, int b); + #ifdef __cplusplus } #endif diff --git a/src/main.c b/src/main.c index 772762f3..f4095d5f 100644 --- a/src/main.c +++ b/src/main.c @@ -237,7 +237,7 @@ static void cleanup_main(void) static void replace_backslashes(char *s) { // Replace all backslashes with forward slashes - for (int i = 0; i < strlen(s); i++) { + for (size_t i = 0; i < strlen(s); i++) { if (s[i] == '\\') { s[i] = '/'; } @@ -267,7 +267,6 @@ int main(int argc, char * argv []) int calibrate; /* 1=calibrate RC oscillator, 0=don't */ char * port; /* device port (/dev/xxx) */ int terminal; /* 1=enter terminal mode, 0=don't */ - int verify; /* perform a verify operation */ char * exitspecs; /* exit specs string from command line */ char * programmer; /* programmer id */ char * partdesc; /* part id */ @@ -284,7 +283,7 @@ int main(int argc, char * argv []) int init_ok; /* Device initialization worked well */ int is_open; /* Device open succeeded */ char * logfile; /* Use logfile rather than stderr for diagnostics */ - enum updateflags uflags = UF_AUTO_ERASE; /* Flags for do_op() */ + enum updateflags uflags = UF_AUTO_ERASE | UF_VERIFY; /* Flags for do_op() */ #if !defined(WIN32) char * homedir; @@ -349,7 +348,6 @@ int main(int argc, char * argv []) p = NULL; ovsigck = 0; terminal = 0; - verify = 1; /* on by default */ quell_progress = 0; exitspecs = NULL; pgm = NULL; @@ -525,12 +523,6 @@ int main(int argc, char * argv []) exit(1); } ladd(updates, upd); - - if (verify && upd->op == DEVICE_WRITE) { - upd = dup_update(upd); - upd->op = DEVICE_VERIFY; - ladd(updates, upd); - } break; case 'v': @@ -538,7 +530,7 @@ int main(int argc, char * argv []) break; case 'V': - verify = 0; + uflags &= ~UF_VERIFY; break; case 'x': diff --git a/src/update.c b/src/update.c index 1840f81f..ffddfa16 100644 --- a/src/update.c +++ b/src/update.c @@ -45,7 +45,7 @@ UPDATE * parse_op(char * s) i = 0; p = s; - while ((i < (sizeof(buf)-1) && *p && (*p != ':'))) + while (i < (int) sizeof(buf)-1 && *p && *p != ':') buf[i++] = *p++; buf[i] = 0; @@ -214,18 +214,124 @@ void free_update(UPDATE * u) } +// Memory statistics considering holes after a file read returned size bytes +int memstats(struct avrpart *p, char *memtype, int size, Filestats *fsp) { + Filestats ret = { 0 }; + AVRMEM *mem = avr_locate_mem(p, memtype); + + if(!mem) { + avrdude_message(MSG_INFO, "%s: %s %s undefined\n", + progname, p->desc, memtype); + return LIBAVRDUDE_GENERAL_FAILURE; + } + + if(!mem->buf || !mem->tags) { + avrdude_message(MSG_INFO, "%s: %s %s is not set\n", + progname, p->desc, memtype); + return LIBAVRDUDE_GENERAL_FAILURE; + } + + int pgsize = mem->page_size; + if(pgsize < 1) + pgsize = 1; + + if(size < 0 || size > mem->size) { + avrdude_message(MSG_INFO, "%s: memstats() size %d at odds with %s %s size %d\n", + progname, size, p->desc, memtype, mem->size); + return LIBAVRDUDE_GENERAL_FAILURE; + } + + ret.lastaddr = -1; + int firstset = 0, insection = 0; + // Scan all memory + for(int addr = 0; addr < mem->size; ) { + int pageset = 0; + // Go page by page + for(int pgi = 0; pgi < pgsize; pgi++, addr++) { + if(mem->tags[addr] & TAG_ALLOCATED) { + if(!firstset) { + firstset = 1; + ret.firstaddr = addr; + } + ret.lastaddr = addr; + // size can be smaller than tags suggest owing to flash trailing-0xff + if(addr < size) { + ret.nbytes++; + if(!pageset) { + pageset = 1; + ret.nfill += pgi; + ret.npages++; + } + if(!insection) { + insection = 1; + ret.nsections++; + } + } else { // Now beyond size returned by input file read + ret.ntrailing++; + if(pageset) + ret.nfill++; + } + } else { // In a hole or beyond input file + insection = 0; + if(pageset) + ret.nfill++; + } + } + } + + if(fsp) + *fsp = ret; + + return LIBAVRDUDE_SUCCESS; +} + + +// Convenience functions for printing +const char *plural(int x) { + return x==1? "": "s"; +} + +const char *inname(const char *fn) { + return !fn? "???": strcmp(fn, "-")? fn: ""; +} + +const char *outname(const char *fn) { + return !fn? "???": strcmp(fn, "-")? fn: ""; +} + +// Return sth like "[0, 0x1ff]" +const char *interval(int a, int b) { + // Cyclic buffer for 20+ temporary interval strings each max 41 bytes at 64-bit int + static char space[20*41 + 80], *sp; + if(!sp || sp-space > (int) sizeof space - 80) + sp = space; + + char *ret = sp; + + sprintf(sp, a<16? "[%d": "[0x%x", a); + sp += strlen(sp); + sprintf(sp, b<16? ", %d]": ", 0x%x]", b); + + // Advance beyond return string in temporary ring buffer + sp += strlen(sp)+1; + + return ret; +} + + int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags flags) { struct avrpart * v; AVRMEM * mem; - int size, vsize; + int size; int rc; + Filestats fs; mem = avr_locate_mem(p, upd->memtype); if (mem == NULL) { - avrdude_message(MSG_INFO, "\"%s\" memory type not defined for part \"%s\"\n", - upd->memtype, p->desc); - return -1; + avrdude_message(MSG_INFO, "%s memory type not defined for part %s\n", + upd->memtype, p->desc); + return LIBAVRDUDE_GENERAL_FAILURE; } AVRMEM_ALIAS * alias_mem = avr_find_memalias(p, mem); @@ -235,167 +341,189 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f strcat(alias_mem_desc, alias_mem->desc); } - if (upd->op == DEVICE_READ) { - /* - * read out the specified device memory and write it to a file - */ + switch (upd->op) { + case DEVICE_READ: + // Read out the specified device memory and write it to a file if (upd->format == FMT_IMM) { avrdude_message(MSG_INFO, - "%s: Invalid file format 'immediate' for output\n", - progname, upd->filename); - return -1; + "%s: Invalid file format 'immediate' for output\n", progname); + return LIBAVRDUDE_GENERAL_FAILURE; } - if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: reading %s%s memory:\n", - progname, mem->desc, alias_mem_desc); - } - report_progress(0,1,"Reading"); + if (quell_progress < 2) + avrdude_message(MSG_INFO, "%s: reading %s%s memory ...\n", + progname, mem->desc, alias_mem_desc); + + report_progress(0, 1, "Reading"); rc = avr_read(pgm, p, upd->memtype, 0); + report_progress(1, 1, NULL); if (rc < 0) { avrdude_message(MSG_INFO, "%s: failed to read all of %s%s memory, rc=%d\n", - progname, mem->desc, alias_mem_desc, rc); - return -1; + progname, mem->desc, alias_mem_desc, rc); + return LIBAVRDUDE_GENERAL_FAILURE; } - report_progress(1,1,NULL); size = rc; if (quell_progress < 2) { if (rc == 0) - avrdude_message(MSG_INFO, "%s: Flash is empty, resulting file has no contents.\n", - progname); - avrdude_message(MSG_INFO, "%s: writing output file \"%s\"\n", - progname, - strcmp(upd->filename, "-")==0 ? "" : upd->filename); + avrdude_message(MSG_INFO, "%s: flash is empty, resulting file has no contents\n", + progname); + avrdude_message(MSG_INFO, "%s: writing output file %s\n", + progname, outname(upd->filename)); } rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size); if (rc < 0) { - avrdude_message(MSG_INFO, "%s: write to file '%s' failed\n", - progname, upd->filename); - return -1; - } - } - else if (upd->op == DEVICE_WRITE) { - /* - * write the selected device memory using data from a file; first - * read the data from the specified file - */ - if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: reading input file \"%s\"\n", - progname, - strcmp(upd->filename, "-")==0 ? "" : upd->filename); + avrdude_message(MSG_INFO, "%s: write to file %s failed\n", + progname, outname(upd->filename)); + return LIBAVRDUDE_GENERAL_FAILURE; } + break; + + case DEVICE_WRITE: + // Write the selected device memory using data from a file + rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1); + if (quell_progress < 2) + avrdude_message(MSG_INFO, "%s: reading input file %s for %s%s\n", + progname, inname(upd->filename), mem->desc, alias_mem_desc); if (rc < 0) { - avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n", - progname, upd->filename); - return -1; + avrdude_message(MSG_INFO, "%s: read from file %s failed\n", + progname, inname(upd->filename)); + return LIBAVRDUDE_GENERAL_FAILURE; } size = rc; - /* - * write the buffer contents to the selected memory type - */ - if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: writing %s%s (%d bytes):\n", - progname, mem->desc, alias_mem_desc, size); - } + if(memstats(p, upd->memtype, size, &fs) < 0) + return LIBAVRDUDE_GENERAL_FAILURE; + + if(quell_progress < 2) { + int level = fs.nsections > 1 || fs.firstaddr > 0 || fs.ntrailing? MSG_INFO: MSG_NOTICE; + + avrdude_message(level, "%*s with %d byte%s in %d section%s within %s\n", + (int) strlen(progname)+1, "", + fs.nbytes, plural(fs.nbytes), + fs.nsections, plural(fs.nsections), + interval(fs.firstaddr, fs.lastaddr)); + if(mem->page_size > 1) { + avrdude_message(level, "%*s using %d page%s and %d pad byte%s", + (int) strlen(progname)+1, "", + fs.npages, plural(fs.npages), + fs.nfill, plural(fs.nfill)); + if(fs.ntrailing) + avrdude_message(level, ", cutting off %d trailing 0xff byte%s", + fs.ntrailing, plural(fs.ntrailing)); + avrdude_message(level, "\n"); + } + } + + // Write the buffer contents to the selected memory type + if (quell_progress < 2) + avrdude_message(MSG_INFO, "%s: writing %d byte%s %s%s ...\n", + progname, fs.nbytes, plural(fs.nbytes), mem->desc, alias_mem_desc); if (!(flags & UF_NOWRITE)) { - report_progress(0,1,"Writing"); + report_progress(0, 1, "Writing"); rc = avr_write(pgm, p, upd->memtype, size, (flags & UF_AUTO_ERASE) != 0); - report_progress(1,1,NULL); - } - else { - /* - * test mode, don't actually write to the chip, output the buffer - * to stdout in intel hex instead - */ + report_progress(1, 1, NULL); + } else { + // Test mode: write to stdout in intel hex rather than to the chip rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size); } if (rc < 0) { avrdude_message(MSG_INFO, "%s: failed to write %s%s memory, rc=%d\n", - progname, mem->desc, alias_mem_desc, rc); - return -1; + progname, mem->desc, alias_mem_desc, rc); + return LIBAVRDUDE_GENERAL_FAILURE; } - vsize = rc; + if (quell_progress < 2) + avrdude_message(MSG_INFO, "%s: %d byte%s of %s%s written\n", + progname, fs.nbytes, plural(fs.nbytes), mem->desc, alias_mem_desc); - if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: %d bytes of %s%s written\n", progname, - vsize, mem->desc, alias_mem_desc); - } + // Fall through for (default) auto verify, ie, unless -V was specified + if (!(flags & UF_VERIFY)) + break; - } - else if (upd->op == DEVICE_VERIFY) { - /* - * verify that the in memory file (p->mem[AVR_M_FLASH|AVR_M_EEPROM]) - * is the same as what is on the chip - */ + case DEVICE_VERIFY: + // Verify that the in memory file is the same as what is on the chip pgm->vfy_led(pgm, ON); + int userverify = upd->op == DEVICE_VERIFY; // Explicit -U :v by user + if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: verifying %s%s memory against %s:\n", - progname, mem->desc, alias_mem_desc, upd->filename); + avrdude_message(MSG_INFO, "%s: verifying %s%s memory against %s\n", + progname, mem->desc, alias_mem_desc, inname(upd->filename)); - avrdude_message(MSG_NOTICE2, "%s: load data %s%s data from input file %s:\n", - progname, mem->desc, alias_mem_desc, upd->filename); + if (userverify) + avrdude_message(MSG_NOTICE, "%s: load %s%s data from input file %s\n", + progname, mem->desc, alias_mem_desc, inname(upd->filename)); } - rc = fileio(FIO_READ_FOR_VERIFY, upd->filename, upd->format, p, upd->memtype, -1); - if (rc < 0) { - avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n", - progname, upd->filename); - return -1; + // No need to read file when fallen through from DEVICE_WRITE + if (userverify) { + rc = fileio(FIO_READ_FOR_VERIFY, upd->filename, upd->format, p, upd->memtype, -1); + + if (rc < 0) { + avrdude_message(MSG_INFO, "%s: read from file %s failed\n", + progname, inname(upd->filename)); + return LIBAVRDUDE_GENERAL_FAILURE; + } + size = rc; + + if(memstats(p, upd->memtype, size, &fs) < 0) + return LIBAVRDUDE_GENERAL_FAILURE; + } else { + // Correct size of last read to include potentially cut off, trailing 0xff (flash) + size = fs.lastaddr+1; } + v = avr_dup_part(p); - size = rc; + if (quell_progress < 2) { - avrdude_message(MSG_NOTICE2, "%s: input file %s contains %d bytes\n", - progname, upd->filename, size); - avrdude_message(MSG_NOTICE2, "%s: reading on-chip %s%s data:\n", - progname, mem->desc, alias_mem_desc); + if (userverify) + avrdude_message(MSG_NOTICE, "%s: input file %s contains %d byte%s\n", + progname, inname(upd->filename), fs.nbytes, plural(fs.nbytes)); + avrdude_message(MSG_NOTICE2, "%s: reading on-chip %s%s data ...\n", + progname, mem->desc, alias_mem_desc); } report_progress (0,1,"Reading"); rc = avr_read(pgm, p, upd->memtype, v); + report_progress (1,1,NULL); if (rc < 0) { avrdude_message(MSG_INFO, "%s: failed to read all of %s%s memory, rc=%d\n", - progname, mem->desc, alias_mem_desc, rc); + progname, mem->desc, alias_mem_desc, rc); pgm->err_led(pgm, ON); avr_free_part(v); - return -1; + return LIBAVRDUDE_GENERAL_FAILURE; } - report_progress (1,1,NULL); - - - if (quell_progress < 2) { + if (quell_progress < 2) avrdude_message(MSG_NOTICE2, "%s: verifying ...\n", progname); - } + rc = avr_verify(p, v, upd->memtype, size); if (rc < 0) { avrdude_message(MSG_INFO, "%s: verification error; content mismatch\n", - progname); + progname); pgm->err_led(pgm, ON); avr_free_part(v); - return -1; + return LIBAVRDUDE_GENERAL_FAILURE; } if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: %d bytes of %s%s verified\n", - progname, rc, mem->desc, alias_mem_desc); + int verified = fs.nbytes+fs.ntrailing; + avrdude_message(MSG_INFO, "%s: %d byte%s of %s%s verified\n", + progname, verified, plural(verified), mem->desc, alias_mem_desc); } pgm->vfy_led(pgm, OFF); avr_free_part(v); - } - else { + break; + + default: avrdude_message(MSG_INFO, "%s: invalid update operation (%d) requested\n", - progname, upd->op); - return -1; + progname, upd->op); + return LIBAVRDUDE_GENERAL_FAILURE; } - return 0; + return LIBAVRDUDE_SUCCESS; } - From 42c8169c37923345758664ca3fdc4e229bc112d0 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Tue, 2 Aug 2022 23:53:00 +0100 Subject: [PATCH 2/5] Add ordered list of known memories to avr.c with access functions --- src/avr.c | 39 +++++++++++++++++++++++++++++++++++++++ src/config_gram.y | 1 + src/developer_opts.c | 43 +++++++------------------------------------ src/libavrdude.h | 5 +++++ 4 files changed, 52 insertions(+), 36 deletions(-) diff --git a/src/avr.c b/src/avr.c index 7f91e9a1..eb077b32 100644 --- a/src/avr.c +++ b/src/avr.c @@ -1220,7 +1220,46 @@ int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles) } return 0; +} + + +// Typical order in which memories show in avrdude.conf, runtime adds unknown ones (if any) +const char *avr_mem_order[100] = { + "eeprom", "flash", "application", "apptable", + "boot", "lfuse", "hfuse", "efuse", + "fuse", "fuse0", "wdtcfg", "fuse1", + "bodcfg", "fuse2", "osccfg", "fuse3", + "fuse4", "tcd0cfg", "fuse5", "syscfg0", + "fuse6", "syscfg1", "fuse7", "append", + "codesize", "fuse8", "fuse9", "bootend", + "bootsize", "fuses", "lock", "lockbits", + "tempsense", "signature", "prodsig", "sernum", + "calibration", "osccal16", "osccal20", "osc16err", + "osc20err", "usersig", "userrow", "data", +}; + +void avr_add_mem_order(const char *str) { + for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++) { + if(avr_mem_order[i] && !strcmp(avr_mem_order[i], str)) + return; + if(!avr_mem_order[i]) { + avr_mem_order[i] = strdup(str); + return; + } } + avrdude_message(MSG_INFO, + "%s: avr_mem_order[] under-dimensioned in avr.c; increase and recompile\n", + progname); + exit(1); +} + +int avr_known_mem(const char *str) { + for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++) + if(avr_mem_order[i] && !strcmp(avr_mem_order[i], str)) + return 1; + return 0; +} + int avr_chip_erase(PROGRAMMER * pgm, AVRPART * p) { diff --git a/src/config_gram.y b/src/config_gram.y index 68718dd1..ce11b924 100644 --- a/src/config_gram.y +++ b/src/config_gram.y @@ -1320,6 +1320,7 @@ part_parm : mem->desc[AVR_MEMDESCLEN-1] = 0; ladd(current_part->mem, mem); } + avr_add_mem_order($2->value.string); current_mem = mem; free_token($2); } diff --git a/src/developer_opts.c b/src/developer_opts.c index f4a55478..b82c1805 100644 --- a/src/developer_opts.c +++ b/src/developer_opts.c @@ -280,35 +280,6 @@ static void dev_stack_out(bool tsv, AVRPART *p, const char *name, unsigned char } -// order in which memories are processed, runtime adds unknown ones (but there shouldn't be any) -static const char *mem_order[100] = { - "eeprom", "flash", "application", "apptable", - "boot", "lfuse", "hfuse", "efuse", - "fuse", "fuse0", "wdtcfg", "fuse1", - "bodcfg", "fuse2", "osccfg", "fuse3", - "fuse4", "tcd0cfg", "fuse5", "syscfg0", - "fuse6", "syscfg1", "fuse7", "append", - "codesize", "fuse8", "fuse9", "bootend", - "bootsize", "fuses", "lock", "lockbits", - "tempsense", "signature", "prodsig", "sernum", - "calibration", "osccal16", "osccal20", "osc16err", - "osc20err", "usersig", "userrow", "data", -}; - -static void add_mem_order(const char *str) { - for(size_t i=0; i < sizeof mem_order/sizeof *mem_order; i++) { - if(mem_order[i] && !strcmp(mem_order[i], str)) - return; - if(!mem_order[i]) { - mem_order[i] = strdup(str); - return; - } - } - dev_info("%s: mem_order[] under-dimensioned in developer_opts.c; increase and recompile\n", progname); - exit(1); -} - - static int intcmp(int a, int b) { return a-b; } @@ -410,8 +381,8 @@ static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) { // fill in all memories we got in defined order di = 0; - for(size_t mi=0; mi < sizeof mem_order/sizeof *mem_order && mem_order[mi]; mi++) { - m = p->mem? avr_locate_mem(p, mem_order[mi]): NULL; + for(size_t mi=0; mi < sizeof avr_mem_order/sizeof *avr_mem_order && avr_mem_order[mi]; mi++) { + m = p->mem? avr_locate_mem(p, avr_mem_order[mi]): NULL; if(m) { if(di >= sizeof d->mems/sizeof *d->mems) { avrdude_message(MSG_INFO, "%s: ran out of mems[] space, increase size in AVRMEMdeep of developer_opts.c and recompile\n", progname); @@ -566,11 +537,11 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) { if(!base || opcodecmp(p->op[i], base->op[i], i)) dev_part_strct_entry(tsv, ".ptop", p->desc, "part", opcodename(i), opcode2str(p->op[i], i, !tsv)); - for(size_t mi=0; mi < sizeof mem_order/sizeof *mem_order && mem_order[mi]; mi++) { + for(size_t mi=0; mi < sizeof avr_mem_order/sizeof *avr_mem_order && avr_mem_order[mi]; mi++) { AVRMEM *m, *bm; - m = p->mem? avr_locate_mem(p, mem_order[mi]): NULL; - bm = base && base->mem? avr_locate_mem(base, mem_order[mi]): NULL; + m = p->mem? avr_locate_mem(p, avr_mem_order[mi]): NULL; + bm = base && base->mem? avr_locate_mem(base, avr_mem_order[mi]): NULL; if(!m && bm && !tsv) dev_info("\n memory \"%s\" = NULL;\n", bm->desc); @@ -694,12 +665,12 @@ void dev_output_part_defs(char *partdesc) { AVRPART *p = ldata(ln1); if(p->mem) for(LNODEID lnm=lfirst(p->mem); lnm; lnm=lnext(lnm)) - add_mem_order(((AVRMEM *) ldata(lnm))->desc); + avr_add_mem_order(((AVRMEM *) ldata(lnm))->desc); // same for aliased memories (though probably not needed) if(p->mem_alias) for(LNODEID lnm=lfirst(p->mem_alias); lnm; lnm=lnext(lnm)) - add_mem_order(((AVRMEM_ALIAS *) ldata(lnm))->desc); + avr_add_mem_order(((AVRMEM_ALIAS *) ldata(lnm))->desc); } nprinted = dev_nprinted; diff --git a/src/libavrdude.h b/src/libavrdude.h index 95e08d18..cc332ec7 100644 --- a/src/libavrdude.h +++ b/src/libavrdude.h @@ -783,6 +783,7 @@ void sort_programmers(LISTID programmers); typedef void (*FP_UpdateProgress)(int percent, double etime, char *hdr); extern struct avrpart parts[]; +extern const char *avr_mem_order[100]; extern FP_UpdateProgress update_progress; @@ -818,6 +819,10 @@ int avr_get_cycle_count(PROGRAMMER * pgm, AVRPART * p, int * cycles); int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles); +void avr_add_mem_order(const char *str); + +int avr_known_mem(const char *str); + #define disable_trailing_ff_removal() avr_mem_hiaddr(NULL) int avr_mem_hiaddr(AVRMEM * mem); From 9604a3ef365b088b2d6b36e04dde6a225b271a56 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Wed, 3 Aug 2022 00:04:14 +0100 Subject: [PATCH 3/5] Check -U option for unknown memories during parsing $ avrdude -qp ATmega2560 -c usbtiny -U flesh:w:blink-mega2560+lext-test.hex:i avrdude: unknown memory type flesh avrdude: error parsing update operation 'flesh:w:blink-mega2560+lext-test.hex:i' --- src/avr.c | 2 +- src/libavrdude.h | 2 +- src/update.c | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/avr.c b/src/avr.c index eb077b32..3a7d4a9e 100644 --- a/src/avr.c +++ b/src/avr.c @@ -1253,7 +1253,7 @@ void avr_add_mem_order(const char *str) { exit(1); } -int avr_known_mem(const char *str) { +int avr_mem_is_known(const char *str) { for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++) if(avr_mem_order[i] && !strcmp(avr_mem_order[i], str)) return 1; diff --git a/src/libavrdude.h b/src/libavrdude.h index cc332ec7..f8bdf682 100644 --- a/src/libavrdude.h +++ b/src/libavrdude.h @@ -821,7 +821,7 @@ int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles); void avr_add_mem_order(const char *str); -int avr_known_mem(const char *str); +int avr_mem_is_known(const char *str); #define disable_trailing_ff_removal() avr_mem_hiaddr(NULL) int avr_mem_hiaddr(AVRMEM * mem); diff --git a/src/update.c b/src/update.c index ffddfa16..ee205bbc 100644 --- a/src/update.c +++ b/src/update.c @@ -62,6 +62,12 @@ UPDATE * parse_op(char * s) return upd; } + if (!avr_mem_is_known(buf)) { + avrdude_message(MSG_INFO, "%s: unknown memory type %s\n", progname, buf); + free(upd); + return NULL; + } + upd->memtype = (char *)malloc(strlen(buf)+1); if (upd->memtype == NULL) { avrdude_message(MSG_INFO, "%s: out of memory\n", progname); From 648f3319a92ab0fdd004cb88bbb84dc6c04bf280 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Wed, 3 Aug 2022 00:23:15 +0100 Subject: [PATCH 4/5] Ignore target memories not present in part $ avrdude -qp m8 -c ... -U efuse:w:0xff:m && echo OK avrdude: AVR device initialized and ready to accept instructions avrdude: skipping -U efuse:... as memory not defined for part ATmega8 avrdude done. Thank you. OK --- src/main.c | 2 +- src/update.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.c b/src/main.c index f4095d5f..2984203a 100644 --- a/src/main.c +++ b/src/main.c @@ -1243,7 +1243,7 @@ int main(int argc, char * argv []) for (ln=lfirst(updates); ln; ln=lnext(ln)) { upd = ldata(ln); rc = do_op(pgm, p, upd, uflags); - if (rc) { + if (rc && rc != LIBAVRDUDE_SOFTFAIL) { exitrc = 1; break; } diff --git a/src/update.c b/src/update.c index ee205bbc..78703e89 100644 --- a/src/update.c +++ b/src/update.c @@ -335,9 +335,9 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f mem = avr_locate_mem(p, upd->memtype); if (mem == NULL) { - avrdude_message(MSG_INFO, "%s memory type not defined for part %s\n", - upd->memtype, p->desc); - return LIBAVRDUDE_GENERAL_FAILURE; + avrdude_message(MSG_INFO, "%s: skipping -U %s:... as memory not defined for part %s\n", + progname, upd->memtype, p->desc); + return LIBAVRDUDE_SOFTFAIL; } AVRMEM_ALIAS * alias_mem = avr_find_memalias(p, mem); From 3412196cd904dc02bc04678758c8f2e7c943c09f Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Thu, 4 Aug 2022 00:14:19 +0100 Subject: [PATCH 5/5] Weaken -U memory type check and move after config file parsing in main.c The check for typos in -U memory names against a list of known memory names now happens after the config files have been read, so newly declared memory names can be considered. This commit also weakens the check against existence of a known memory: it is now sufficent for a name to pass when it could be the initial string of any known memory of any part. Any -U memory that cannot possibly be matched up with a known memory is considered a typo and leads to an exit before the programmer is opened. This to protect users from typos that leave a device partially programmed. When every -U memory name might be matching one of the known memories, the programming is attempted. If the part to be programmed turns out not to have a particular -U memory, AVRDUDE warns the user and skips this -U update. This to support unifying interfaces that call AVRDUDE with potentially more memories than the actual part has (eg, efuse on ATmega8). --- src/avr.c | 15 ++++++++++++--- src/libavrdude.h | 1 + src/main.c | 7 +++++++ src/update.c | 6 ------ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/avr.c b/src/avr.c index 3a7d4a9e..eea0ad8a 100644 --- a/src/avr.c +++ b/src/avr.c @@ -1254,9 +1254,18 @@ void avr_add_mem_order(const char *str) { } int avr_mem_is_known(const char *str) { - for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++) - if(avr_mem_order[i] && !strcmp(avr_mem_order[i], str)) - return 1; + if(str && *str) + for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++) + if(avr_mem_order[i] && !strcmp(avr_mem_order[i], str)) + return 1; + return 0; +} + +int avr_mem_might_be_known(const char *str) { + if(str && *str) + for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++) + if(avr_mem_order[i] && !strncmp(avr_mem_order[i], str, strlen(str))) + return 1; return 0; } diff --git a/src/libavrdude.h b/src/libavrdude.h index f8bdf682..a30c1402 100644 --- a/src/libavrdude.h +++ b/src/libavrdude.h @@ -822,6 +822,7 @@ int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles); void avr_add_mem_order(const char *str); int avr_mem_is_known(const char *str); +int avr_mem_might_be_known(const char *str); #define disable_trailing_ff_removal() avr_mem_hiaddr(NULL) int avr_mem_hiaddr(AVRMEM * mem); diff --git a/src/main.c b/src/main.c index 2984203a..9f72847f 100644 --- a/src/main.c +++ b/src/main.c @@ -751,6 +751,7 @@ int main(int argc, char * argv []) bitclock = default_bitclock; } + avrdude_message(MSG_NOTICE, "\n"); // developer option -p /[*codws] prints various aspects of part descriptions and exits @@ -918,6 +919,12 @@ int main(int argc, char * argv []) exit(1); } } + + if (!avr_mem_might_be_known(upd->memtype)) { + avrdude_message(MSG_INFO, "%s: unknown memory type %s\n", progname, upd->memtype); + exit(1); + } + // TODO: check whether filename other than "-" is readable/writable } /* diff --git a/src/update.c b/src/update.c index 78703e89..025bfb6b 100644 --- a/src/update.c +++ b/src/update.c @@ -62,12 +62,6 @@ UPDATE * parse_op(char * s) return upd; } - if (!avr_mem_is_known(buf)) { - avrdude_message(MSG_INFO, "%s: unknown memory type %s\n", progname, buf); - free(upd); - return NULL; - } - upd->memtype = (char *)malloc(strlen(buf)+1); if (upd->memtype == NULL) { avrdude_message(MSG_INFO, "%s: out of memory\n", progname);