mirror of
https://github.com/avrdudes/avrdude.git
synced 2026-09-21 08:36:29 +03:00
Merge pull request #2197 from stefanrueger/dryrun-holes
Provide `-x holes` for dryrun programmers
This commit is contained in:
@@ -201,6 +201,7 @@ set(SOURCES
|
||||
dfu.h
|
||||
dryrun.c
|
||||
dryrun.h
|
||||
dryrun_part.c
|
||||
dryrun_private.h
|
||||
fileio.c
|
||||
flip1.c
|
||||
|
||||
@@ -122,6 +122,7 @@ libavrdude_la_SOURCES = \
|
||||
dfu.h \
|
||||
dryrun.c \
|
||||
dryrun.h \
|
||||
dryrun_part.c \
|
||||
dryrun_private.h \
|
||||
fileio.c \
|
||||
flip1.c \
|
||||
|
||||
@@ -1607,24 +1607,53 @@ versions of the bootloader.
|
||||
.It Ar dryboot
|
||||
Dryrun emulates external programming without the need to connect a
|
||||
programmer or a part while dryboot emulates bootloader programming without
|
||||
the need to connect the target part. They accept the following parameters:
|
||||
the need to connect the target part.
|
||||
.Pp
|
||||
Lock and fuse memories are initialised with with factory values as far as
|
||||
known, 0xff otherwise. The signature memory is set from the configuration
|
||||
file; the calibration memory is filled with U (for uncalibrated); osc16err
|
||||
with e and osc20err with E (for error); osccal16 with o and osccal20 with
|
||||
O; sib with S; tempsense with T; sernum with the downward letter sequence
|
||||
UTSRQP...; and the volatile io memory with reset values if known, 0x00
|
||||
otherwise.
|
||||
.Pp
|
||||
If either the init or random parameters are set, then the flash memory is
|
||||
randomly configured in terms of bootloader sections, code and application
|
||||
data sections, and the fuses updated accordingly. In either case, flash
|
||||
(including ATxmega submemories of application, apptable and boot), eeprom,
|
||||
and all other existing memories such as prodsig/sigrow, userrow/usersig
|
||||
and bootrow are updated with random data. Flash is always initialised with
|
||||
benign code, that is its opcodes will not access I/O memories, SRAM or
|
||||
flash.
|
||||
.Pp
|
||||
If none of init or random parameters are set, these memories are
|
||||
initialised with 0xff. Note that init and random are not meant to be both
|
||||
set at the same time.
|
||||
.Pp
|
||||
The dryrun and dryboot programmers accept the following parameters:
|
||||
.Bl -tag -offset indent -width indent
|
||||
.It Ar init
|
||||
Initialise memories with human-readable patterns. Flash memory will be
|
||||
randomly configured with respect to bootloader, data and code length.
|
||||
Patterns can best be seen with fixed-width font and the :I format
|
||||
by inspecting the generated hex file or by using, eg, -U flash:r:-:I.
|
||||
Patterns in flash memory are executable and represent benign AVR code, ie,
|
||||
no I/O memory access. Choose a fixed seed for reproducible results.
|
||||
The patterns that are used for initialising memories as detailed above are
|
||||
human-readable. These patterns can best be seen with a fixed-width font
|
||||
and the :I format by inspecting the generated hex file or by using, eg, -U
|
||||
flash:r:-:I to dump the patterns on screen. eeprom, userrow/usersig and
|
||||
bootrow memories are filled with pangrams such as The quick brown fox
|
||||
jumps over the lazy dog. Choose a fixed seed for reproducible results.
|
||||
.It Ar init=<n>
|
||||
Shortcut for -x init -x seed=<n> (see below)
|
||||
.It Ar random
|
||||
Initialise memories with random code and values. Flash memory will be
|
||||
randomly configured with respect to bootloader, data and code length.
|
||||
Random code in flash will be benign, that is, not accessing I/O memories,
|
||||
SRAM or flash. Choose a fixed seed for reproducible results.
|
||||
Initialise flash with random opcodes and, if applicable, random
|
||||
application table data. The sernum memory, if it exists, will be
|
||||
initialised with a random upper-letter sequence. Other memories are
|
||||
initialised with a random sequence of at-signs and spaces. Choose a fixed
|
||||
seed for reproducible results.
|
||||
.It Ar random=<n>
|
||||
Shortcut for -x random -x seed=<n>
|
||||
.It Ar holes
|
||||
Put holes into larger memories, ie, longer sequences of 0xff, and add
|
||||
small islands of code or data. Some of these holes can pose problems for
|
||||
programmers that do not anticipate them. As such these can be used for
|
||||
hardened testing, which is the main purpose of the dryrun programmers.
|
||||
.It Ar seed=<n>
|
||||
Seed random number generator with <n>; the default is time(NULL).
|
||||
Setting this option with a fixed n > 0 will make the random choices
|
||||
|
||||
@@ -1003,6 +1003,9 @@ AVRPART *avr_dup_part(const AVRPART *d) {
|
||||
}
|
||||
|
||||
void avr_free_part(AVRPART *d) {
|
||||
if(d == NULL)
|
||||
return;
|
||||
|
||||
ldestroy_cb(d->mem, (void (*)(void *)) avr_free_mem);
|
||||
d->mem = NULL;
|
||||
ldestroy_cb(d->mem_alias, (void (*)(void *)) avr_free_memalias);
|
||||
|
||||
@@ -1351,32 +1351,70 @@ extended parameters to be specified on the command line.
|
||||
|
||||
Dryrun emulates external programming without the need to connect a
|
||||
programmer or a part while dryboot emulates bootloader programming without
|
||||
the need to connect the target part. They accept the following parameters:
|
||||
the need to connect the target part.
|
||||
|
||||
@code{Lock} and @code{fuse} memories are initialised with with factory
|
||||
values as far as known, @code{0xff} otherwise. The @code{signature} memory
|
||||
is set from the configuration file; the @code{calibration} memory is
|
||||
filled with @code{U} (for uncalibrated); @code{osc16err} with @code{e} and
|
||||
@code{osc20err} with @code{E} (for error); @code{osccal16} with o and
|
||||
@code{osccal20} with @code{O}; @code{sib} with @code{S}; @code{tempsense}
|
||||
with @code{T}; @code{sernum} with the downward letter sequence
|
||||
@code{UTSRQP...}; and the volatile @code{io} memory with reset values if
|
||||
known, @code{0x00} otherwise.
|
||||
|
||||
@cindex benign code
|
||||
If either the @code{init} or @code{random} parameters are set, then the
|
||||
@code{flash} memory is randomly configured in terms of bootloader
|
||||
sections, code and application data sections, and the fuses updated
|
||||
accordingly. In either case, @code{flash} (including ATxmega submemories
|
||||
of @code{application}, @code{apptable} and @code{boot}), @code{eeprom},
|
||||
and all other existing memories such as @code{prodsig}/@code{sigrow},
|
||||
@code{userrow}/@code{usersig} and @code{bootrow} are updated with random
|
||||
data. @code{flash} is always initialised with benign code, that is, its
|
||||
opcodes will not access I/O memories, SRAM or flash.
|
||||
|
||||
If none of @code{init} or @code{random} parameters are set, these memories
|
||||
are initialised with @code{0xff}. Note that @code{init} and @code{random}
|
||||
are not meant to be both set at the same time.
|
||||
|
||||
|
||||
The dryrun and dryboot programmers accept the following parameters:
|
||||
|
||||
@table @code
|
||||
@cindex @code{flash}
|
||||
@item init
|
||||
Initialise memories with human-readable patterns. Flash memory will be
|
||||
randomly configured with respect to bootloader, data and code length.
|
||||
Patterns can best be seen with fixed-width font and the @code{:I} format
|
||||
by inspecting the generated hex file or by using, eg, @code{-U
|
||||
flash:r:-:I}. Patterns in flash memory are executable and represent benign
|
||||
AVR code, ie, no I/O memory access. Choose a fixed seed for reproducible
|
||||
results.
|
||||
|
||||
The patterns that are used for initialising memories as detailed above are
|
||||
human-readable. These patterns can best be seen with a fixed-width font
|
||||
and the @code{:I} format by inspecting the generated hex file or by using,
|
||||
eg, @code{-U flash:r:-:I} to dump the patterns on screen. @code{eeprom},
|
||||
@code{userrow}/@code{usersig} and @code{bootrow} memories are filled with
|
||||
pangrams such as The quick brown fox jumps over the lazy dog. Choose a
|
||||
fixed seed for reproducible results.
|
||||
|
||||
@item init=@var{n}
|
||||
Shortcut for @code{-x init -x seed=@var{n}} (see below)
|
||||
|
||||
@cindex @code{flash}
|
||||
@item random
|
||||
Initialise memories with random code and values. Flash memory will be
|
||||
randomly configured with respect to bootloader, data and code length.
|
||||
Random code in flash will be benign, that is, not accessing I/O memories,
|
||||
SRAM or flash. Choose a fixed seed for reproducible results.
|
||||
Initialise @code{flash} with random opcodes and, if applicable, random
|
||||
application table data. The @code{sernum} memory, if it exists, will be
|
||||
initialised with a random upper-letter sequence. Other memories are
|
||||
initialised with a random sequence of at-signs and spaces. Choose a fixed
|
||||
seed for reproducible results.
|
||||
|
||||
@cindex @code{flash}
|
||||
@item random=@var{n}
|
||||
Shortcut for @code{-x random -x seed=@var{n}}
|
||||
|
||||
@cindex @code{flash}
|
||||
@item holes
|
||||
Put holes into larger memories, ie, longer sequences of @code{0xff}, and
|
||||
add small islands of code or data. Some of these holes can pose problems
|
||||
for programmers that do not anticipate them. As such these can be used for
|
||||
hardened testing, which is the main purpose of the dryrun programmers.
|
||||
|
||||
@item seed=@var{n}
|
||||
Seed random number generator with @var{n}; the default is
|
||||
@code{time(NULL)}. Setting this option with a fixed positive @var{n} will
|
||||
|
||||
550
src/dryrun.c
550
src/dryrun.c
@@ -53,14 +53,11 @@ typedef enum {
|
||||
typedef struct {
|
||||
AVRPART *dp;
|
||||
Dry_prog bl; // Bootloader and, if so, at top/bottom of flash?
|
||||
int bootsize; // Size of boot section (if any)
|
||||
int init; // Initialise memories with something interesting
|
||||
int random; // Random initialisation of memories
|
||||
int seed; // Seed for random number generator
|
||||
// Flash configuration irrespective of -c programming is bootloading or not
|
||||
int appstart, appsize; // Start and size of application section
|
||||
int datastart, datasize; // Start and size of application data section (if any)
|
||||
int bootstart, bootsize; // Start and size of boot section (if any)
|
||||
int initialised; // 1 once the part memories are initialised
|
||||
int holes; // Whether eeprom/flash should have holes
|
||||
struct {
|
||||
int vectornum; // Vector bootloader vector number for jump to application op code
|
||||
int urversion; // Octal byte 076 means v7.6 (minor version number is lowest 3 bit)
|
||||
@@ -360,8 +357,8 @@ static int dryrun_cmd(const PROGRAMMER *pgm, const unsigned char *cmd, unsigned
|
||||
}
|
||||
|
||||
static int dryrun_page_erase(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m, unsigned int addr) {
|
||||
|
||||
pmsg_debug("%s(%s, 0x%04x)\n", __func__, m->desc, addr);
|
||||
|
||||
if(!dry.dp)
|
||||
Return("no dryrun device?");
|
||||
|
||||
@@ -388,535 +385,26 @@ static int dryrun_program_enable(const PROGRAMMER *pgm, const AVRPART *p_unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Randomly set configuration values for bootloading, bootloader size and codesize, if any
|
||||
static void randflashconfig(const PROGRAMMER *pgm, const AVRPART *p, const Avrintel *up,
|
||||
const Configitem *cp, int nc) {
|
||||
|
||||
if(up && is_updi(p)) {
|
||||
int sectorsize = up->bootsize > 0? up->bootsize: 256;
|
||||
int nsectors = up->flashsize/sectorsize;
|
||||
int bootsize = random()%(nsectors > 4? nsectors/4: nsectors);
|
||||
int codesize = !bootsize || random()%3? 0: bootsize + random()%(nsectors - bootsize);
|
||||
|
||||
int size = !!avr_locate_config(cp, nc, "bootsize", str_eq);
|
||||
|
||||
avr_set_config_value(pgm, p, size? "bootsize": "bootend", bootsize);
|
||||
avr_set_config_value(pgm, p, size? "codesize": "append", codesize);
|
||||
} else if(up && up->nboots > 0 && (p->prog_modes & (PM_Classic | PM_PDI))) {
|
||||
avr_set_config_value(pgm, p, "bootrst", random()%2);
|
||||
if(up->nboots == 4)
|
||||
avr_set_config_value(pgm, p, "bootsz", random()%4);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute app, data and boot start/size
|
||||
static int flashlayout(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *flm,
|
||||
const Avrintel *up, const Configitem *cp, int nc) {
|
||||
|
||||
AVRMEM *m;
|
||||
|
||||
if(is_updi(p)) {
|
||||
int nbootsec = 0, ncodesec = 0;
|
||||
|
||||
int size = !!avr_locate_config(cp, nc, "bootsize", str_eq);
|
||||
|
||||
avr_get_config_value(pgm, p, size? "bootsize": "bootend", &nbootsec);
|
||||
avr_get_config_value(pgm, p, size? "codesize": "append", &ncodesec);
|
||||
if(nbootsec == 0 || (ncodesec && ncodesec <= nbootsec)) { // Treat boot section for code
|
||||
dry.bootstart = 0, dry.bootsize = 0;
|
||||
dry.appstart = 0, dry.appsize = nbootsec? nbootsec*up->bootsize: up->flashsize;
|
||||
} else { // Distinct boot and application section
|
||||
dry.bootstart = 0, dry.bootsize = nbootsec*up->bootsize;
|
||||
dry.appstart = dry.bootsize;
|
||||
dry.appsize = ncodesec? (ncodesec - nbootsec)*up->bootsize: up->flashsize - dry.appstart;
|
||||
}
|
||||
dry.datasize = up->flashsize - dry.bootsize - dry.appsize; // Remainder is apptable
|
||||
dry.datastart = dry.datasize? dry.bootsize + dry.appsize: 0;
|
||||
} else if(p->prog_modes & (PM_Classic | PM_PDI)) {
|
||||
dry.bootstart = 0, dry.bootsize = 0;
|
||||
if(up->nboots) {
|
||||
int bootrst = 1;
|
||||
|
||||
avr_get_config_value(pgm, p, "bootrst", &bootrst);
|
||||
if(bootrst == 0) { // Jump to bootloader on reset
|
||||
if(is_pdi(p) && (m = avr_locate_boot(p)) && m->size > 0) {
|
||||
dry.bootstart = m->offset - flm->offset;
|
||||
dry.bootsize = m->size;
|
||||
} else if(is_classic(p)) {
|
||||
if(up->nboots == 4) {
|
||||
int bootsz = 0;
|
||||
|
||||
avr_get_config_value(pgm, p, "bootsz", &bootsz);
|
||||
dry.bootsize = (8 >> bootsz)*up->bootsize;
|
||||
} else
|
||||
dry.bootsize = up->bootsize;
|
||||
dry.bootstart = up->flashsize - dry.bootsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
dry.datastart = 0, dry.datasize = 0;
|
||||
if(is_pdi(p) && (m = avr_locate_apptable(p)) && m->size > 0) {
|
||||
dry.datastart = m->offset - flm->offset;
|
||||
dry.datasize = up->flashsize - dry.datastart - dry.bootsize;
|
||||
}
|
||||
dry.appstart = 0, dry.appsize = up->flashsize - dry.datasize - dry.bootsize;
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
if(dry.appsize < 0)
|
||||
Retwarning("negative application size");
|
||||
if(dry.appstart < 0 || dry.appstart + dry.appsize > up->flashsize)
|
||||
Retwarning("application section %s outside flash [0, 0x%04x]",
|
||||
str_ccinterval(dry.appstart, dry.appstart + dry.appsize - 1), up->flashsize - 1);
|
||||
|
||||
if(dry.datasize < 0)
|
||||
Retwarning("negative apptable size");
|
||||
if(dry.datastart < 0 || dry.datastart + dry.datasize > up->flashsize)
|
||||
Retwarning("apptable section %s outside flash [0, 0x%04x]",
|
||||
str_ccinterval(dry.datastart, dry.datastart + dry.datasize - 1), up->flashsize - 1);
|
||||
|
||||
if(dry.bootsize < 0)
|
||||
Retwarning("negative boot section size");
|
||||
if(dry.bootstart < 0 || dry.bootstart + dry.bootsize > up->flashsize)
|
||||
Retwarning("boot section %s outside flassh [0, 0x%04x]",
|
||||
str_ccinterval(dry.bootstart, dry.bootstart + dry.bootsize - 1), up->flashsize - 1);
|
||||
|
||||
if(dry.appsize + dry.datasize + dry.bootsize != up->flashsize)
|
||||
Retwarning("section sizes do not add up (0x%x) to flash size 0x%x",
|
||||
dry.appsize + dry.datasize + dry.bootsize, up->flashsize);
|
||||
|
||||
if(!dry.appsize)
|
||||
Retwarning("no application section");
|
||||
|
||||
if(is_updi(p)) {
|
||||
if(dry.bootsize && dry.appstart != dry.bootsize)
|
||||
Retwarning("application section %s does not touch boot section %s",
|
||||
str_ccinterval(dry.appstart, dry.appstart + dry.appsize - 1),
|
||||
str_ccinterval(dry.bootstart, dry.bootstart + dry.bootsize - 1));
|
||||
if(dry.datasize && dry.datastart != dry.bootsize + dry.appsize)
|
||||
Retwarning("apptable section %s does not touch code section %s",
|
||||
str_ccinterval(dry.datastart, dry.datastart + dry.appsize - 1),
|
||||
str_ccinterval(0, dry.bootsize + dry.appsize - 1));
|
||||
} else {
|
||||
if(dry.datasize && dry.datastart != dry.appsize && dry.appstart != 0)
|
||||
Retwarning("apptable section %s does not touch application section %s",
|
||||
str_ccinterval(dry.datastart, dry.datastart + dry.appsize - 1),
|
||||
str_ccinterval(dry.appstart, dry.appstart + dry.appsize - 1));
|
||||
if(dry.datasize && dry.bootsize && dry.bootstart != dry.appsize + dry.datasize)
|
||||
Retwarning("apptable section %s does not touch boot section %s",
|
||||
str_ccinterval(dry.datastart, dry.datastart + dry.appsize - 1),
|
||||
str_ccinterval(dry.bootstart, dry.bootstart + dry.bootsize - 1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write a vector table to flash addr and return number of bytes written
|
||||
static int putvectortable(const AVRPART *p, const AVRMEM *flm, int addr, int round32) {
|
||||
int vecsz = flm->size <= 8192? 2: 4, ret = p->n_interrupts*vecsz;
|
||||
int app = (ret + vecsz - 2)/2; // Distance to application in words
|
||||
|
||||
for(int i = 0; i < ret; i += vecsz) { // First store rjmps to after table
|
||||
flm->buf[addr + i] = app;
|
||||
flm->buf[addr + i + 1] = 0xc0 + (app >> 8); // rjmp app, rjmp app, ...
|
||||
if(vecsz == 4) // Put nop behind rjmp
|
||||
flm->buf[addr + i + 2] = 0, flm->buf[addr + i + 3] = 0;
|
||||
app -= vecsz/2;
|
||||
}
|
||||
for(int i = 0; i < vecsz; i++) // Leave one vector gap
|
||||
flm->buf[addr + ret++] = round32? ' ': 0;
|
||||
|
||||
if(round32) {
|
||||
flm->buf[addr + ret++] = 0xff; // Put endless loop rjmp .-2 as application
|
||||
flm->buf[addr + ret++] = 0xcf;
|
||||
|
||||
// Then round up to multiples of 32
|
||||
while(ret%32)
|
||||
flm->buf[addr + ret++] = ' ';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Human-readable messages in flash shown with, eg, avrdude -c dryrun -p m168 -xinit -Uflash:r:-:I
|
||||
static const int u384[] = {
|
||||
0x00000800, 0x08000800, 0x1c4218ca, 0x08a5284a, 0x1842184e, 0x00000000, 0x00000000, 0x08010000,
|
||||
0x08010000, 0x08c53086, 0x00430942, 0x08653082,
|
||||
}, u512[] = {
|
||||
|
||||
0x20000800, 0x20000800, 0xf71c7b51, 0x28a288d1, 0x28a28851, 0x28a28859, 0xc71c7856, 0x00000000,
|
||||
0x80020000, 0x80020000, 0x8f22f1cd, 0x80920a23, 0x870e0a21, 0x08120a21, 0x87a2f1c1, 0x00000000,
|
||||
}, bdata[] = {
|
||||
|
||||
0x00000000, 0x00000001, 0x00000001, 0x08000001, 0x08000001, 0xfe381c1d, 0x08442223, 0x08824121,
|
||||
0x08824121, 0x08824121, 0x08442223, 0xf0381c1d, 0x00000000, 0x00000000, 0x00400000, 0x00400000,
|
||||
0x00400000, 0x00400000, 0x785c0e3c, 0x88621102, 0x84422081, 0xfc422081, 0x04422081, 0x04621102,
|
||||
0xf85c0e3c, 0x00000000, 0x00000000, 0x00000000,
|
||||
}, adata[] = {
|
||||
|
||||
0x00000020, 0x00000020, 0x00040020, 0x00040020, 0x3c7f1e2e, 0x40042031, 0x40042021, 0x7c043e21,
|
||||
0x42042121, 0x42042131, 0xfc787e2e, 0x00000000, 0x00000000, 0x00000000,
|
||||
}, rocks[] = {
|
||||
|
||||
0x00000004, 0x0000003c, 0x000000fc, 0x000007fc, 0x00001ffc, 0x0000ffe0, 0x0003ff00, 0x001ffc00,
|
||||
0x007fc000, 0x03fe0000, 0x07f00000, 0x07800000, 0x07e00000, 0x07fc0000, 0x03ff0000, 0x007fe000,
|
||||
0x001ffc00, 0x0003ff00, 0x0000ffe0, 0x00001ffc, 0x000007fc, 0x000000fc, 0x0000003c, 0x00000004,
|
||||
0x04000000, 0x07800000, 0x07e00000, 0x07fc0000, 0x07ff0000, 0x00ffe000, 0x003ff800, 0x0007ff00,
|
||||
0x0001ffc0, 0x00003ff8, 0x00000ffc, 0x000001fc, 0x0000007c, 0x000003fc, 0x00001ff8, 0x0000ffc0,
|
||||
0x0003ff00, 0x001ff800, 0x00ffe000, 0x03ff0000, 0x07fc0000, 0x07e00000, 0x07800000, 0x04000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x07fffffc, 0x07fffffc, 0x07fffffc, 0x07007000, 0x07007000,
|
||||
0x07007c00, 0x0700fe00, 0x0700ff00, 0x0781ffc0, 0x07c3cfe0, 0x03ffc3f0, 0x03ff81fc, 0x01ff00fc,
|
||||
0x007c003c, 0x0000001c, 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x07fffffc, 0x07fffffc,
|
||||
0x07fffffc, 0x0700001c, 0x0700001c, 0x0700001c, 0x0700003c, 0x03800038, 0x03c00078, 0x03e000f0,
|
||||
0x01f803f0, 0x00ffffe0, 0x003fff00, 0x0007f800, 0x00000000, 0x00000000, 0x00000000, 0x07ffff00,
|
||||
0x07ffffc0, 0x07fffff0, 0x000000f8, 0x00000038, 0x0000003c, 0x0000001c, 0x0000003c, 0x00000078,
|
||||
0x000000f8, 0x07fffff0, 0x07ffffc0, 0x07ffff00, 0x00000000, 0x00000000, 0x00000000, 0x07fffffc,
|
||||
0x07fffffc, 0x07fffffc, 0x0700001c, 0x0700001c, 0x0700001c, 0x0700003c, 0x03800038, 0x03c00078,
|
||||
0x03e000f0, 0x01f803f0, 0x00ffffe0, 0x003fff00, 0x0007f800, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x07fffffc, 0x07fffffc, 0x07fffffc, 0x0700e01c, 0x0700e01c, 0x0700e01c, 0x0700e01c, 0x0700e01c,
|
||||
0x0700e01c, 0x0700e01c, 0x0700001c, 0x0700001c, 0x0000001c, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x007ffffc, 0x007ffffc, 0x007ffffc, 0x00070000,
|
||||
0x000e0000, 0x001c0000, 0x00380000, 0x00380000, 0x00700000, 0x00700000, 0x007f0000, 0x007f0000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x0000fe00, 0x0007ffc0, 0x000fffe0, 0x003f01f8, 0x003c0078,
|
||||
0x0078003c, 0x0070001c, 0x0070001c, 0x0078003c, 0x003c0078, 0x003f01f8, 0x000fffe0, 0x0007ffc0,
|
||||
0x0000fe00, 0x00000000, 0x00000000, 0x00000000, 0x0000fe00, 0x0003ffc0, 0x000fffe0, 0x001f01f0,
|
||||
0x003c0078, 0x0078003c, 0x0070001c, 0x0070001c, 0x0070001c, 0x0078003c, 0x00380038, 0x00380038,
|
||||
0x00000000, 0x00000000, 0x00000000, 0xfffffffc, 0xfffffffc, 0xfffffffc, 0x00003000, 0x00007800,
|
||||
0x0000fc00, 0x0003fe00, 0x0007ff00, 0x000fcf80, 0x001f87c0, 0x007f03f0, 0x007e01f8, 0x007800fc,
|
||||
0x0070007c, 0x0060003c, 0x0040001c, 0x0000000c, 0x00000004, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000078, 0x000f8038, 0x001fc038, 0x003fe03c, 0x003fe01c, 0x0038f01c, 0x0078f01c, 0x0070701c,
|
||||
0x0070381c, 0x00703c1c, 0x00703c3c, 0x00701e78, 0x00781ff8, 0x00380fe0, 0x000003c0, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x7ffffc3c, 0x7ffffc3c, 0x7ffffc3c, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
const struct {
|
||||
const int *bits, n32;
|
||||
} banner[] = {
|
||||
{u384, sizeof u384/sizeof *u384},
|
||||
{u512, sizeof u512/sizeof *u512},
|
||||
{bdata, sizeof bdata/sizeof *bdata},
|
||||
{adata, sizeof adata/sizeof *adata},
|
||||
{rocks, sizeof rocks/sizeof *rocks},
|
||||
{rocks, sizeof rocks/sizeof *rocks}, // Sic, dummy entry for RND
|
||||
};
|
||||
|
||||
enum { U384, U512, BDATA, ADATA, ROCKS, RND };
|
||||
|
||||
/*
|
||||
* Given a bit stream, put a sequence of '@' or ' ' into flash; note they are
|
||||
* all benign opcodes that do not touch memory or the I/O area:
|
||||
* " ": and r2, r0
|
||||
* "@ ": and r4, r0
|
||||
* " @": sbci r18, 0
|
||||
* "@@": sbci r20, 0
|
||||
*/
|
||||
static void putbanner(const AVRMEM *flm, int addr, int n, int bi) {
|
||||
const int *bp = banner[bi].bits, len = n/10 + random()%(9*n/10);
|
||||
|
||||
for(int i = 0; i < n;) {
|
||||
int scan = bi == RND? random(): *bp;
|
||||
|
||||
for(int j = 0; j < 32; j++) {
|
||||
flm->buf[addr++] = scan & 1? '@': ' ';
|
||||
scan >>= 1;
|
||||
if(++i == n)
|
||||
break;
|
||||
}
|
||||
if(++bp == banner[bi].bits + banner[bi].n32) {
|
||||
bp = banner[bi].bits;
|
||||
if(i > len) // Stop repeating banner after some threshold
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put single 16-bit opcode into memory
|
||||
static void putop16(unsigned char *addr, int op) {
|
||||
addr[0] = op, addr[1] = op >> 8;
|
||||
}
|
||||
|
||||
// Put n/2 random benign opcodes compatible with part into memory at addr
|
||||
static void putcode(const AVRPART *p, const AVRMEM *flm, int addr, int n) {
|
||||
int i, op, inrange, pc, end = addr + n/2*2, avrlevel = avr_get_archlevel(p);
|
||||
|
||||
for(i = 0; i < n/2; i++) {
|
||||
do {
|
||||
inrange = 0;
|
||||
// Last opcode is a long backward jump; the others are random
|
||||
op = i == n/2 - 1? dist2rjmp(-2*(i < 2048? i: 2047)): random() & 0xffff;
|
||||
if(op16_is_benign(op, avrlevel))
|
||||
inrange = (pc = op16_target(addr + 2*i, op)) >= addr && pc < end;
|
||||
} while(!inrange);
|
||||
putop16(flm->buf + addr + 2*i, op);
|
||||
}
|
||||
}
|
||||
|
||||
// Write valid opcodes to flash (banners for -xinit, random code for -xrandom)
|
||||
static void putflash(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *flm, int addr, int n, int bi) {
|
||||
unsigned char *top = flm->buf + addr + n - 4;
|
||||
|
||||
if(dry.random) {
|
||||
switch(bi) {
|
||||
case U384:
|
||||
case U512:
|
||||
case BDATA: // Bootloader stuff, reduce code length a little
|
||||
n -= random()%(n/8);
|
||||
break;
|
||||
case ADATA:
|
||||
case ROCKS: // Set random code length in [n/4, n]
|
||||
n -= random()%(3*n/4);
|
||||
}
|
||||
if(bi != ADATA) {
|
||||
putcode(p, flm, addr, n);
|
||||
goto seal;
|
||||
}
|
||||
bi = RND; // Make apptable data random @/space sequences
|
||||
}
|
||||
putbanner(flm, addr, n, bi);
|
||||
|
||||
seal: // Put 1-2 endless loops in top memory section
|
||||
if(*top == 0xff)
|
||||
putop16(top, 0xcfff);
|
||||
putop16(top + 2, 0xcfff);
|
||||
}
|
||||
|
||||
// Initialise a user writable memory other than flash or fuses
|
||||
static void putother(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m, const char *str) {
|
||||
const char *name = avr_mem_name(p, m);
|
||||
int len = strlen(str);
|
||||
|
||||
if(len > m->size)
|
||||
len = m->size;
|
||||
if(len <= 0)
|
||||
return;
|
||||
|
||||
memset(m->buf, 0xff, m->size);
|
||||
|
||||
if(dry.random)
|
||||
putbanner(m, 0, m->size, RND);
|
||||
else
|
||||
for(int i = 0; i < m->size/3; i += len)
|
||||
if(m->size - i > len)
|
||||
memcpy(m->buf + i, str, len);
|
||||
|
||||
if((len = strlen(name)) > m->size)
|
||||
len = m->size;
|
||||
memcpy(m->buf + m->size - len, name, len);
|
||||
if(len < m->size)
|
||||
m->buf[m->size - len - 1] = ' ';
|
||||
}
|
||||
|
||||
// Copy chunk in one flash memory to other overlapping flash memories (think XMEGA)
|
||||
static void sharedflash(const PROGRAMMER *pgm, const AVRMEM *fm, unsigned addr, int chunk) {
|
||||
for(LNODEID ln = lfirst(dry.dp->mem); ln; ln = lnext(ln)) {
|
||||
AVRMEM *m = ldata(ln);
|
||||
|
||||
if(mem_is_in_flash(m) && fm != m) { // Overlapping region?
|
||||
unsigned int cpaddr = addr + fm->offset - m->offset;
|
||||
|
||||
if(cpaddr < (unsigned int) m->size && cpaddr + chunk <= (unsigned int) m->size)
|
||||
memmove(m->buf + cpaddr, fm->buf + addr, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dryrun_enable(PROGRAMMER *pgm, const AVRPART *p) {
|
||||
pmsg_debug("%s()\n", __func__);
|
||||
AVRMEM *m;
|
||||
|
||||
if(dry.dp) // Already configured
|
||||
return;
|
||||
|
||||
unsigned char inifuses[16]; // For fuses: made up from fuse0, fuse1, ...
|
||||
AVRMEM *m, *fusesm = NULL, *prodsigm = NULL, *calm;
|
||||
AVRPART *q = dry.dp = avr_dup_part(p); // Allocate dryrun part and abbreviate with q
|
||||
dry.dp = dryrun_part(p->id, &dry.bootsize, dry.init, dry.random, dry.holes, dry.seed);
|
||||
|
||||
// Initialise urboot descriptor so that all flash is programmable and there is no bootloader
|
||||
if((m = avr_locate_flash(p)))
|
||||
ur.pfend = m->size-1;
|
||||
|
||||
memset(inifuses, 0xff, sizeof inifuses);
|
||||
srandom(dry.seed? dry.seed: time(NULL));
|
||||
|
||||
// Initialise the device with factory setting and erase flash/EEPROM to 0xff
|
||||
for(LNODEID ln = lfirst(q->mem); ln; ln = lnext(ln)) {
|
||||
m = ldata(ln);
|
||||
if(mem_is_in_flash(m) || mem_is_eeprom(m)) {
|
||||
memset(m->buf, 0xff, m->size);
|
||||
} else if(mem_is_fuses(m)) {
|
||||
fusesm = m;
|
||||
} else if(mem_is_a_fuse(m) || mem_is_lock(m)) {
|
||||
// Lock, eg, can have 4 bytes: still allow initialisation from initval
|
||||
if(m->initval != -1 && m->size >= 1 && m->size <= (int) sizeof(m->initval)) {
|
||||
for(int i = 0; i < m->size; i++)
|
||||
m->buf[i] = m->initval >> 8*i;
|
||||
if(mem_is_a_fuse(m)) {
|
||||
int fno = mem_fuse_offset(m);
|
||||
|
||||
for(int i = 0; i < m->size && fno + i < (int) sizeof inifuses; i++) // pdicfg has 2 bytes
|
||||
inifuses[fno + i] = m->initval >> 8*i;
|
||||
}
|
||||
} else {
|
||||
memset(m->buf, 0xff, m->size);
|
||||
}
|
||||
} else if(mem_is_signature(m) && (int) sizeof(q->signature) == m->size) {
|
||||
memcpy(m->buf, q->signature, m->size);
|
||||
} else if(mem_is_calibration(m)) {
|
||||
memset(m->buf, 'U', m->size); // 'U' for uncalibrated or unknown :)
|
||||
} else if(mem_is_osc16err(m)) {
|
||||
memset(m->buf, 'e', m->size);
|
||||
} else if(mem_is_osc20err(m)) {
|
||||
memset(m->buf, 'E', m->size);
|
||||
} else if(mem_is_osccal16(m)) {
|
||||
memset(m->buf, 'o', m->size);
|
||||
} else if(mem_is_osccal20(m)) {
|
||||
memset(m->buf, 'O', m->size);
|
||||
} else if(mem_is_sib(m)) {
|
||||
memset(m->buf, 'S', m->size);
|
||||
} else if(mem_is_tempsense(m)) {
|
||||
memset(m->buf, 'T', m->size); // 'T' for temperature calibration values
|
||||
} else if(mem_is_sernum(m)) {
|
||||
for(int i = 0; i < m->size; i++) // Set serial number UTSRQPONM...
|
||||
m->buf[i] = dry.random? 'A' + random()%26: 'U' - i >= 'A'? 'U' - i: 0xff;
|
||||
} else if(mem_is_sigrow(m) && m->size >= 6) {
|
||||
prodsigm = m;
|
||||
memset(m->buf, 0xff, m->size);
|
||||
// Classic parts: signature at even addresses
|
||||
int n = is_tpi(q)? 1: 2; // ... unless it's the TPI parts t102/t104
|
||||
|
||||
if(is_classic(q))
|
||||
for(int i = 0; i < 3; i++)
|
||||
m->buf[n*i] = q->signature[i];
|
||||
} else if(mem_is_io(m)) { // Initialise reset values (if known)
|
||||
int nr;
|
||||
const Register_file *rf = avr_locate_register_file(q, &nr);
|
||||
|
||||
if(rf)
|
||||
for(int i = 0; i < nr; i++)
|
||||
if(rf[i].initval != -1 && rf[i].size > 0 && rf[i].size < 5)
|
||||
if(rf[i].addr >= 0 && rf[i].addr + rf[i].size <= m->size)
|
||||
for(int k = 0; k < rf[i].size; k++)
|
||||
m->buf[rf[i].addr + k] = rf[i].initval >> 8*k;
|
||||
}
|
||||
}
|
||||
if(prodsigm) {
|
||||
if(q->prog_modes & (PM_UPDI | PM_PDI)) {
|
||||
for(LNODEID ln = lfirst(q->mem); ln; ln = lnext(ln)) {
|
||||
AVRMEM *m = ldata(ln);
|
||||
|
||||
if(m->buf == prodsigm->buf) // Skip prodsig memory
|
||||
continue;
|
||||
int off = m->offset - prodsigm->offset;
|
||||
int cpy = m->size;
|
||||
|
||||
// Submemory of prodsig, eg, signature and tempsense? Copy into prodsig
|
||||
if(off >= 0 && off + cpy <= prodsigm->size)
|
||||
memcpy(prodsigm->buf + off, m->buf, cpy);
|
||||
}
|
||||
}
|
||||
if(is_classic(q) && (calm = avr_locate_calibration(q))) {
|
||||
// Calibration bytes of classic parts are interspersed with signature
|
||||
int n, tpi = is_tpi(q); // ... unless it's the TPI parts t102/t104
|
||||
|
||||
for(int i = 0; i < calm->size; i++) {
|
||||
if((n = tpi? 3 + i: 2*i + 1) < prodsigm->size)
|
||||
prodsigm->buf[n] = 'U';
|
||||
}
|
||||
}
|
||||
if(is_classic(q) && (m = avr_locate_sernum(q))) { // m324pb/m328pb, t102/t104
|
||||
int off = m->offset - prodsigm->offset;
|
||||
int cpy = m->size;
|
||||
|
||||
if(off >= 0 && off + cpy <= prodsigm->size)
|
||||
memcpy(prodsigm->buf + off, m->buf, cpy);
|
||||
}
|
||||
}
|
||||
if(fusesm) {
|
||||
size_t fusz = fusesm->size;
|
||||
|
||||
memcpy(fusesm->buf, inifuses, minm(fusz, sizeof inifuses));
|
||||
}
|
||||
|
||||
// Is the programmer a bootloader?
|
||||
if((m = avr_locate_flash(q)) && m->size >= 1024 && is_spm(pgm))
|
||||
dry.bl = is_updi(q)? DRY_BOTTOM: DRY_TOP;
|
||||
if((m = avr_locate_flash(p)) && m->size >= 1024 && is_spm(pgm))
|
||||
dry.bl = is_updi(p)? DRY_BOTTOM: DRY_TOP;
|
||||
|
||||
// So that dryrun can emulate AVRDUDE page erase
|
||||
if(!is_spm(pgm) && (q->prog_modes & (PM_PDI | PM_UPDI)))
|
||||
if(!is_spm(pgm) && (p->prog_modes & (PM_PDI | PM_UPDI)))
|
||||
pgm->page_erase = dryrun_page_erase;
|
||||
|
||||
if(!dry.random && !dry.init) // OK, no further initialisation needed
|
||||
return;
|
||||
|
||||
int nc, bakverb = verbose;
|
||||
|
||||
verbose = -123; // Silently retrieve uP_table[] entry and config list
|
||||
const Avrintel *up = avr_locate_uP(q);
|
||||
const Configitem *cp = avr_locate_configitems(q, &nc);
|
||||
|
||||
verbose = bakverb;
|
||||
AVRMEM *flm = avr_locate_flash(q);
|
||||
AVRMEM *ee = avr_locate_eeprom(q);
|
||||
int incons = flm && up && (up->flashsize != flm->size || flm->size <= 0 ||
|
||||
(ee && (up->eepromsize != ee->size || ee->size <= 0)) ||
|
||||
up->nboots != q->n_boot_sections || up->nboots < 0 ||
|
||||
up->bootsize != q->boot_section_size || up->bootsize < 0 || memcmp(up->sigs, q->signature, 3)
|
||||
);
|
||||
|
||||
// Ensure can use up and cp with impunity
|
||||
if(!flm || !up || incons || !cp) {
|
||||
pmsg_warning("%s for %s; not initialising memories beyond factory settings\n", !flm? "no flash":
|
||||
!up? "no uP_table[] entry": incons? "inconsistent uP_table[] entry": "no config table", q->desc);
|
||||
return;
|
||||
}
|
||||
|
||||
randflashconfig(pgm, q, up, cp, nc);
|
||||
if(flashlayout(pgm, q, flm, up, cp, nc) < 0)
|
||||
return;
|
||||
|
||||
int vtb = putvectortable(q, flm, dry.appstart, dry.init), urbtsz = 0;
|
||||
|
||||
int urboot = random()%3 && dry.bootsize <= 512 && flm->size >= 1024 &&
|
||||
flm->size >= 4*dry.bootsize && is_classic(q) && is_spm(q);
|
||||
if(urboot) { // Give some classic parts a small bootloader
|
||||
int ps = flm->page_size;
|
||||
|
||||
urbtsz = dry.bootsize? dry.bootsize: flm->size > 32768? 512: flm->size < 16384? 256: 384;
|
||||
urbtsz = (urbtsz + ps - 1)/ps*ps;
|
||||
if(!dry.bootsize && !dry.datasize) {
|
||||
dry.bootsize += urbtsz;
|
||||
dry.appsize -= urbtsz;
|
||||
dry.bootstart = dry.appsize;
|
||||
}
|
||||
int ubaddr = dry.bootstart;
|
||||
|
||||
putflash(pgm, dry.dp, flm, ubaddr, urbtsz, urbtsz == 384? U384: U512);
|
||||
} else if(dry.bootsize) {
|
||||
int btb = 0;
|
||||
|
||||
if(dry.bootsize >= 2048)
|
||||
btb = putvectortable(q, flm, dry.bootstart, dry.init);
|
||||
putflash(pgm, dry.dp, flm, dry.bootstart + btb, dry.bootsize - btb, BDATA);
|
||||
}
|
||||
|
||||
if(dry.datasize)
|
||||
putflash(pgm, dry.dp, flm, dry.datastart, dry.datasize, ADATA);
|
||||
|
||||
putflash(pgm, dry.dp, flm, dry.appstart + vtb, dry.appsize - vtb - urbtsz, ROCKS);
|
||||
|
||||
for(int i = 0; i < flm->size; i += flm->page_size)
|
||||
sharedflash(pgm, flm, i, flm->page_size);
|
||||
|
||||
if((m = avr_locate_eeprom(q)))
|
||||
putother(pgm, q, m, "The quick brown fox jumps over the lazy dog. ");
|
||||
if((m = avr_locate_userrow(q)))
|
||||
putother(pgm, q, m, "The five boxing wizards jump quickly. ");
|
||||
if((m = avr_locate_bootrow(q)))
|
||||
putother(pgm, q, m, "Lorem ipsum dolor sit amet. ");
|
||||
|
||||
dry.initialised = 1;
|
||||
}
|
||||
|
||||
// Initialise the AVR device and prepare it to accept commands
|
||||
@@ -970,6 +458,20 @@ static void *memand(void *dest, const void *src, size_t n) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Copy chunk in one flash memory to other overlapping flash memories (think XMEGA)
|
||||
static void sharedflash(AVRPART *dp, const AVRMEM *fm, unsigned addr, int chunk) {
|
||||
for(LNODEID ln = lfirst(dp->mem); ln; ln = lnext(ln)) {
|
||||
AVRMEM *m = ldata(ln);
|
||||
|
||||
if(mem_is_in_flash(m) && fm != m) { // Overlapping region?
|
||||
unsigned int cpaddr = addr + fm->offset - m->offset;
|
||||
|
||||
if(cpaddr < (unsigned int) m->size && cpaddr + chunk <= (unsigned int) m->size)
|
||||
memcpy(m->buf + cpaddr, fm->buf + addr, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int dryrun_paged_write(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m,
|
||||
unsigned int page_size, unsigned int addr, unsigned int n_bytes) {
|
||||
|
||||
@@ -1026,7 +528,7 @@ static int dryrun_paged_write(const PROGRAMMER *pgm, const AVRPART *p, const AVR
|
||||
|
||||
// Copy chunk to overlapping XMEGA's apptable, application, boot and flash memories
|
||||
if(mchr == 'F')
|
||||
sharedflash(pgm, dmem, addr, chunk);
|
||||
sharedflash(dry.dp, dmem, addr, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1224,7 +726,7 @@ static int dryrun_readonly(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM
|
||||
* return 1;
|
||||
*/
|
||||
|
||||
if(dry.initialised && (mem_is_in_fuses(mem) || mem_is_lock(mem)))
|
||||
if(dry.dp && (mem_is_in_fuses(mem) || mem_is_lock(mem)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -1257,6 +759,10 @@ static int dryrun_parseextparams(const PROGRAMMER *pgm, const LISTID extparms) {
|
||||
dry.random = 1;
|
||||
continue;
|
||||
}
|
||||
if(str_eq(xpara, "holes")) {
|
||||
dry.holes = 1;
|
||||
continue;
|
||||
}
|
||||
if(str_starts(xpara, "seed=") || str_starts(xpara, "init=") || str_starts(xpara, "random=")) {
|
||||
const char *errptr;
|
||||
int seed = str_int(strchr(xpara, '=') + 1, STR_INT32, &errptr);
|
||||
|
||||
734
src/dryrun_part.c
Normal file
734
src/dryrun_part.c
Normal file
@@ -0,0 +1,734 @@
|
||||
/*
|
||||
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2026- Stefan Rueger <stefan.rueger@urclocks.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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* AVRPART *dryrun_part(const char *id, int *bootsizep,
|
||||
* int init, int random, int holes, int seed)
|
||||
*
|
||||
* Returns a duplicate of the part structure that is known from the
|
||||
* configuration file under the given id (eg, m328p). Lock and fuse
|
||||
* memories are initialised with with factory values as far as known, 0xff
|
||||
* otherwise. The signature memory is set from the configuration file; the
|
||||
* calibration memory is filled with U (for uncalibrated); osc16err with e
|
||||
* and osc20err with E (for error); osccal16 with o and osccal20 with O;
|
||||
* sib with S; tempsense with T; sernum with the downward letter sequence
|
||||
* UTSRQP...; and the volatile io memory with reset values if known, 0x00
|
||||
* otherwise.
|
||||
*
|
||||
* If either the init or random parameters are set, then the flash memory
|
||||
* is randomly configured in terms of bootloader sections, code and
|
||||
* application data sections, and the fuses updated accordingly. In either
|
||||
* case, flash (including ATxmega submemories of application, apptable and
|
||||
* boot), eeprom, and all other existing memories such as prodsig/sigrow,
|
||||
* userrow/usersig and bootrow are updated with random data. Flash is
|
||||
* always initialised with benign code, that is its opcodes will not
|
||||
* access I/O memories, SRAM or flash.
|
||||
*
|
||||
* If none of init or random parameters are set, these memories are
|
||||
* initialised with 0xff. Note that init and random are not meant to be
|
||||
* both set at the same time.
|
||||
*
|
||||
* If init is set then, the patterns that are used for initialising
|
||||
* memories as detailed above are human-readable. These patterns can best
|
||||
* be seen with a fixed-width font and the :I format by inspecting the
|
||||
* generated hex file or by using, eg, -U flash:r:-:I to dump the patterns
|
||||
* on screen. eeprom, userrow/usersig and bootrow memories are filled with
|
||||
* pangrams such as The quick brown fox jumps over the lazy dog.
|
||||
*
|
||||
* If random is set flash is initialised with random opcodes and, if
|
||||
* applicable, random application table data. The sernum memory, if it
|
||||
* exists, will be initialised with a random upper-letter sequence. Other
|
||||
* memories are initialised with a random sequence of at-signs and spaces.
|
||||
*
|
||||
* If holes is set then dryrun_parts() puts holes into larger memories,
|
||||
* ie, longer sequences of 0xff, and adds small islands of code or data.
|
||||
* Some of these holes can pose problems for programmers that do not
|
||||
* anticipate them. As such these can be used for hardened testing, which
|
||||
* is the main purpose of the dryrun programmers
|
||||
*
|
||||
* The argument seed, if positive, initialises the seed of the pseudo
|
||||
* random number generator. If seed is zero, time(NULL) is used for
|
||||
* initialisation, ie, subsequent calls of dryrun_part() with the same
|
||||
* arguments differ in the initialisation of the part. Use a positive seed
|
||||
* for reproducible, but random, initialisation.
|
||||
*
|
||||
* If the pointer bootsizep is not NULL, the integer pointed to will be
|
||||
* set to the size of the configured boot section in bytes and 0 if no
|
||||
* boot section was configured.
|
||||
*
|
||||
* The caller is responsible for deallocating the memory associated with
|
||||
* the returned structure pointer by using avr_free_part().
|
||||
*/
|
||||
|
||||
#include <ac_cfg.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "avrdude.h"
|
||||
#include "libavrdude.h"
|
||||
|
||||
#define random() rand() // For platform independence
|
||||
#define srandom(n) srand(n)
|
||||
|
||||
// Use Context data for functions in this file as if they were a global structure me
|
||||
#define me (*mep)
|
||||
|
||||
typedef struct {
|
||||
AVRPART *dp;
|
||||
unsigned char fuses[16]; // Cache of lfuse, hfuse, efuse or, generally, fuses memory
|
||||
int lock; // Cache of lock (unused)
|
||||
int init; // Initialise memories with something interesting
|
||||
int random; // Random initialisation of memories
|
||||
int holes; // Whether eeprom/flash should have holes
|
||||
int seed; // Seed for random number generator
|
||||
// Flash configuration irrespective of -c programming is bootloading or not
|
||||
int appstart, appsize; // Start and size of application section
|
||||
int datastart, datasize; // Start and size of application data section (if any)
|
||||
int bootstart, bootsize; // Start and size of boot section (if any)
|
||||
} Testpart_data;
|
||||
|
||||
|
||||
// Fill in lock/fuse associated with config item and the pointer to Configitem record
|
||||
static int locate_config_c_value(const Testpart_data *mep, const AVRPART *p,
|
||||
const char *cname, const Configitem **cp, int *valp) {
|
||||
|
||||
int nc = 0;
|
||||
const Configitem *cfg = avr_locate_configitems(p, &nc);
|
||||
|
||||
if(!cfg || nc < 1) {
|
||||
pmsg_error("avrintel.c does not hold configuration information for %s\n", p->desc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const Configitem *c = avr_locate_config(cfg, nc, cname, str_contains);
|
||||
|
||||
if(!c) {
|
||||
pmsg_error("%s does not have a unique config item matched by %s\n", p->desc, cname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*cp = c; // @@@ does not work for pdicfg as that spans 2 fuse bytes
|
||||
*valp = str_starts(c->memstr, "lock")? me.lock: me.fuses[c->memoffset];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialise *valuep with configuration value of named configuration bitfield (does not work for pdicfg)
|
||||
static int get_config_value(const Testpart_data *mep, const AVRPART *p, const char *cname, int *valuep) {
|
||||
const Configitem *c;
|
||||
int fusel;
|
||||
|
||||
if(locate_config_c_value(mep, p, cname, &c, &fusel) < 0)
|
||||
return -1;
|
||||
|
||||
if(valuep)
|
||||
*valuep = (fusel & c->mask) >> c->lsh;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set to value in mep's lock/fuses the configuration value of named configuration bitfield
|
||||
static int set_config_value(Testpart_data *mep, const AVRPART *p, const char *cname, int value) {
|
||||
const Configitem *c;
|
||||
int fusel;
|
||||
|
||||
if(locate_config_c_value(mep, p, cname, &c, &fusel) < 0)
|
||||
return -1;
|
||||
|
||||
if((value << c->lsh) & ~c->mask)
|
||||
pmsg_warning("value 0x%02x for %s has bits set outside bitfield mask 0x%02x\n", value, cname, c->mask >> c->lsh);
|
||||
|
||||
int newval = (fusel & ~c->mask) | ((value << c->lsh) & c->mask);
|
||||
|
||||
if(str_starts(c->memstr, "lock"))
|
||||
me.lock = newval;
|
||||
else {
|
||||
me.fuses[c->memoffset] = newval; // Does not work for pdicfg
|
||||
|
||||
// Write to the the corresponding fuse of dp and its fuses memory, if any
|
||||
AVRMEM *m = avr_locate_fuse_by_offset(me.dp, c->memoffset);
|
||||
if(m)
|
||||
m->buf[0] = newval;
|
||||
if((m = avr_locate_fuses(me.dp)))
|
||||
m->buf[c->memoffset] = newval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Randomly set configuration values for bootloading, bootloader size and codesize, if any
|
||||
static void randflashconfig(Testpart_data *mep, const AVRPART *p, const Avrintel *up,
|
||||
const Configitem *cp, int nc) {
|
||||
|
||||
if(up && is_updi(p)) {
|
||||
int sectorsize = up->bootsize > 0? up->bootsize: 256;
|
||||
int nsectors = up->flashsize/sectorsize;
|
||||
int bootsize = random()%(nsectors > 4? nsectors/4: nsectors);
|
||||
int codesize = !bootsize || random()%3? 0: bootsize + random()%(nsectors - bootsize);
|
||||
|
||||
int size = !!avr_locate_config(cp, nc, "bootsize", str_eq);
|
||||
|
||||
set_config_value(mep, p, size? "bootsize": "bootend", bootsize);
|
||||
set_config_value(mep, p, size? "codesize": "append", codesize);
|
||||
} else if(up && up->nboots > 0 && (p->prog_modes & (PM_Classic | PM_PDI))) {
|
||||
set_config_value(mep, p, "bootrst", random()%2);
|
||||
if(up->nboots == 4)
|
||||
set_config_value(mep, p, "bootsz", random()%4);
|
||||
}
|
||||
}
|
||||
|
||||
#define Retwarning(...) do { pmsg_warning(__VA_ARGS__); \
|
||||
msg_warning("; not initialising %s memories\n", p->desc); return -1; } while(0)
|
||||
|
||||
// Compute app, data and boot start/size
|
||||
static int flashlayout(Testpart_data *mep, const AVRPART *p, const AVRMEM *flm,
|
||||
const Avrintel *up, const Configitem *cp, int nc) {
|
||||
|
||||
AVRMEM *m;
|
||||
|
||||
if(is_updi(p)) {
|
||||
int nbootsec = 0, ncodesec = 0;
|
||||
|
||||
int size = !!avr_locate_config(cp, nc, "bootsize", str_eq);
|
||||
|
||||
get_config_value(mep, p, size? "bootsize": "bootend", &nbootsec);
|
||||
get_config_value(mep, p, size? "codesize": "append", &ncodesec);
|
||||
if(nbootsec == 0 || (ncodesec && ncodesec <= nbootsec)) { // Treat boot section for code
|
||||
me.bootstart = 0, me.bootsize = 0;
|
||||
me.appstart = 0, me.appsize = nbootsec? nbootsec*up->bootsize: up->flashsize;
|
||||
} else { // Distinct boot and application section
|
||||
me.bootstart = 0, me.bootsize = nbootsec*up->bootsize;
|
||||
me.appstart = me.bootsize;
|
||||
me.appsize = ncodesec? (ncodesec - nbootsec)*up->bootsize: up->flashsize - me.appstart;
|
||||
}
|
||||
me.datasize = up->flashsize - me.bootsize - me.appsize; // Remainder is apptable
|
||||
me.datastart = me.datasize? me.bootsize + me.appsize: 0;
|
||||
} else if(p->prog_modes & (PM_Classic | PM_PDI)) {
|
||||
me.bootstart = 0, me.bootsize = 0;
|
||||
if(up->nboots) {
|
||||
int bootrst = 1;
|
||||
|
||||
get_config_value(mep, p, "bootrst", &bootrst);
|
||||
if(bootrst == 0) { // Jump to bootloader on reset
|
||||
if(is_pdi(p) && (m = avr_locate_boot(p)) && m->size > 0) {
|
||||
me.bootstart = m->offset - flm->offset;
|
||||
me.bootsize = m->size;
|
||||
} else if(is_classic(p)) {
|
||||
if(up->nboots == 4) {
|
||||
int bootsz = 0;
|
||||
|
||||
get_config_value(mep, p, "bootsz", &bootsz);
|
||||
me.bootsize = (8 >> bootsz)*up->bootsize;
|
||||
} else
|
||||
me.bootsize = up->bootsize;
|
||||
me.bootstart = up->flashsize - me.bootsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
me.datastart = 0, me.datasize = 0;
|
||||
if(is_pdi(p) && (m = avr_locate_apptable(p)) && m->size > 0) {
|
||||
me.datastart = m->offset - flm->offset;
|
||||
me.datasize = up->flashsize - me.datastart - me.bootsize;
|
||||
}
|
||||
me.appstart = 0, me.appsize = up->flashsize - me.datasize - me.bootsize;
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
if(me.appsize < 0)
|
||||
Retwarning("negative application size");
|
||||
if(me.appstart < 0 || me.appstart + me.appsize > up->flashsize)
|
||||
Retwarning("application section %s outside flash [0, 0x%04x]",
|
||||
str_ccinterval(me.appstart, me.appstart + me.appsize - 1), up->flashsize - 1);
|
||||
|
||||
if(me.datasize < 0)
|
||||
Retwarning("negative apptable size");
|
||||
if(me.datastart < 0 || me.datastart + me.datasize > up->flashsize)
|
||||
Retwarning("apptable section %s outside flash [0, 0x%04x]",
|
||||
str_ccinterval(me.datastart, me.datastart + me.datasize - 1), up->flashsize - 1);
|
||||
|
||||
if(me.bootsize < 0)
|
||||
Retwarning("negative boot section size");
|
||||
if(me.bootstart < 0 || me.bootstart + me.bootsize > up->flashsize)
|
||||
Retwarning("boot section %s outside flassh [0, 0x%04x]",
|
||||
str_ccinterval(me.bootstart, me.bootstart + me.bootsize - 1), up->flashsize - 1);
|
||||
|
||||
if(me.appsize + me.datasize + me.bootsize != up->flashsize)
|
||||
Retwarning("section sizes do not add up (0x%x) to flash size 0x%x",
|
||||
me.appsize + me.datasize + me.bootsize, up->flashsize);
|
||||
|
||||
if(!me.appsize)
|
||||
Retwarning("no application section");
|
||||
|
||||
if(is_updi(p)) {
|
||||
if(me.bootsize && me.appstart != me.bootsize)
|
||||
Retwarning("application section %s does not touch boot section %s",
|
||||
str_ccinterval(me.appstart, me.appstart + me.appsize - 1),
|
||||
str_ccinterval(me.bootstart, me.bootstart + me.bootsize - 1));
|
||||
if(me.datasize && me.datastart != me.bootsize + me.appsize)
|
||||
Retwarning("apptable section %s does not touch code section %s",
|
||||
str_ccinterval(me.datastart, me.datastart + me.appsize - 1),
|
||||
str_ccinterval(0, me.bootsize + me.appsize - 1));
|
||||
} else {
|
||||
if(me.datasize && me.datastart != me.appsize && me.appstart != 0)
|
||||
Retwarning("apptable section %s does not touch application section %s",
|
||||
str_ccinterval(me.datastart, me.datastart + me.appsize - 1),
|
||||
str_ccinterval(me.appstart, me.appstart + me.appsize - 1));
|
||||
if(me.datasize && me.bootsize && me.bootstart != me.appsize + me.datasize)
|
||||
Retwarning("apptable section %s does not touch boot section %s",
|
||||
str_ccinterval(me.datastart, me.datastart + me.appsize - 1),
|
||||
str_ccinterval(me.bootstart, me.bootstart + me.bootsize - 1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write a vector table to flash addr and return number of bytes written
|
||||
static int putvectortable(const AVRPART *p, const AVRMEM *flm, int addr, int round32) {
|
||||
int vecsz = flm->size <= 8192? 2: 4, ret = p->n_interrupts*vecsz;
|
||||
int app = (ret + vecsz - 2)/2; // Distance to application in words
|
||||
|
||||
for(int i = 0; i < ret; i += vecsz) { // First store rjmps to after table
|
||||
flm->buf[addr + i] = app;
|
||||
flm->buf[addr + i + 1] = 0xc0 + (app >> 8); // rjmp app, rjmp app, ...
|
||||
if(vecsz == 4) // Put nop behind rjmp
|
||||
flm->buf[addr + i + 2] = 0, flm->buf[addr + i + 3] = 0;
|
||||
app -= vecsz/2;
|
||||
}
|
||||
for(int i = 0; i < vecsz; i++) // Leave one vector gap
|
||||
flm->buf[addr + ret++] = round32? ' ': 0;
|
||||
|
||||
if(round32) {
|
||||
flm->buf[addr + ret++] = 0xff; // Put endless loop rjmp .-2 as application
|
||||
flm->buf[addr + ret++] = 0xcf;
|
||||
|
||||
// Then round up to multiples of 32
|
||||
while(ret%32)
|
||||
flm->buf[addr + ret++] = ' ';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Human-readable messages in flash shown with, eg, avrdude -c dryrun -p m168 -xinit -Uflash:r:-:I
|
||||
static const int u384[] = {
|
||||
0x00000800, 0x08000800, 0x1c4218ca, 0x08a5284a, 0x1842184e, 0x00000000, 0x00000000, 0x08010000,
|
||||
0x08010000, 0x08c53086, 0x00430942, 0x08653082,
|
||||
}, u512[] = {
|
||||
|
||||
0x20000800, 0x20000800, 0xf71c7b51, 0x28a288d1, 0x28a28851, 0x28a28859, 0xc71c7856, 0x00000000,
|
||||
0x80020000, 0x80020000, 0x8f22f1cd, 0x80920a23, 0x870e0a21, 0x08120a21, 0x87a2f1c1, 0x00000000,
|
||||
}, bdata[] = {
|
||||
|
||||
0x00000000, 0x00000001, 0x00000001, 0x08000001, 0x08000001, 0xfe381c1d, 0x08442223, 0x08824121,
|
||||
0x08824121, 0x08824121, 0x08442223, 0xf0381c1d, 0x00000000, 0x00000000, 0x00400000, 0x00400000,
|
||||
0x00400000, 0x00400000, 0x785c0e3c, 0x88621102, 0x84422081, 0xfc422081, 0x04422081, 0x04621102,
|
||||
0xf85c0e3c, 0x00000000, 0x00000000, 0x00000000,
|
||||
}, adata[] = {
|
||||
|
||||
0x00000020, 0x00000020, 0x00040020, 0x00040020, 0x3c7f1e2e, 0x40042031, 0x40042021, 0x7c043e21,
|
||||
0x42042121, 0x42042131, 0xfc787e2e, 0x00000000, 0x00000000, 0x00000000,
|
||||
}, rocks[] = {
|
||||
|
||||
0x00000004, 0x0000003c, 0x000000fc, 0x000007fc, 0x00001ffc, 0x0000ffe0, 0x0003ff00, 0x001ffc00,
|
||||
0x007fc000, 0x03fe0000, 0x07f00000, 0x07800000, 0x07e00000, 0x07fc0000, 0x03ff0000, 0x007fe000,
|
||||
0x001ffc00, 0x0003ff00, 0x0000ffe0, 0x00001ffc, 0x000007fc, 0x000000fc, 0x0000003c, 0x00000004,
|
||||
0x04000000, 0x07800000, 0x07e00000, 0x07fc0000, 0x07ff0000, 0x00ffe000, 0x003ff800, 0x0007ff00,
|
||||
0x0001ffc0, 0x00003ff8, 0x00000ffc, 0x000001fc, 0x0000007c, 0x000003fc, 0x00001ff8, 0x0000ffc0,
|
||||
0x0003ff00, 0x001ff800, 0x00ffe000, 0x03ff0000, 0x07fc0000, 0x07e00000, 0x07800000, 0x04000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x07fffffc, 0x07fffffc, 0x07fffffc, 0x07007000, 0x07007000,
|
||||
0x07007c00, 0x0700fe00, 0x0700ff00, 0x0781ffc0, 0x07c3cfe0, 0x03ffc3f0, 0x03ff81fc, 0x01ff00fc,
|
||||
0x007c003c, 0x0000001c, 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x07fffffc, 0x07fffffc,
|
||||
0x07fffffc, 0x0700001c, 0x0700001c, 0x0700001c, 0x0700003c, 0x03800038, 0x03c00078, 0x03e000f0,
|
||||
0x01f803f0, 0x00ffffe0, 0x003fff00, 0x0007f800, 0x00000000, 0x00000000, 0x00000000, 0x07ffff00,
|
||||
0x07ffffc0, 0x07fffff0, 0x000000f8, 0x00000038, 0x0000003c, 0x0000001c, 0x0000003c, 0x00000078,
|
||||
0x000000f8, 0x07fffff0, 0x07ffffc0, 0x07ffff00, 0x00000000, 0x00000000, 0x00000000, 0x07fffffc,
|
||||
0x07fffffc, 0x07fffffc, 0x0700001c, 0x0700001c, 0x0700001c, 0x0700003c, 0x03800038, 0x03c00078,
|
||||
0x03e000f0, 0x01f803f0, 0x00ffffe0, 0x003fff00, 0x0007f800, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x07fffffc, 0x07fffffc, 0x07fffffc, 0x0700e01c, 0x0700e01c, 0x0700e01c, 0x0700e01c, 0x0700e01c,
|
||||
0x0700e01c, 0x0700e01c, 0x0700001c, 0x0700001c, 0x0000001c, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x007ffffc, 0x007ffffc, 0x007ffffc, 0x00070000,
|
||||
0x000e0000, 0x001c0000, 0x00380000, 0x00380000, 0x00700000, 0x00700000, 0x007f0000, 0x007f0000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x0000fe00, 0x0007ffc0, 0x000fffe0, 0x003f01f8, 0x003c0078,
|
||||
0x0078003c, 0x0070001c, 0x0070001c, 0x0078003c, 0x003c0078, 0x003f01f8, 0x000fffe0, 0x0007ffc0,
|
||||
0x0000fe00, 0x00000000, 0x00000000, 0x00000000, 0x0000fe00, 0x0003ffc0, 0x000fffe0, 0x001f01f0,
|
||||
0x003c0078, 0x0078003c, 0x0070001c, 0x0070001c, 0x0070001c, 0x0078003c, 0x00380038, 0x00380038,
|
||||
0x00000000, 0x00000000, 0x00000000, 0xfffffffc, 0xfffffffc, 0xfffffffc, 0x00003000, 0x00007800,
|
||||
0x0000fc00, 0x0003fe00, 0x0007ff00, 0x000fcf80, 0x001f87c0, 0x007f03f0, 0x007e01f8, 0x007800fc,
|
||||
0x0070007c, 0x0060003c, 0x0040001c, 0x0000000c, 0x00000004, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000078, 0x000f8038, 0x001fc038, 0x003fe03c, 0x003fe01c, 0x0038f01c, 0x0078f01c, 0x0070701c,
|
||||
0x0070381c, 0x00703c1c, 0x00703c3c, 0x00701e78, 0x00781ff8, 0x00380fe0, 0x000003c0, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x7ffffc3c, 0x7ffffc3c, 0x7ffffc3c, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
const int *bits, n32;
|
||||
} banner[] = {
|
||||
{u384, sizeof u384/sizeof *u384},
|
||||
{u512, sizeof u512/sizeof *u512},
|
||||
{bdata, sizeof bdata/sizeof *bdata},
|
||||
{adata, sizeof adata/sizeof *adata},
|
||||
{rocks, sizeof rocks/sizeof *rocks},
|
||||
{rocks, sizeof rocks/sizeof *rocks}, // Sic, dummy entry for RND
|
||||
};
|
||||
|
||||
enum { U384, U512, BDATA, ADATA, ROCKS, RND };
|
||||
|
||||
/*
|
||||
* Given a bit stream, put a sequence of '@' or ' ' into flash; note they are
|
||||
* all benign opcodes that do not touch memory or the I/O area:
|
||||
* " ": and r2, r0
|
||||
* "@ ": and r4, r0
|
||||
* " @": sbci r18, 0
|
||||
* "@@": sbci r20, 0
|
||||
*/
|
||||
static void putbanner(const AVRMEM *flm, int addr, int n, int bi) {
|
||||
const int *bp = banner[bi].bits, len = n/10 + random()%(9*n/10);
|
||||
|
||||
for(int i = 0; i < n;) {
|
||||
int scan = bi == RND? random(): *bp;
|
||||
|
||||
for(int j = 0; j < 32; j++) {
|
||||
flm->buf[addr++] = scan & 1? '@': ' ';
|
||||
scan >>= 1;
|
||||
if(++i == n)
|
||||
break;
|
||||
}
|
||||
if(++bp == banner[bi].bits + banner[bi].n32) {
|
||||
bp = banner[bi].bits;
|
||||
if(i > len) // Stop repeating banner after some threshold
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put single 16-bit opcode into memory
|
||||
static void putop16(unsigned char *addr, int op) {
|
||||
addr[0] = op, addr[1] = op >> 8;
|
||||
}
|
||||
|
||||
// Put n/2 random benign opcodes compatible with part into memory at addr
|
||||
static void putcode(const AVRPART *p, const AVRMEM *flm, int addr, int n) {
|
||||
int i, op, inrange, pc, end = addr + n/2*2, avrlevel = avr_get_archlevel(p);
|
||||
|
||||
for(i = 0; i < n/2; i++) {
|
||||
do {
|
||||
inrange = 0;
|
||||
// Last opcode is a long backward jump; the others are random
|
||||
op = i == n/2 - 1? dist2rjmp(-2*(i < 2048? i: 2047)): random() & 0xffff;
|
||||
if(op16_is_benign(op, avrlevel))
|
||||
inrange = (pc = op16_target(addr + 2*i, op)) >= addr && pc < end;
|
||||
} while(!inrange);
|
||||
putop16(flm->buf + addr + 2*i, op);
|
||||
}
|
||||
}
|
||||
|
||||
// Write valid opcodes to flash (banners for -xinit, random code for -xrandom)
|
||||
static void putflash(const Testpart_data *mep, const AVRPART *p, const AVRMEM *flm, int addr, int n, int bi) {
|
||||
unsigned char *top = flm->buf + addr + n - 4;
|
||||
|
||||
if(me.random) {
|
||||
switch(bi) {
|
||||
case U384:
|
||||
case U512:
|
||||
case BDATA: // Bootloader stuff, reduce code length a little
|
||||
n -= random()%(n/8);
|
||||
break;
|
||||
case ADATA:
|
||||
case ROCKS: // Set random code length in [n/4, n]
|
||||
n -= random()%(3*n/4);
|
||||
}
|
||||
if(bi != ADATA) {
|
||||
putcode(p, flm, addr, n);
|
||||
goto seal;
|
||||
}
|
||||
bi = RND; // Make apptable data random @/space sequences
|
||||
}
|
||||
putbanner(flm, addr, n, bi);
|
||||
|
||||
seal: // Put 1-2 endless loops in top memory section
|
||||
if(*top == 0xff)
|
||||
putop16(top, 0xcfff);
|
||||
putop16(top + 2, 0xcfff);
|
||||
}
|
||||
|
||||
// Initialise a user writable memory other than flash or fuses
|
||||
static void putother(const Testpart_data *mep, const AVRPART *p, const AVRMEM *m, const char *str) {
|
||||
const char *name = avr_mem_name(p, m), *hi = me.random? "@ @": "Hello, world!";
|
||||
int len = strlen(str);
|
||||
|
||||
if(len > m->size)
|
||||
len = m->size;
|
||||
if(len <= 0)
|
||||
return;
|
||||
|
||||
memset(m->buf, 0xff, m->size);
|
||||
|
||||
if(me.random)
|
||||
putbanner(m, 0, m->size, RND);
|
||||
else
|
||||
for(int i = 0; i < m->size/3; i += len)
|
||||
if(m->size - i > len)
|
||||
memcpy(m->buf + i, str, len);
|
||||
|
||||
if((len = strlen(name)) > m->size)
|
||||
len = m->size;
|
||||
memcpy(m->buf + m->size - len, name, len);
|
||||
if(len < m->size)
|
||||
m->buf[m->size - len - 1] = ' ';
|
||||
|
||||
if(me.holes && m->size >= 64) {
|
||||
// Remove an initial, a middling and a final section
|
||||
int delta[4]; // Random number between -2 and 2
|
||||
for(size_t i = 0; i < sizeof delta/sizeof *delta; i++)
|
||||
delta[i] = random()%5 - 2;
|
||||
memset(m->buf, 0xff, m->size/8 + delta[0]);
|
||||
memset(m->buf + m->size/2 + delta[1], 0xff, m->size/4 + delta[2]);
|
||||
memcpy(m->buf + m->size/2 + delta[1] + 3, hi, strlen(hi));
|
||||
int len = m->size/8 + delta[3];
|
||||
memset(m->buf + m->size - len, 0xff, len);
|
||||
}
|
||||
}
|
||||
|
||||
AVRPART *dryrun_part(const char *partid, int *bootsizep, int init, int random, int holes, int seed) {
|
||||
pmsg_debug("%s()\n", __func__);
|
||||
|
||||
const AVRPART *p = locate_part(part_list, partid);
|
||||
if(!p) {
|
||||
pmsg_error("cannot find part with id %s\n", partid);
|
||||
return NULL;
|
||||
}
|
||||
Testpart_data *mep = mmt_malloc(sizeof(Testpart_data));
|
||||
AVRPART *q = me.dp = avr_dup_part(p); // Allocate dryrun part and abbreviate with q
|
||||
AVRMEM *m, *fusesm = NULL, *prodsigm = NULL, *calm;
|
||||
|
||||
me.init = init; // Initialise memories with something interesting
|
||||
me.random = random; // Random initialisation of memories
|
||||
me.holes = holes; // Whether eeprom/flash should have holes
|
||||
me.seed = seed; // Seed for random number generator
|
||||
memset(me.fuses, 0xff, sizeof me.fuses);
|
||||
srandom(me.seed? me.seed: time(NULL));
|
||||
|
||||
// Initialise the device with factory setting and erase flash/EEPROM to 0xff
|
||||
for(LNODEID ln = lfirst(q->mem); ln; ln = lnext(ln)) {
|
||||
m = ldata(ln);
|
||||
if(mem_is_in_flash(m) || mem_is_eeprom(m)) {
|
||||
memset(m->buf, 0xff, m->size);
|
||||
} else if(mem_is_fuses(m)) {
|
||||
fusesm = m;
|
||||
} else if(mem_is_a_fuse(m) || mem_is_lock(m)) {
|
||||
// Lock, eg, can have 4 bytes: still allow initialisation from initval
|
||||
if(m->initval != -1 && m->size >= 1 && m->size <= (int) sizeof(m->initval)) {
|
||||
for(int i = 0; i < m->size; i++)
|
||||
m->buf[i] = m->initval >> 8*i;
|
||||
if(mem_is_a_fuse(m)) {
|
||||
int fno = mem_fuse_offset(m);
|
||||
|
||||
for(int i = 0; i < m->size && fno + i < (int) sizeof me.fuses; i++) // pdicfg has 2 bytes
|
||||
me.fuses[fno + i] = m->initval >> 8*i;
|
||||
}
|
||||
} else {
|
||||
memset(m->buf, 0xff, m->size);
|
||||
}
|
||||
} else if(mem_is_signature(m) && (int) sizeof(q->signature) == m->size) {
|
||||
memcpy(m->buf, q->signature, m->size);
|
||||
} else if(mem_is_calibration(m)) {
|
||||
memset(m->buf, 'U', m->size); // 'U' for uncalibrated or unknown :)
|
||||
} else if(mem_is_osc16err(m)) {
|
||||
memset(m->buf, 'e', m->size);
|
||||
} else if(mem_is_osc20err(m)) {
|
||||
memset(m->buf, 'E', m->size);
|
||||
} else if(mem_is_osccal16(m)) {
|
||||
memset(m->buf, 'o', m->size);
|
||||
} else if(mem_is_osccal20(m)) {
|
||||
memset(m->buf, 'O', m->size);
|
||||
} else if(mem_is_sib(m)) {
|
||||
memset(m->buf, 'S', m->size);
|
||||
} else if(mem_is_tempsense(m)) {
|
||||
memset(m->buf, 'T', m->size); // 'T' for temperature calibration values
|
||||
} else if(mem_is_sernum(m)) {
|
||||
for(int i = 0; i < m->size; i++) // Set serial number UTSRQPONM...
|
||||
m->buf[i] = me.random? 'A' + random()%26: 'U' - i >= 'A'? 'U' - i: 0xff;
|
||||
} else if(mem_is_sigrow(m) && m->size >= 6) {
|
||||
prodsigm = m;
|
||||
memset(m->buf, 0xff, m->size);
|
||||
// Classic parts: signature at even addresses
|
||||
int n = is_tpi(q)? 1: 2; // ... unless it's the TPI parts t102/t104
|
||||
|
||||
if(is_classic(q))
|
||||
for(int i = 0; i < 3; i++)
|
||||
m->buf[n*i] = q->signature[i];
|
||||
} else if(mem_is_io(m)) { // Initialise reset values (if known)
|
||||
int nr;
|
||||
const Register_file *rf = avr_locate_register_file(q, &nr);
|
||||
|
||||
if(rf)
|
||||
for(int i = 0; i < nr; i++)
|
||||
if(rf[i].initval != -1 && rf[i].size > 0 && rf[i].size < 5)
|
||||
if(rf[i].addr >= 0 && rf[i].addr + rf[i].size <= m->size)
|
||||
for(int k = 0; k < rf[i].size; k++)
|
||||
m->buf[rf[i].addr + k] = rf[i].initval >> 8*k;
|
||||
}
|
||||
}
|
||||
if(prodsigm) {
|
||||
if(q->prog_modes & (PM_UPDI | PM_PDI)) {
|
||||
for(LNODEID ln = lfirst(q->mem); ln; ln = lnext(ln)) {
|
||||
m = ldata(ln);
|
||||
if(m->buf == prodsigm->buf) // Skip prodsig memory
|
||||
continue;
|
||||
int off = m->offset - prodsigm->offset;
|
||||
int cpy = m->size;
|
||||
|
||||
// Submemory of prodsig, eg, signature and tempsense? Copy into prodsig
|
||||
if(off >= 0 && off + cpy <= prodsigm->size)
|
||||
memcpy(prodsigm->buf + off, m->buf, cpy);
|
||||
}
|
||||
}
|
||||
if(is_classic(q) && (calm = avr_locate_calibration(q))) {
|
||||
// Calibration bytes of classic parts are interspersed with signature
|
||||
int n, tpi = is_tpi(q); // ... unless it's the TPI parts t102/t104
|
||||
|
||||
for(int i = 0; i < calm->size; i++) {
|
||||
if((n = tpi? 3 + i: 2*i + 1) < prodsigm->size)
|
||||
prodsigm->buf[n] = 'U';
|
||||
}
|
||||
}
|
||||
if(is_classic(q) && (m = avr_locate_sernum(q))) { // m324pb/m328pb, t102/t104
|
||||
int off = m->offset - prodsigm->offset;
|
||||
int cpy = m->size;
|
||||
|
||||
if(off >= 0 && off + cpy <= prodsigm->size)
|
||||
memcpy(prodsigm->buf + off, m->buf, cpy);
|
||||
}
|
||||
}
|
||||
if(fusesm)
|
||||
memcpy(fusesm->buf, me.fuses, minm((size_t) fusesm->size, sizeof me.fuses));
|
||||
|
||||
if(!me.random && !me.init) // OK, no further initialisation needed
|
||||
goto finished;
|
||||
|
||||
int nc, bakverb = verbose;
|
||||
|
||||
verbose = -123; // Silently retrieve uP_table[] entry and config list
|
||||
const Avrintel *up = avr_locate_uP(q);
|
||||
const Configitem *cp = avr_locate_configitems(q, &nc);
|
||||
|
||||
verbose = bakverb;
|
||||
AVRMEM *flm = avr_locate_flash(q);
|
||||
AVRMEM *ee = avr_locate_eeprom(q);
|
||||
int incons = flm && up && (up->flashsize != flm->size || flm->size <= 0 ||
|
||||
(ee && (up->eepromsize != ee->size || ee->size <= 0)) ||
|
||||
up->nboots != q->n_boot_sections || up->nboots < 0 ||
|
||||
up->bootsize != q->boot_section_size || up->bootsize < 0 || memcmp(up->sigs, q->signature, 3)
|
||||
);
|
||||
|
||||
// Ensure can use up and cp with impunity
|
||||
if(!flm || !up || incons || !cp) {
|
||||
pmsg_warning("%s for %s; not initialising memories beyond factory settings\n", !flm? "no flash":
|
||||
!up? "no uP_table[] entry": incons? "inconsistent uP_table[] entry": "no config table", q->desc);
|
||||
goto finished;
|
||||
}
|
||||
|
||||
randflashconfig(mep, q, up, cp, nc);
|
||||
if(flashlayout(mep, q, flm, up, cp, nc) < 0)
|
||||
goto finished;
|
||||
|
||||
int vtb = putvectortable(q, flm, me.appstart, me.init), urbtsz = 0;
|
||||
|
||||
int urboot = random()%3 && me.bootsize <= 512 && flm->size >= 1024 &&
|
||||
flm->size >= 4*me.bootsize && is_classic(q) && is_spm(q);
|
||||
if(urboot) { // Give some classic parts a small bootloader
|
||||
int ps = flm->page_size;
|
||||
|
||||
urbtsz = me.bootsize? me.bootsize: flm->size > 32768? 512: flm->size < 16384? 256: 384;
|
||||
urbtsz = (urbtsz + ps - 1)/ps*ps;
|
||||
if(!me.bootsize && !me.datasize) {
|
||||
me.bootsize += urbtsz;
|
||||
me.appsize -= urbtsz;
|
||||
me.bootstart = me.appsize;
|
||||
}
|
||||
int ubaddr = me.bootstart;
|
||||
|
||||
putflash(mep, me.dp, flm, ubaddr, urbtsz, urbtsz == 384? U384: U512);
|
||||
} else if(me.bootsize) {
|
||||
int btb = 0;
|
||||
|
||||
if(me.bootsize >= 2048)
|
||||
btb = putvectortable(q, flm, me.bootstart, me.init);
|
||||
putflash(mep, me.dp, flm, me.bootstart + btb, me.bootsize - btb, BDATA);
|
||||
}
|
||||
|
||||
if(me.datasize)
|
||||
putflash(mep, me.dp, flm, me.datastart, me.datasize, ADATA);
|
||||
|
||||
putflash(mep, me.dp, flm, me.appstart + vtb, me.appsize - vtb - urbtsz, ROCKS);
|
||||
|
||||
if(me.holes && me.appsize >= 128) { // Generate holes in the code section
|
||||
int start = me.appstart & ~1, size = me.appsize & ~3;
|
||||
int len3 = size/3 & ~3, len4 = size/4 - 1;
|
||||
unsigned char *code = flm->buf + start;
|
||||
|
||||
/*
|
||||
* Cut away just shy of 1/4 of flash either side deliberately making the
|
||||
* hole odd-sized. Overwrite odd boundary with an @ (0x40): note that the
|
||||
* opcodes 0x40ff (sbrs r20, 0) and 0xff40 (sbci r31, 0x0f) are benign.
|
||||
* Then cut off the central third of the code section and introduce an
|
||||
* island with a single @ in the middle (generating a benign opcode).
|
||||
*/
|
||||
memset(code, 0xff, len4); code[len4] = '@';
|
||||
memset(code + size - len4, 0xff, len4); code[size - len4 - 1] = '@';
|
||||
memset(code + len3, 0xff, len3); code[size/2 - 1] = '@';
|
||||
// Terminate code section with two endless loops
|
||||
code[size - 4] = code[size - 2] = 0xff;
|
||||
code[size - 3] = code[size - 1] = 0xcf;
|
||||
}
|
||||
|
||||
// Initialise other overlapping flash memories from flash (think XMEGA)
|
||||
for(LNODEID ln = lfirst(me.dp->mem); ln; ln = lnext(ln)) {
|
||||
m = ldata(ln);
|
||||
|
||||
if(mem_is_in_flash(m) && flm != m) { // Overlapping flash memories?
|
||||
unsigned int faddr = m->offset - flm->offset;
|
||||
|
||||
if(faddr < (unsigned int) flm->size && faddr + m->size <= (unsigned int) flm->size)
|
||||
memcpy(m->buf, flm->buf + faddr, m->size);
|
||||
}
|
||||
}
|
||||
|
||||
if((m = avr_locate_eeprom(q)))
|
||||
putother(mep, q, m, "The quick brown fox jumps over the lazy dog. ");
|
||||
if((m = avr_locate_userrow(q)))
|
||||
putother(mep, q, m, "The five boxing wizards jump quickly. ");
|
||||
if((m = avr_locate_bootrow(q)))
|
||||
putother(mep, q, m, "Lorem ipsum dolor sit amet. ");
|
||||
|
||||
finished:
|
||||
if(bootsizep)
|
||||
*bootsizep = me.bootsize;
|
||||
AVRPART *ret = me.dp;
|
||||
mmt_free(mep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
14
src/fileio.c
14
src/fileio.c
@@ -200,6 +200,10 @@ AVRMEM *fileio_any_memory(const char *name) {
|
||||
return avr_new_memory(name, ANY_MEM_SIZE);
|
||||
}
|
||||
|
||||
int mem_is_any(const AVRMEM *mem) {
|
||||
return mem->type == 0 && mem->size == (int) ANY_MEM_SIZE;
|
||||
}
|
||||
|
||||
#define boffset(p, basemem) baseoffset((p), avr_locate_ ## basemem(p), # basemem)
|
||||
|
||||
static int baseoffset(const AVRPART *p, const AVRMEM *base, const char *memname) {
|
||||
@@ -210,7 +214,7 @@ static int baseoffset(const AVRPART *p, const AVRMEM *base, const char *memname)
|
||||
|
||||
// Extends where memory is put in flat address space of .elf files
|
||||
unsigned fileio_mem_offset(const AVRPART *p, const AVRMEM *mem) {
|
||||
if(mem->type == 0 && mem->size == (int) ANY_MEM_SIZE)
|
||||
if(mem_is_any(mem))
|
||||
return 0;
|
||||
|
||||
unsigned location =
|
||||
@@ -450,8 +454,7 @@ static int ihex_readrec(struct ihexsrec *ihex, char *rec) {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
int fileio_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 > MEND(FLASH) + 1? fileio_mem_offset(p, mem): 0;
|
||||
|
||||
@@ -622,7 +625,7 @@ static int ihex2b(const char *infile, FILE *inf, const AVRPART *p, const AVRMEM
|
||||
pmsg_warning("no end of file record found for Intel Hex file %s\n", infile);
|
||||
|
||||
done:
|
||||
rc = any2mem(p, mem, segp, any, maxaddr);
|
||||
rc = fileio_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);
|
||||
@@ -950,7 +953,7 @@ static int srec2b(const char *infile, FILE *inf, const AVRPART *p,
|
||||
|
||||
pmsg_warning("no end of file record found for Motorola S-Records file %s\n", infile);
|
||||
done:
|
||||
rc = any2mem(p, mem, segp, any, maxaddr);
|
||||
rc = fileio_any2mem(p, mem, segp, any, maxaddr);
|
||||
avr_free_mem(any);
|
||||
if(!rc)
|
||||
pmsg_warning("no %s data found in Motorola S-Record file %s\n", mem->desc, infile);
|
||||
@@ -1249,7 +1252,6 @@ done:
|
||||
|
||||
// Read/write binary files and return highest memory addr set + 1
|
||||
static int fileio_rbin(struct fioparms *fio, const char *filename, FILE *f, const AVRMEM *mem, const Segment *segp) {
|
||||
|
||||
int rc;
|
||||
|
||||
switch(fio->op) {
|
||||
|
||||
@@ -1303,10 +1303,12 @@ extern "C" {
|
||||
char *fileio_fmtstr(FILEFMT format);
|
||||
int fileio_fmtchr(FILEFMT format);
|
||||
AVRMEM *fileio_any_memory(const char *name);
|
||||
int mem_is_any(const AVRMEM *mem);
|
||||
unsigned fileio_mem_offset(const AVRPART *p, const AVRMEM *mem);
|
||||
FILE *fileio_fopenr(const char *fname);
|
||||
int is_generated_fname(const char *filename);
|
||||
int generated_file_has_contents(const AVRPART *part, const char *filename);
|
||||
int fileio_any2mem(const AVRPART *p, const AVRMEM *mem, const Segment *segp, const AVRMEM *any, unsigned maxsize);
|
||||
int fileio_fmt_autodetect_fp(FILE *f);
|
||||
int fileio_fmt_autodetect(const char *fname);
|
||||
int fileio_mem(int oprwv, const char *filename, FILEFMT format, const AVRPART *p, const AVRMEM *mem, int size);
|
||||
@@ -1853,6 +1855,8 @@ extern "C" {
|
||||
Urboot_template **urboottemplate(const Avrintel *up, const char *mcu, const char *io, const char *blt,
|
||||
int req_feat, int req_ulevel, int showall, int *np, int silent);
|
||||
|
||||
AVRPART *dryrun_part(const char *id, int *bootsizep, int init, int random, int holes, int seed);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user