mirror of
https://github.com/avrdudes/avrdude.git
synced 2026-06-02 09:46:34 +03:00
Patch sketches witch -c dryboot as with -c urclock -x nometadata
This commit is contained in:
223
src/dryrun.c
223
src/dryrun.c
@@ -62,9 +62,8 @@ typedef struct {
|
||||
int bootstart, bootsize; // Start and size of boot section (if any)
|
||||
int initialised; // 1 once the part memories are initialised
|
||||
struct {
|
||||
int vblvectornum, // Vector bootloader vector number for jump to application op code
|
||||
vbllevel, // 0=n/a, 1=patch externally, 2=bl patches, 3=bl patches & verifies
|
||||
blurversion; // Octal byte 076 means v7.6 (minor version number is lowest 3 bit)
|
||||
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;
|
||||
@@ -80,6 +79,215 @@ 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 < 512)
|
||||
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.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
|
||||
|
||||
// Sanity: no patching if bootloader location is unknown
|
||||
if(ur.blend <= ur.blstart)
|
||||
goto nopatch;
|
||||
|
||||
// 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 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__);
|
||||
@@ -523,6 +731,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));
|
||||
|
||||
@@ -1082,4 +1294,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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user