mirror of
https://github.com/avrdudes/avrdude.git
synced 2026-06-02 09:46:34 +03:00
Merge pull request #2090 from stefanrueger/dryboot
Make `-c dryboot` emulate `-c urclock -x nometadata`
This commit is contained in:
@@ -1600,7 +1600,9 @@ versions of the bootloader.
|
||||
.Bl -tag -offset indent -width indent
|
||||
.It Ar dryrun
|
||||
.It Ar dryboot
|
||||
These two programmers emulate programming and accept the following parameters:
|
||||
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:
|
||||
.Bl -tag -offset indent -width indent
|
||||
.It Ar init
|
||||
Initialise memories with human-readable patterns. Flash memory will be
|
||||
|
||||
@@ -149,6 +149,8 @@ eeprom, programming fuse/lock bits, etc.
|
||||
@cindex Programmers supported
|
||||
@cindex Emulating a HW programmer (dryrun)
|
||||
@cindex Emulating a bootloader (dryboot)
|
||||
@cindex dryrun
|
||||
@cindex dryboot
|
||||
|
||||
Programming a microcontroller either requires a physical programmer that
|
||||
sits between the target chip and the PC running AVRDUDE, or a bootloader
|
||||
@@ -1339,7 +1341,9 @@ extended parameters to be specified on the command line.
|
||||
@item dryrun
|
||||
@itemx dryboot
|
||||
|
||||
Both dryrun and dryboot programmers emulate programming and accept the following parameters:
|
||||
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:
|
||||
|
||||
@table @code
|
||||
@cindex @code{flash}
|
||||
@@ -4574,6 +4578,92 @@ L7ffe: .byte 0xe6, 0x40 ; _@
|
||||
@end smallexample
|
||||
|
||||
|
||||
@page
|
||||
|
||||
@noindent
|
||||
It is noteworthy that @code{-c dryboot} allows uploading of one urboot
|
||||
bootloader and, from that point onwards, emulates @code{-c urclock -x
|
||||
nometadata}. This means, eg, @code{-c dryboot} will not allow the
|
||||
bootloader be overwritten:
|
||||
|
||||
@cindex Emulating a bootloader (dryboot)
|
||||
@cindex Emulating a HW programmer (dryrun)
|
||||
@cindex Vector table
|
||||
@cindex Patching the vector table
|
||||
@cindex dryrun
|
||||
@cindex dryboot
|
||||
@cindex urclock
|
||||
|
||||
@smallexample
|
||||
@cartouche
|
||||
$ avrdude -q -c dryboot -p m328p -U urboot:autobaud_ee -U blink.hex \
|
||||
-T "write flash 0x7f80 0x00"
|
||||
|
||||
Processing -U flash:w:urboot:autobaud_ee:i
|
||||
Reading 376 bytes for flash from input file urboot:autobaud_ee
|
||||
Writing 376 bytes to flash, 376 bytes written, 376 verified
|
||||
Setting fuses for bootloader urboot:autobaud_ee
|
||||
Detected urboot bootloader u8.0 weU-jPra- in [0x7e80, 0x7fff] with vector=25
|
||||
|
||||
Processing -U flash:w:blink.hex:i
|
||||
Reading 354 bytes for flash from input file blink.hex
|
||||
Writing 354 bytes to flash, 354 bytes written, 354 verified
|
||||
|
||||
Processing -T write flash 0x7f80 0x00
|
||||
Warning: (write) programmer write protects flash address 0x7f80
|
||||
|
||||
Avrdude done. Thank you.
|
||||
@end cartouche
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
More importantly, @code{-c dryboot} patches the vector table of any
|
||||
to-be-uploaded sketch if the previously installed bootloader is an urboot
|
||||
vector bootloader just like @code{avrdude -c urclock -x nometadata} would;
|
||||
this is needed for the vector bootloader to work (for details, see the
|
||||
@url{https://github.com/stefanrueger/urboot} project):
|
||||
|
||||
@smallexample
|
||||
@cartouche
|
||||
$ avrdude -qq -c dryboot -p m328p -U urboot:autobaud_ee -U blink.hex \
|
||||
-T "disasm flash 0 2" -T "disasm flash 100 4"
|
||||
|
||||
L0000: 3f cf rjmp .-386 ; L7e80
|
||||
L0064: 0c 94 34 00 jmp 0x0068
|
||||
@end cartouche
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
In contrast, using @code{-c dryrun} does not patch the vector table of a
|
||||
sketch as it emulates and behaves like an external programmer:
|
||||
|
||||
@smallexample
|
||||
@cartouche
|
||||
$ avrdude -qq -c dryrun -p m328p -U urboot:autobaud_ee -U blink.hex \
|
||||
-T "disasm flash 0 2" -T "disasm flash 100 2"
|
||||
|
||||
L0000: 33 c0 rjmp .+102 ; L0068
|
||||
L0064: 11 c0 rjmp .+34 ; L0088
|
||||
@end cartouche
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
The following example prepares a patched sketch for upload through a
|
||||
self-written uploader or through urboot's dual boot. Note that @code{385}
|
||||
is the size of the bootloader plus 1 and that the generated file
|
||||
@code{blink-patched.hex} has the size of flash minus 384. In absence of
|
||||
the option @code{-A} AVRDUDE drops all trailing 0xff from the patched
|
||||
sketch.
|
||||
|
||||
@cindex Dual boot
|
||||
|
||||
@smallexample
|
||||
@cartouche
|
||||
$ avrdude -qq -c dryboot -p m328p -U urboot:autobaud_ee -U blink.hex \
|
||||
-T "save flash 0 -385 blink-patched.hex:I"
|
||||
@end cartouche
|
||||
@end smallexample
|
||||
|
||||
@page
|
||||
|
||||
@c
|
||||
|
||||
270
src/dryrun.c
270
src/dryrun.c
@@ -45,7 +45,7 @@
|
||||
|
||||
// Context of the programmer
|
||||
typedef enum {
|
||||
DRY_NOBOOTLOADER, // No bootloader, taking to an ordinary programmer
|
||||
DRY_NOBOOTLOADER, // No bootloader, talking to an ordinary programmer
|
||||
DRY_TOP, // Bootloader and it sits at top of flash
|
||||
DRY_BOTTOM, // Bootloader sits at bottom of flash (UPDI parts)
|
||||
} Dry_prog;
|
||||
@@ -61,10 +61,17 @@ typedef struct {
|
||||
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
|
||||
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)
|
||||
int32_t blstart, blend; // Bootloader address range [blstart, blend] for write protection
|
||||
int32_t pfstart, pfend; // Programmable flash address range [pfstart, pfend]
|
||||
} urdesc;
|
||||
} Dryrun_data;
|
||||
|
||||
// Use private programmer data as if they were a global structure dry
|
||||
#define dry (*(Dryrun_data *)(pgm->cookie))
|
||||
#define ur (dry.urdesc)
|
||||
|
||||
#define Return(...) do { pmsg_error(__VA_ARGS__); msg_error("\n"); return -1; } while(0)
|
||||
#define Retwarning(...) do { pmsg_warning(__VA_ARGS__); \
|
||||
@@ -72,6 +79,216 @@ typedef struct {
|
||||
|
||||
static int dryrun_readonly(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, unsigned int addr);
|
||||
|
||||
// Initialise urboot descriptor once an urboot bootloader is detected
|
||||
static int dryrun_init_ur(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||
// The first urboot bootloader detection freezes the parameters
|
||||
if(ur.urversion)
|
||||
return 0;
|
||||
|
||||
uint8_t top[6];
|
||||
const AVRMEM *flm = avr_locate_flash(p);
|
||||
if(!flm)
|
||||
Return("cannot locate flash memory for %s\n", p->desc);
|
||||
|
||||
// No urboot bootloaders on AVR32 parts, neither on really small devices
|
||||
if(is_awire(p) || flm->size < 1024)
|
||||
return 0;
|
||||
|
||||
if(!is_updi(p)) {
|
||||
// Check top 6 bytes from flash to obtain intell about bootloader and type
|
||||
for(int i = sizeof top - 1; i >= 0; i--) {
|
||||
if(pgm->read_byte(pgm, p, flm, flm->size - sizeof top + i, top + i) < 0)
|
||||
return -1;
|
||||
// Abort if last byte in flash does not indicate urboot v7.5 ... v12.7 == 0147
|
||||
if(i == sizeof top - 1 && (top[i] < 075 || top[i] > 0147))
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t numpags = top[0] & 0x7f; // Number of bootloader pages from v7.5
|
||||
uint8_t vectnum = top[1] & 0x7f; // Vector number for application start from v7.5
|
||||
uint16_t rjmpwp = buf2uint16(top+2); // rjmp to bootloader pgm_write_page() or ret
|
||||
// uint8_t cap = top[4]; // Capability byte not needed
|
||||
uint8_t urver = top[5]; // Urboot version; low 3 bits = minor version: 076 = v7.6
|
||||
|
||||
// Could be urboot bootloader v7.5 .. v12.7: check further properties
|
||||
if(isop(rjmpwp, rjmp) || isop(rjmpwp, ret)) { // OK, valid rjmpwp opcode
|
||||
int blsize = numpags*flm->page_size;
|
||||
// Size of urboot bootloader should be in [64, 2048] (in v7.6 these are 224-512 bytes)
|
||||
if(blsize >= 64 && blsize <= 2048 && vectnum <= p->n_interrupts) { // Within range
|
||||
int dfromend = dist_rjmp(rjmpwp, flm->size) - 4;
|
||||
// Further check whether writepage() rjmp opcode jumps backwards into bootloader
|
||||
if(isop(rjmpwp, ret) || (dfromend >= -blsize && dfromend < -6)) { // urboot!
|
||||
ur.blstart = flm->size - blsize;
|
||||
ur.blend = flm->size - 1;
|
||||
ur.pfstart = 0;
|
||||
ur.pfend = ur.blstart - 1;
|
||||
ur.vectornum = vectnum;
|
||||
ur.urversion = urver;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // @@@ Fixme: todo when UPDI urboot bootloaders available
|
||||
}
|
||||
|
||||
if(ur.urversion) {
|
||||
char buf[20];
|
||||
urbootPutVersion(buf, (uint16_t *) top);
|
||||
pmsg_info("detected urboot bootloader %s in [0x%04x, 0x%04x] with vector=%d\n",
|
||||
buf, ur.blstart, ur.blend, ur.vectornum);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// At the beginning of every terminal command
|
||||
static int dryrun_cmdhook(const PROGRAMMER *pgm, const AVRPART *p, int argc_uu, const char *argv_uu[]) {
|
||||
return dryrun_init_ur(pgm, p);
|
||||
}
|
||||
|
||||
// At the beginning of every -U command and during execution of terminal backup, restore and verify
|
||||
static int dryrun_updatehook(const PROGRAMMER *pgm, const AVRPART *p, const UPDATE *upd_uu, int flags_uu) {
|
||||
return dryrun_init_ur(pgm, p);
|
||||
}
|
||||
|
||||
// Called after the input file has been read for writing or verifying flash
|
||||
static int dryrun_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *flm,
|
||||
const char *fname_uu, int size) {
|
||||
|
||||
int maxsize = ur.pfend+1, firstbeg, firstlen;
|
||||
const int vecsz = flm->size <= 8192? 2: 4; // Small parts use rjmp, large a 4-byte jmp
|
||||
|
||||
// Compute begin and length of first contiguous block in input
|
||||
for(firstbeg=0; firstbeg < size; firstbeg++)
|
||||
if(flm->tags[firstbeg] & TAG_ALLOCATED)
|
||||
break;
|
||||
for(firstlen=0; firstbeg+firstlen < size; firstlen++)
|
||||
if(!(flm->tags[firstbeg+firstlen] & TAG_ALLOCATED))
|
||||
break;
|
||||
|
||||
// Sanity: no patching if bootloader location is unknown
|
||||
if(ur.blend <= ur.blstart)
|
||||
goto nopatch;
|
||||
|
||||
// Sanity check the bootloader position
|
||||
if(ur.blstart < 0 || ur.blstart >= flm->size || ur.blend < 0 || ur.blend >= flm->size)
|
||||
Return("bootloader [0x%04x, 0x%04x] outside flash [0, 0x%04x]",
|
||||
ur.blstart, ur.blend, flm->size-1);
|
||||
|
||||
// Check size of uploded application and protect bootloader from being overwritten
|
||||
if((!is_updi(p) && size > maxsize) || (is_updi(p) && firstbeg <= ur.blend))
|
||||
Return("input [0x%04x, 0x%04x] overlaps b/loader [0x%04x, 0x%04x]",
|
||||
firstbeg, size-1, ur.blstart, ur.blend);
|
||||
|
||||
if(size > maxsize)
|
||||
Return("input [0x%04x, 0x%04x] extends programmable area [0x%04x, 0x%04x]",
|
||||
firstbeg, size-1, ur.pfstart, ur.pfend);
|
||||
|
||||
if(is_updi(p))
|
||||
goto nopatch;
|
||||
|
||||
bool llcode = firstbeg == 0 && firstlen > p->n_interrupts*vecsz; // Looks like code
|
||||
bool llvectors = firstbeg == 0 && firstlen >= p->n_interrupts*vecsz; // Looks like vector table
|
||||
for(int i = 0; llvectors && i < p->n_interrupts*vecsz; i += vecsz) {
|
||||
uint16_t op16 = buf2uint16(flm->buf+i);
|
||||
if(!isop(op16, rjmp) && !(vecsz == 4 && isop(op16, jmp)))
|
||||
llvectors = 0;
|
||||
}
|
||||
|
||||
if(llcode && !llvectors && ur.vectornum > 0)
|
||||
pmsg_warning("not patching jmp to application as input does not start with a vector table\n");
|
||||
|
||||
// Patch vectors if input looks like code and it's a vector bootloader with known vector number
|
||||
if(llcode && llvectors && ur.vectornum > 0) {
|
||||
uint16_t reset16;
|
||||
int reset32, appstart, appvecloc;
|
||||
|
||||
appvecloc = ur.vectornum*vecsz; // Location of jump-to-application in vector table
|
||||
reset16 = buf2uint16(flm->buf); // First reset word of to-be-written application
|
||||
reset32 = vecsz == 2? reset16: buf2uint32(flm->buf);
|
||||
|
||||
/*
|
||||
* Compute where the application starts from the reset vector. The assumptions are that the
|
||||
* - Vector table, and therefore the reset vector, resides at address zero
|
||||
* - Compiler puts either a jmp or an rjmp at address zero
|
||||
* - Compiler does not shorten the vector table if no or few interrupts are used
|
||||
* - Compiler does not utilise unused interrupt vectors to place code there
|
||||
*/
|
||||
|
||||
if(reset2addr(flm->buf, vecsz, flm->size, &appstart) < 0) {
|
||||
pmsg_warning("not patching input as opcode word %04x at reset is not r/jmp\n", reset16);
|
||||
goto nopatch;
|
||||
}
|
||||
|
||||
// Only patch if appstart does not already point to the bootloader
|
||||
if(appstart != ur.blstart) {
|
||||
int vectorsend = vecsz*ur.vectornum;
|
||||
if(appstart < vectorsend || appstart >= size) { // appstart should be in [vectorsend, size)
|
||||
if(appstart != ur.blstart) {
|
||||
pmsg_warning("not patching as reset opcode %0*x jumps to 0x%04x,\n",
|
||||
vecsz*2, reset32, appstart);
|
||||
imsg_warning("ie, outside code area [0x%04x, 0x%04x)\n",
|
||||
vectorsend, size);
|
||||
}
|
||||
goto nopatch;
|
||||
}
|
||||
|
||||
// OK, now have bootloader start and application start: patch
|
||||
set_resetvector(ur.blstart, flm->size, flm->buf+0, vecsz, 1);
|
||||
if(vecsz == 4)
|
||||
uint32tobuf(flm->buf+appvecloc, jmp_opcode(appstart));
|
||||
else
|
||||
uint16tobuf(flm->buf+appvecloc, rjmp_opcode(appstart - appvecloc, flm->size));
|
||||
}
|
||||
}
|
||||
|
||||
nopatch:
|
||||
|
||||
// Ensure that vector bootloaders have correct r/jmp at address 0
|
||||
if(!is_updi(p) && ur.blstart && ur.vectornum > 0) {
|
||||
int resetdest, set = 0;
|
||||
for(int i = 0; i < vecsz; i++)
|
||||
if(flm->tags[i] & TAG_ALLOCATED)
|
||||
set++;
|
||||
|
||||
// Reset vector not programmed? Or -F? Ensure a jmp to bootloader
|
||||
if(ovsigck || set != vecsz) {
|
||||
unsigned char jmptoboot[4];
|
||||
int resetsize = set_resetvector(ur.blstart, flm->size, jmptoboot, vecsz, 1);
|
||||
|
||||
if(set != vecsz) {
|
||||
unsigned char device[4];
|
||||
// Read reset vector from device flash
|
||||
for(int i = 0; i < vecsz; i++)
|
||||
if(pgm->read_byte(pgm, p, flm, i, device+i) < 0)
|
||||
return -1;
|
||||
|
||||
// Mix with already set bytes
|
||||
for(int i = 0; i < vecsz; i++)
|
||||
if(!(flm->tags[i] & TAG_ALLOCATED))
|
||||
flm->buf[i] = device[i];
|
||||
}
|
||||
|
||||
if(reset2addr(flm->buf, vecsz, flm->size, &resetdest) < 0 || resetdest != ur.blstart) {
|
||||
for(int i=0; i < resetsize; i++) {
|
||||
flm->buf[i] = jmptoboot[i];
|
||||
flm->tags[i] |= TAG_ALLOCATED;
|
||||
}
|
||||
}
|
||||
} else if(firstbeg < vecsz) { // Double-check reset vector jumps to bootloader
|
||||
if(reset2addr(flm->buf, vecsz, flm->size, &resetdest) < 0)
|
||||
Return("input would overwrite the reset vector bricking the bootloader\n"
|
||||
" using -F will try to patch the input but this may not be what is needed");
|
||||
if(resetdest != ur.blstart)
|
||||
Return("input points reset to 0x%04x, not to bootloader at 0x%04x\n"
|
||||
" using -F will try to patch the input but this may not be what is needed",
|
||||
resetdest, ur.blstart);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
// Read expected signature bytes from part description
|
||||
static int dryrun_read_sig_bytes(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *sigmem) {
|
||||
pmsg_debug("%s()", __func__);
|
||||
@@ -515,6 +732,10 @@ static void dryrun_enable(PROGRAMMER *pgm, const AVRPART *p) {
|
||||
AVRMEM *m, *fusesm = NULL, *prodsigm = NULL, *calm;
|
||||
AVRPART *q = dry.dp = avr_dup_part(p); // Allocate dryrun part and abbreviate with q
|
||||
|
||||
// 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));
|
||||
|
||||
@@ -777,9 +998,26 @@ static int dryrun_paged_write(const PROGRAMMER *pgm, const AVRPART *p, const AVR
|
||||
Return("cannot write page [0x%04x, 0x%04x] to %s %s as it is incompatible with memory [0, 0x%04x]",
|
||||
addr, end - 1, dry.dp->desc, dmem->desc, dmem->size - 1);
|
||||
|
||||
// Protect reset vector just as -c urclock would
|
||||
if(dry.bl == DRY_TOP && ur.vectornum > 0 && (mem_is_application(m) || mem_is_flash(m)) && addr == 0)
|
||||
for(unsigned vecsz = m->size <= 8192? 2u: 4u, i = 0; i < vecsz && i < n_bytes; i++)
|
||||
pgm->read_byte(pgm, p, dmem, i, m->buf+i);
|
||||
|
||||
for(; addr < end; addr += chunk) {
|
||||
chunk = end - addr < page_size? end - addr: page_size;
|
||||
// @@@ Check for bootloader write protection here
|
||||
|
||||
// Silently skip writing the chunk if that were to overwrite bootloader
|
||||
if(dry.bl && mchr == 'F' && !mem_is_apptable(m) && ur.blend > ur.blstart) {
|
||||
const AVRMEM *am;
|
||||
int testa = addr;
|
||||
|
||||
// Translate XMEGA boot addresses to flash addresses
|
||||
if(is_pdi(p) && mem_is_boot(m) && (am = avr_locate_application(p)))
|
||||
testa += am->size;
|
||||
|
||||
if(testa >= ur.blstart && testa+chunk-1 <= ur.blend)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unless it is a bootloader flash looks like NOR-memory
|
||||
(mchr == 'F' && !dry.bl? memand: memcpy) (dmem->buf + addr, m->buf + addr, chunk);
|
||||
@@ -953,7 +1191,6 @@ static void dryrun_display(const PROGRAMMER *pgm, const char *p_unused) {
|
||||
|
||||
// Return whether an address is write protected
|
||||
static int dryrun_readonly(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, unsigned int addr) {
|
||||
|
||||
if(mem_is_readonly(mem))
|
||||
return 1;
|
||||
|
||||
@@ -963,7 +1200,27 @@ static int dryrun_readonly(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM
|
||||
return 0;
|
||||
}
|
||||
|
||||
// @@@ check for bootloader write protection
|
||||
// Bootloader
|
||||
if(mem_is_in_flash(mem) && !mem_is_apptable(mem)) {
|
||||
const AVRMEM *m;
|
||||
|
||||
// Translate XMEGA boot addresses to flash addresses
|
||||
if(is_pdi(p) && mem_is_boot(mem) && (m = avr_locate_application(p)))
|
||||
addr += m->size;
|
||||
|
||||
if(addr > (unsigned int) ur.pfend)
|
||||
return 1;
|
||||
if(addr < (unsigned int) ur.pfstart)
|
||||
return 1;
|
||||
// Protect reset vector once vector bootloader detected
|
||||
if(addr < 4 && !is_updi(p) && ur.vectornum > 0)
|
||||
if(addr < ((m = avr_locate_flash(p)) && m->size <= 8192? 2u: 4u))
|
||||
return 1;
|
||||
}
|
||||
/* // Below is too realistic as it precludes -U urboot: fuse settings
|
||||
* else if(is_classic(p) && !mem_is_eeprom(mem))
|
||||
* return 1;
|
||||
*/
|
||||
|
||||
if(dry.initialised && (mem_is_in_fuses(mem) || mem_is_lock(mem)))
|
||||
return 1;
|
||||
@@ -1074,4 +1331,9 @@ void dryrun_initpgm(PROGRAMMER *pgm) {
|
||||
pgm->term_keep_alive = dryrun_term_keep_alive;
|
||||
pgm->readonly = dryrun_readonly;
|
||||
pgm->parseextparams = dryrun_parseextparams;
|
||||
if(is_spm(pgm)) {
|
||||
pgm->flash_readhook = dryrun_flash_readhook;
|
||||
pgm->updatehook = dryrun_updatehook;
|
||||
pgm->cmdhook = dryrun_cmdhook;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,6 +981,8 @@ typedef struct {
|
||||
unsigned long ms[LED_N]; // Time in ms after last physical change
|
||||
} Leds;
|
||||
|
||||
typedef struct update UPDATE;
|
||||
|
||||
/*
|
||||
* Any changes in PROGRAMMER, please also ensure changes are made in
|
||||
* - lexer.l
|
||||
@@ -1077,6 +1079,8 @@ typedef struct programmer {
|
||||
void (*teardown)(PROGRAMMER *pgm);
|
||||
int (*flash_readhook)(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *flm,
|
||||
const char *fname, int size);
|
||||
int (*updatehook)(const PROGRAMMER *pgm, const AVRPART *p, const UPDATE *upd, int flags);
|
||||
int (*cmdhook)(const PROGRAMMER *pgm, const AVRPART *p, int argc, const char *argv[]);
|
||||
|
||||
// Cached r/w API for terminal reads/writes
|
||||
int (*write_byte_cached)(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m,
|
||||
@@ -1767,6 +1771,9 @@ extern "C" {
|
||||
|
||||
char *avr_cc_buffer(size_t n);
|
||||
|
||||
// Shortcut for testing whether a 16-bit opcode is a certain mnemonic (rjmp, ret, ...)
|
||||
#define isop(op16, code) op16_is_mnemo(op16, MNEMO_ ## code)
|
||||
|
||||
int op16_is_mnemo(int op16, AVR_mnemo mnemo);
|
||||
int is_opcode32(int op16);
|
||||
int op_width(int op16);
|
||||
@@ -1797,6 +1804,9 @@ extern "C" {
|
||||
uint16_t buf2uint16(const unsigned char *buf);
|
||||
void uint32tobuf(unsigned char *buf, uint32_t opcode32);
|
||||
void uint16tobuf(unsigned char *buf, uint16_t opcode16);
|
||||
int reset2addr(const unsigned char *opcode, int vecsz, int flashsize, int *addrp);
|
||||
int set_resetvector(int blstart, int flsize, uint8_t *reset, int vecsz, int isur);
|
||||
void urbootPutVersion(char *buf, uint16_t *top6table);
|
||||
|
||||
const Uart_conf *getuartsigs(const Avrintel *up, int uart, int alt);
|
||||
int urbootfuses(const PROGRAMMER *pgm, const AVRPART *part, const char *filename);
|
||||
|
||||
@@ -119,6 +119,8 @@ void pgm_init_functions(PROGRAMMER *pgm) {
|
||||
pgm->parseextparams = NULL;
|
||||
pgm->readonly = NULL;
|
||||
pgm->flash_readhook = NULL;
|
||||
pgm->updatehook = NULL;
|
||||
pgm->cmdhook = NULL;
|
||||
}
|
||||
|
||||
PROGRAMMER *pgm_new(void) {
|
||||
|
||||
@@ -2762,6 +2762,9 @@ static int do_cmd(const PROGRAMMER *pgm, const AVRPART *p, int argc, const char
|
||||
int hold, matches;
|
||||
size_t len;
|
||||
|
||||
if(pgm->cmdhook)
|
||||
pgm->cmdhook(pgm, p, argc, argv);
|
||||
|
||||
len = strlen(argv[0]);
|
||||
matches = 0;
|
||||
for(int i = 0; i < NCMDS; i++)
|
||||
|
||||
@@ -640,6 +640,9 @@ int do_op(const PROGRAMMER *pgm, const AVRPART *p, const UPDATE *upd, enum updat
|
||||
Filestats fs;
|
||||
const char *umstr = upd->memstr;
|
||||
|
||||
if(pgm->updatehook)
|
||||
pgm->updatehook(pgm, p, upd, flags);
|
||||
|
||||
if(!(flags & UF_NOHEADING)) {
|
||||
char *heading = update_str(upd);
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
* Published under GNU General Public License, version 3 (GPL-3.0)
|
||||
* Meta-author Stefan Rueger <stefan.rueger@urclocks.com>
|
||||
*
|
||||
* v 1.3
|
||||
* 09.01.2026
|
||||
* v 1.31
|
||||
* 02.04.2026
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -273220,8 +273220,8 @@ static int urlistsearch(const void *p1, const void *p2) {
|
||||
return ((Ul_urlist *) p1)->n - ((Ul_urlist *) p2)->n;
|
||||
}
|
||||
|
||||
// Put version string into a buffer of max 16 characters incl nul; must be u8.0
|
||||
static void urbootPutVersion(char *buf, uint16_t *vertable) {
|
||||
// Put version string into a buffer of max 16 characters incl nul; must be u8.0+
|
||||
static void urbootPutVersion8(char *buf, uint16_t *vertable) {
|
||||
uint16_t ver = vertable[2], rjmpwp = vertable[1];
|
||||
|
||||
uint8_t hi = ver>>8, type = ver & 0xff, flags;
|
||||
@@ -273449,7 +273449,7 @@ Urboot_template **urboottemplate(const Avrintel *up, const char *mcu, const char
|
||||
features |= URFEATURE_U4;
|
||||
|
||||
ret[n]->features = features;
|
||||
urbootPutVersion(ret[n]->urversion, ret[n]->table);
|
||||
urbootPutVersion8(ret[n]->urversion, ret[n]->table);
|
||||
|
||||
if(features & URFEATURE_HW) {
|
||||
strcpy(ret[n]->type, "hardware-supported");
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
* Published under GNU General Public License, version 3 (GPL-3.0)
|
||||
* Meta-author Stefan Rueger <stefan.rueger@urclocks.com>
|
||||
*
|
||||
* v 1.3
|
||||
* 09.01.2026
|
||||
* v 1.31
|
||||
* 02.04.2026
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -345,15 +345,6 @@ static int nmeta(int mcode, int flashsize) {
|
||||
|
||||
// Need to know a bit about avr opcodes, in particular jmp and rjmp for patching vector table
|
||||
|
||||
#define ret_opcode 0x9508
|
||||
|
||||
|
||||
// Is the opcode an rjmp, ie, a relative jump [.-4096, .+4094]
|
||||
static int isRjmp(uint16_t opcode) {
|
||||
return (opcode & 0xf000) == 0xc000;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Map distances to [-flashsize/2, flashsize/2) for smaller devices. As rjmp can go +/- 4 kB, so
|
||||
* smaller flash than 8k (eg, 4k) benefit from wrap around logic.
|
||||
@@ -415,12 +406,6 @@ int addr_jmp(uint32_t jmp) {
|
||||
}
|
||||
|
||||
|
||||
// Is the instruction word the lower 16 bit part of a jmp instruction?
|
||||
static int isJmp(uint16_t opcode) {
|
||||
return (opcode & 0xfe0e) == 0x940c;
|
||||
}
|
||||
|
||||
|
||||
// Assemble little endian 32-bit word from buffer
|
||||
uint32_t buf2uint32(const unsigned char *buf) {
|
||||
return buf[0] | buf[1]<<8 | buf[2]<<16 | buf[3]<<24;
|
||||
@@ -488,18 +473,17 @@ static void set_date_filename(const PROGRAMMER *pgm, const char *fname) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Put destination address of reset vector jmp or rjmp into addr, return -1 if not an r/jmp
|
||||
static int reset2addr(const unsigned char *opcode, int vecsz, int flashsize, int *addrp) {
|
||||
int reset2addr(const unsigned char *opcode, int vecsz, int flashsize, int *addrp) {
|
||||
int op32, addr, rc = 0;
|
||||
uint16_t op16;
|
||||
|
||||
op16 = buf2uint16(opcode); // First word of the jmp or the full rjmp
|
||||
op32 = vecsz == 2? op16: buf2uint32(opcode);
|
||||
|
||||
if(vecsz == 4 && isJmp(op16)) {
|
||||
if(vecsz == 4 && isop(op16, jmp)) {
|
||||
addr = addr_jmp(op32); // Accept compiler's destination (do not normalise)
|
||||
} else if(isRjmp(op16)) { // rjmp might be generated for larger parts, too
|
||||
} else if(isop(op16, rjmp)) { // rjmp might be generated for larger parts, too
|
||||
addr = dist_rjmp(op16, flashsize);
|
||||
while(addr < 0) // If rjmp was backwards
|
||||
addr += flashsize; // OK for small parts, likely(!) OK if flashsize is a power of 2
|
||||
@@ -514,30 +498,29 @@ static int reset2addr(const unsigned char *opcode, int vecsz, int flashsize, int
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Can a rjmp at 0 reach the bootloader in a large part?
|
||||
static int rjmp_reaches_blstart(const PROGRAMMER *pgm) {
|
||||
if(ur.uP.flashsize & (ur.uP.flashsize-1)) // Only if flash is a power of 2
|
||||
// Can a rjmp at reset location 0 reach the bootloader in a large part?
|
||||
static int rjmp_reaches_blstart(int blstart, int flsize) {
|
||||
if(flsize & (flsize-1)) // No as flash is not a power of 2
|
||||
return 0;
|
||||
return ur.blstart <= 4096 || ur.blstart >= ur.uP.flashsize - 4094;
|
||||
return blstart <= 4096 || blstart >= flsize - 4094;
|
||||
}
|
||||
|
||||
// What reset looks like for vector bootloaders
|
||||
static int set_reset(const PROGRAMMER *pgm, unsigned char *jmptoboot, int vecsz) {
|
||||
int set_resetvector(int blstart, int flsize, uint8_t *reset, int vecsz, int isur) {
|
||||
// Small part or larger flash that is power or 2: urboot P reset vector protection uses this
|
||||
if(vecsz == 2 || rjmp_reaches_blstart(pgm)) {
|
||||
uint16tobuf(jmptoboot, rjmp_bwd_blstart(ur.blstart, ur.uP.flashsize));
|
||||
if(ur.urprotocol && vecsz == 4) {
|
||||
uint16tobuf(jmptoboot + 2, 0x7275 /* ur */);
|
||||
if(vecsz == 2 || rjmp_reaches_blstart(blstart, flsize)) {
|
||||
uint16tobuf(reset, rjmp_bwd_blstart(blstart, flsize));
|
||||
if(isur && vecsz == 4) {
|
||||
uint16tobuf(reset + 2, 0x7275 /* ur */);
|
||||
return 4;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
uint32tobuf(jmptoboot, jmp_opcode(ur.blstart));
|
||||
uint32tobuf(reset, jmp_opcode(blstart));
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
||||
// Called after the input file has been read for writing or verifying flash
|
||||
static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *flm,
|
||||
const char *fname, int size) { // Size is max memory address + 1
|
||||
@@ -628,7 +611,7 @@ static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p, const
|
||||
bool llvectors = firstbeg == 0 && firstlen >= ur.uP.ninterrupts*vecsz; // Looks like vector table
|
||||
for(int i=0; llvectors && i<ur.uP.ninterrupts*vecsz; i+=vecsz) {
|
||||
uint16_t op16 = buf2uint16(flm->buf+i);
|
||||
if(!isRjmp(op16) && !(vecsz == 4 && isJmp(op16)))
|
||||
if(!isop(op16, rjmp) && !(vecsz == 4 && isop(op16, jmp)))
|
||||
llvectors = 0;
|
||||
}
|
||||
|
||||
@@ -677,7 +660,7 @@ static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p, const
|
||||
}
|
||||
|
||||
// OK, now have bootloader start and application start: patch
|
||||
set_reset(pgm, flm->buf+0, vecsz);
|
||||
set_resetvector(ur.blstart, ur.uP.flashsize, flm->buf+0, vecsz, ur.urprotocol);
|
||||
if(vecsz == 4)
|
||||
uint32tobuf(flm->buf+appvecloc, jmp_opcode(appstart));
|
||||
else
|
||||
@@ -777,7 +760,7 @@ nopatch_nometa:
|
||||
// Reset vector not programmed? Or -F? Ensure a jmp to bootloader
|
||||
if(ovsigck || set != vecsz) {
|
||||
unsigned char jmptoboot[4];
|
||||
int resetsize = set_reset(pgm, jmptoboot, vecsz);
|
||||
int resetsize = set_resetvector(ur.blstart, ur.uP.flashsize, jmptoboot, vecsz, ur.urprotocol);
|
||||
|
||||
if(!ur.urprotocol || (ur.urfeatures & UB_READ_FLASH)) { // Flash readable?
|
||||
int resetdest;
|
||||
@@ -827,7 +810,7 @@ nopatch_nometa:
|
||||
|
||||
if(!ur.done_ce) { // Unless chip erase was just issued (where all mem is 0xff)
|
||||
if((ur.urprotocol && !(ur.urfeatures & UB_FLASH_LL_NOR)) || !ur.urprotocol) {
|
||||
// Scan the memory for eff pages with unset bytes and read these bytes from device flash
|
||||
// Scan memory for effective pages with unset bytes and read these bytes from device flash
|
||||
int ai, npe, addr, nset;
|
||||
|
||||
uint8_t spc[2048];
|
||||
@@ -907,7 +890,7 @@ nopatch_nometa:
|
||||
|
||||
|
||||
// Put version string into a buffer of max 19 characters incl nul (normally 15-16 bytes incl nul)
|
||||
static void urbootPutVersion(const PROGRAMMER *pgm, char *buf, uint16_t ver, uint16_t rjmpwp) {
|
||||
static void allbootPutVersion(const PROGRAMMER *pgm, char *buf, uint16_t ver, uint16_t rjmpwp) {
|
||||
uint8_t hi = ver>>8, type = ver & 0xff, flags;
|
||||
|
||||
if(ver == 0xffff) // Unknown provenance
|
||||
@@ -916,7 +899,7 @@ static void urbootPutVersion(const PROGRAMMER *pgm, char *buf, uint16_t ver, uin
|
||||
if(hi >= 072) { // These are urboot versions
|
||||
sprintf(buf, "u%d.%d ", hi>>3, hi&7);
|
||||
buf += strlen(buf);
|
||||
*buf++ = (hi < 077 && (type & UR_PGMWRITEPAGE)) || (hi >= 077 && rjmpwp != ret_opcode)? 'w': '-';
|
||||
*buf++ = (hi < 077 && (type & UR_PGMWRITEPAGE)) || (hi >= 077 && !isop(rjmpwp, ret))? 'w': '-';
|
||||
*buf++ = type & UR_EEPROM? 'e': '-';
|
||||
if(hi >= 076) {
|
||||
if(hi > 077) // From version 8.0 it's always urprotocol
|
||||
@@ -940,14 +923,18 @@ static void urbootPutVersion(const PROGRAMMER *pgm, char *buf, uint16_t ver, uin
|
||||
*buf = 0;
|
||||
} else if(hi) { // Version number in binary from optiboot v4.1
|
||||
sprintf(buf, "o%d.%d -%cs-%c-r--", hi, type,
|
||||
ur.blguessed? (ur.bleepromrw? 'e': '-'): '?',
|
||||
ur.blguessed? "hjvV"[ur.vbllevel & 3]: '?');
|
||||
pgm && ur.blguessed? (ur.bleepromrw? 'e': '-'): '?',
|
||||
pgm && ur.blguessed? "hjvV"[ur.vbllevel & 3]: '?');
|
||||
} else
|
||||
sprintf(buf, "x0.0 .........");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Puts urboot version and capabilities into 19+ byte buffer from table with top 6 bytes
|
||||
void urbootPutVersion(char *buf, uint16_t *top6table) {
|
||||
allbootPutVersion(NULL, buf, top6table[2], top6table[1]);
|
||||
}
|
||||
|
||||
// Return name of the vector with number num
|
||||
static const char *vblvecname(const PROGRAMMER *pgm, int num) {
|
||||
@@ -1340,7 +1327,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||
Return("please specify -x bootsize=<num> and, if needed, %s-x eepromrw",
|
||||
ur.boothigh? "-x vectornum=<num> or ": "");
|
||||
|
||||
uint16_t v16 = 0xffff, rjmpwp = ret_opcode;
|
||||
uint16_t v16 = 0xffff, rjmpwp = 0x9508; // 0x9508 is the ret opcode
|
||||
|
||||
// Sporting chance that we can read top flash to get intell about bootloader?
|
||||
if(ur.boothigh && (!ur.urprotocol || (ur.urfeatures & UB_READ_FLASH))) {
|
||||
@@ -1357,7 +1344,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||
v16 = buf2uint16(spc+4); // Combo word for neatly printed version line of urboot bootloader
|
||||
|
||||
// Extensively check this is an urboot bootloader v7.2 .. v12.7 == 0147 and extract properties
|
||||
if(urver >= 072 && urver <= 0147 && (isRjmp(rjmpwp) || rjmpwp == ret_opcode)) { // Prob urboot
|
||||
if(urver >= 072 && urver <= 0147 && (isop(rjmpwp, rjmp) || isop(rjmpwp, ret))) { // Prob urboot
|
||||
if(urver < 075) { // Early urboot versions don't offer many sanity checks
|
||||
ur.blurversion = urver;
|
||||
ur.bleepromrw = iseeprom_cap(cap);
|
||||
@@ -1369,7 +1356,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||
if(blsize >= 64 && blsize <= 2048 && vectnum <= ur.uP.ninterrupts) { // Within range
|
||||
int dfromend = dist_rjmp(rjmpwp, ur.uP.flashsize) - 4;
|
||||
// Further check whether writepage() rjmp opcode jumps backwards into bootloader
|
||||
if(rjmpwp == ret_opcode || (dfromend >= -blsize && dfromend < -6)) { // Due diligence
|
||||
if(isop(rjmpwp, ret) || (dfromend >= -blsize && dfromend < -6)) { // Due diligence
|
||||
if(ur.xbootsize) {
|
||||
if(flm->size - blsize != ur.blstart) {
|
||||
pmsg_warning("urboot bootloader size %d explicitly overwritten by -x bootsize=%d\n",
|
||||
@@ -1419,7 +1406,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||
|
||||
uint16_t reset16 = buf2uint16(spc);
|
||||
|
||||
if(isRjmp(reset16)) { // rjmp op code (could be from a large or a small part)
|
||||
if(isop(reset16, rjmp)) { // rjmp op code (could be from a large or a small part)
|
||||
if((flm->size & (flm->size-1)) == 0) { // Flash size a power of 2? True for small parts
|
||||
int guess = dist_rjmp(reset16, ur.uP.flashsize); // Relative destination to reset vector
|
||||
while(guess < 0) // Convert to absolute address
|
||||
@@ -1431,7 +1418,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||
ur.pfend = guess - 1;
|
||||
}
|
||||
}
|
||||
} else if(vecsz == 4 && isJmp(reset16)) { // Jmp op code
|
||||
} else if(vecsz == 4 && isop(reset16, jmp)) { // Jmp op code
|
||||
int guess = addr_jmp(buf2uint32(spc));
|
||||
if(guess < flm->size)
|
||||
if((guess & (flm->page_size-1)) == 0) // Page aligned? Good
|
||||
@@ -1469,14 +1456,14 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||
op16 = 0;
|
||||
} else if(wasop32) { // Skip opcode evaluation
|
||||
wasop32 = 0;
|
||||
} else if(isRjmp(opcode) && toend > 4) { // 4 top bytes of bl are data, not rjmp
|
||||
} else if(isop(opcode, rjmp) && toend > 4) { // 4 top bytes of bl are data, not rjmp
|
||||
// Does that rjmp end in the vector table?
|
||||
if((dist = dist_rjmp(opcode, ur.uP.flashsize)) > toend &&
|
||||
dist <= toend+ur.uP.ninterrupts*vecsz) { // "<=" for extended vector table
|
||||
ur.vblvectornum = (dist-toend)/vecsz; // Solve for the vbl vector number
|
||||
goto vblvecfound;
|
||||
}
|
||||
} else if(isJmp(opcode) && toend > 6) { // 4 top bytes are data + 2 the jmp addr
|
||||
} else if(isop(opcode, jmp) && toend > 6) { // 4 top bytes are data + 2 the jmp addr
|
||||
op16 = opcode;
|
||||
wasjmp = 1; // Look at destination address in next loop iteration
|
||||
} else if(is_opcode32(opcode)) { // Skip next opcode, too
|
||||
@@ -1502,7 +1489,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||
}
|
||||
|
||||
vblvecfound:
|
||||
urbootPutVersion(pgm, ur.desc, v16, rjmpwp);
|
||||
allbootPutVersion(pgm, ur.desc, v16, rjmpwp);
|
||||
|
||||
ur.mcode = 0xff;
|
||||
int havemetadata = !ur.nometadata;
|
||||
@@ -1696,7 +1683,7 @@ static int urclock_paged_rdwr(const PROGRAMMER *pgm, const AVRPART *part, char r
|
||||
if(badd < 4U && ur.boothigh && ur.blstart && ur.vbllevel == 1) {
|
||||
int vecsz = ur.uP.flashsize <= 8192? 2: 4;
|
||||
unsigned char jmptoboot[4];
|
||||
int resetsize = set_reset(pgm, jmptoboot, vecsz);
|
||||
int resetsize = set_resetvector(ur.blstart, ur.uP.flashsize, jmptoboot, vecsz, ur.urprotocol);
|
||||
|
||||
if(badd < (unsigned int) resetsize) { // Ensure reset vector points to bl
|
||||
int n = urmin((unsigned int) resetsize - badd, (unsigned int) len);
|
||||
@@ -2181,7 +2168,7 @@ static int urclock_chip_erase(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||
if(flm && flm->page_size >= vecsz) {
|
||||
unsigned char *page = mmt_malloc(flm->page_size);
|
||||
memset(page, 0xff, flm->page_size);
|
||||
set_reset(pgm, page, vecsz);
|
||||
set_resetvector(ur.blstart, ur.uP.flashsize, page, vecsz, ur.urprotocol);
|
||||
if(avr_write_page_default(pgm, p, flm, 0, page) < 0) {
|
||||
mmt_free(page);
|
||||
return -1;
|
||||
@@ -2367,7 +2354,7 @@ static int urclock_paged_load(const PROGRAMMER *pgm, const AVRPART *p, const AVR
|
||||
int vecsz = ur.uP.flashsize <= 8192? 2: 4;
|
||||
if(chunk == ur.uP.pagesize && ur.boothigh && ur.blstart && ur.vbllevel == 1) {
|
||||
unsigned char jmptoboot[4];
|
||||
int resetsize = set_reset(pgm, jmptoboot, vecsz);
|
||||
int resetsize = set_resetvector(ur.blstart, ur.uP.flashsize, jmptoboot, vecsz, ur.urprotocol);
|
||||
int resetdest;
|
||||
|
||||
if(reset2addr(m->buf, vecsz, ur.uP.flashsize, &resetdest) < 0 || resetdest != ur.blstart) {
|
||||
@@ -2444,16 +2431,10 @@ static int urclock_readonly(const PROGRAMMER *pgm, const AVRPART *p_unused, cons
|
||||
return 1;
|
||||
if(addr < (unsigned int) ur.pfstart)
|
||||
return 1;
|
||||
if(ur.boothigh && addr < 512 && ur.vbllevel) {
|
||||
unsigned int vecsz = ur.uP.flashsize <= 8192? 2u: 4u;
|
||||
if(addr < vecsz)
|
||||
// Protect reset vector once vector bootloader detected
|
||||
if(addr < 4 && ur.boothigh && ur.vblvectornum > 0)
|
||||
if(addr < (ur.uP.flashsize <= 8192? 2u: 4u))
|
||||
return 1;
|
||||
if(ur.vblvectornum > 0) {
|
||||
unsigned int appvecloc = ur.vblvectornum*vecsz;
|
||||
if(addr >= appvecloc && addr < appvecloc+vecsz)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else if(!mem_is_eeprom(mem))
|
||||
return 1;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user