vfdboard/bootloader/updater/updater.c

371 lines
11 KiB
C

#ifndef F_CPU
#define F_CPU 1000000UL /* 1 Mhz-Takt; hier richtigen Wert eintragen */
#endif
#include "../misc/iofixes.h"
#include "../firmware/spminterface.h"
#include "usbasploader.h"
// activate updaters full set of features
#ifndef CONFIG_UPDATER_REDUCEWRITES
#define CONFIG_UPDATER_REDUCEWRITES
#endif
#ifndef CONFIG_UPDATER_CLEANMEMCLEAR
#define CONFIG_UPDATER_CLEANMEMCLEAR
#endif
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>
#include <stdint.h>
#include <util/delay.h>
#include <string.h>
#define updater_pagefillcode ((1<<SPMEN))
#define updater_pageerasecode ((1<<PGERS) | (1<<SPMEN))
#define updater_pagewritecode ((1<<PGWRT) | (1<<SPMEN))
#include "../firmware/bootloaderconfig.h"
#if !HAVE_SPMINTEREFACE
#error "bootloader does not support updating itself! (HAVE_SPMINTEREFACE)"
#endif
// helpful definitions and makros ////
#ifndef NEW_BOOTLOADER_ADDRESS
#error "where should the new bootloader be positioned?"
#endif
#if (NEW_BOOTLOADER_ADDRESS != (funcaddr___bootloader__do_spm-(funcaddr___bootloader__do_spm % SPM_PAGESIZE)))
#warning the new bootloader seems to be located elsewhere, than the current one!
#endif
#ifndef NEW_SPM_ADDRESS
#warning I do not know where new "bootloader__do_spm" is located - assuming "NEW_BOOTLOADER_ADDRESS+(funcaddr___bootloader__do_spm % SPM_PAGESIZE)"
#define NEW_SPM_ADDRESS (NEW_BOOTLOADER_ADDRESS+(funcaddr___bootloader__do_spm % SPM_PAGESIZE))
#endif
// TEMP_SPM supports up to 4 pages for "bootloader__do_spm"...
#define TEMP_SPM_NUMPAGE 4
#define TEMP_SPM_BLKSIZE (TEMP_SPM_NUMPAGE*SPM_PAGESIZE)
#ifndef TEMP_SPM_PAGEADR
#warning "TEMP_SPM_PAGEADR" is not defined explicitly - will choose END OF FLASH !
#define TEMP_SPM_PAGEADR ((FLASHEND - TEMP_SPM_BLKSIZE)+1)
#endif
#define TEMP_SPM_ADDRESS ((TEMP_SPM_PAGEADR) + (funcaddr___bootloader__do_spm % SPM_PAGESIZE))
#if (NEW_SPM_ADDRESS == funcaddr___bootloader__do_spm)
#define new_do_spm do_spm
#else
void new_do_spm(const uint32_t flash_byteaddress, const uint8_t spmcrval, const uint16_t dataword) {
__do_spm_Ex(flash_byteaddress, spmcrval, dataword, NEW_SPM_ADDRESS >> 1);
}
#endif
void temp_do_spm(const uint32_t flash_byteaddress, const uint8_t spmcrval, const uint16_t dataword) {
__do_spm_Ex(flash_byteaddress, spmcrval, dataword, TEMP_SPM_ADDRESS >> 1);
}
// some important consistency checks ////
//check if "NEW_BOOTLOADER_ADDRESS" is page-aligned
#if (NEW_BOOTLOADER_ADDRESS % SPM_PAGESIZE != 0)
#error "NEW_BOOTLOADER_ADDRESS" is not aligned to pages!
#endif
//check, if NEW_SPM_ADDRESS is an even address
#if ((NEW_SPM_ADDRESS % 2) != 0)
#error NEW_SPM_ADDRESS must be an even address, since it contains executable code!
#endif
//check, if TEMP_SPM somehow overlaps with old SPM
#if (((TEMP_SPM_ADDRESS + TEMP_SPM_BLKSIZE + SPM_PAGESIZE) >= funcaddr___bootloader__do_spm) && (TEMP_SPM_ADDRESS <= (funcaddr___bootloader__do_spm + TEMP_SPM_BLKSIZE + SPM_PAGESIZE)))
#error TEMP_SPM_ADDRESS overlaps "funcaddr___bootloader__do_spm"!
#endif
//check, if TEMP_SPM somehow overlaps with new SPM
#if (((TEMP_SPM_ADDRESS + TEMP_SPM_BLKSIZE + SPM_PAGESIZE) >= NEW_SPM_ADDRESS) && (TEMP_SPM_ADDRESS <= (NEW_SPM_ADDRESS + TEMP_SPM_BLKSIZE + SPM_PAGESIZE)))
#error TEMP_SPM_ADDRESS overlaps "NEW_SPM_ADDRESS"!
#endif
//check, if TEMP_SPM_ADDRESS is an even address
#if ((TEMP_SPM_ADDRESS % 2) != 0)
#error TEMP_SPM_ADDRESS must be an even address, since it contains executable code!
#endif
//check, if TEMP_SPM_ADDRESS fits into flash
#if ((TEMP_SPM_PAGEADR + TEMP_SPM_BLKSIZE) > (FLASHEND+1))
#error TEMP_SPM_ADDRESS exceeds flashend!
#endif
//check if size too low
#if (SIZEOF_new_firmware <= (TEMP_SPM_BLKSIZE + (NEW_SPM_ADDRESS - NEW_BOOTLOADER_ADDRESS)))
#error empty firmware!
#endif
//check if size too high
#if (SIZEOF_new_firmware > ((FLASHEND+1)-NEW_BOOTLOADER_ADDRESS))
#error firmware too big! firmware does not fit into flash memory!
#endif
// main code ////
/*
* in this case a near address
*/
typedef uint32_t mypgm_addr_t;
typedef void (*mypgm_spminterface)(const uint32_t flash_byteaddress, const uint8_t spmcrval, const uint16_t dataword);
#if FLASHEND > 65535
# define FULLCORRECTFLASHADDRESS(addr) (((mypgm_addr_t)(addr)) | (((mypgm_addr_t)FLASHADDRESS) & ((mypgm_addr_t)0xffff0000)))
# define mymemcpy_PF mymemcpy_PF_far
void *mymemcpy_PF_far (void *dest, mypgm_addr_t src, size_t n) {
uint8_t *pagedata = (void*)dest;
mypgm_addr_t pageaddr = src;
size_t i;
for (i=0;i<n;i+=1) {
pagedata[i]=pgm_read_byte_far(pageaddr);
pageaddr+=1;
}
return dest;
}
#else
# define FULLCORRECTFLASHADDRESS(addr) (addr)
# define mymemcpy_PF memcpy_PF
#endif
#if 0
size_t mypgm_readpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize) {
size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
size_t pagesize = result >> 1;
uint16_t *pagedata = (void*)buffer;
mypgm_addr_t pageaddr = byteaddress - (byteaddress % SPM_PAGESIZE);
size_t i;
for (i=0;i<pagesize;i+=1) {
pagedata[i]=pgm_read_word_far(pageaddr);
pageaddr+=2;
}
return result;
}
#else
// replace it somehow with "memcpy_PF" in order to save some ops...
size_t mypgm_readpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize) {
size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
mypgm_addr_t pageaddr = byteaddress - (byteaddress % SPM_PAGESIZE);
mymemcpy_PF((void*)buffer, (uint_farptr_t)pageaddr, result);
return result;
}
#endif
#ifdef CONFIG_UPDATER_REDUCEWRITES
size_t mypgm_WRITEpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize, mypgm_spminterface spmfunc) {
size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
size_t pagesize = result >> 1;
uint16_t *pagedata = (void*)buffer;
mypgm_addr_t pageaddr_bakup = byteaddress - (byteaddress % SPM_PAGESIZE);
mypgm_addr_t pageaddr = pageaddr_bakup;
uint8_t changed=0, needs_erase=0;
uint16_t deeword;
size_t i;
// just check, if page needs a rewrite or an erase...
for (i=0;i<pagesize;i+=1) {
#if (FLASHEND > 65535)
deeword=pgm_read_word_far(pageaddr);
#else
deeword=pgm_read_word(pageaddr);
#endif
if (deeword != pagedata[i]) changed=1;
/*
* deeword = x
* buffer = y
*
* 1 ? 1 ==> 1
* 1 ? 0 ==> 1
* 0 ? 1 ==> 0
* 0 ? 0 ==> 1
*
* ==> /(/x * y) ==> x + /y
*/
deeword |= ~pagedata[i];
if ((~deeword) != 0) needs_erase=1;
pageaddr+=2;
}
if (changed) {
if (needs_erase) {
//do a page-erase, ATTANTION: flash only can be erased a limited number of times !
spmfunc(pageaddr_bakup, updater_pageerasecode, 0);
}
// from now on, the page is assumed empty !! (hopefully our code is located somewhere else!)
// now, fill the tempoary buffer
// ATTANTION: see comment on "do_spm" !
pageaddr = pageaddr_bakup;
for (i=0;i<pagesize;i+=1) {
spmfunc(pageaddr, updater_pagefillcode, pagedata[i]);
pageaddr+=2;
}
// so, now finally write the page to the FLASH
spmfunc(pageaddr_bakup, updater_pagewritecode, 0);
} else {
// no change - no write...
result = 0;
}
return result;
}
#else
size_t mypgm_WRITEpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize, mypgm_spminterface spmfunc) {
size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
size_t pagesize = result >> 1;
uint16_t *pagedata = (void*)buffer;
mypgm_addr_t pageaddr_bakup = byteaddress - (byteaddress % SPM_PAGESIZE);
mypgm_addr_t pageaddr = pageaddr_bakup;
size_t i;
//do a page-erase, ATTANTION: flash only can be erased a limited number of times !
spmfunc(pageaddr_bakup, updater_pageerasecode, 0);
// from now on, the page is assumed empty !! (hopefully our code is located somewhere else!)
// now, fill the tempoary buffer
// ATTANTION: see comment on "do_spm" !
pageaddr = pageaddr_bakup;
for (i=0;i<pagesize;i+=1) {
spmfunc(pageaddr, updater_pagefillcode, pagedata[i]);
pageaddr+=2;
}
// so, now finally write the page to the FLASH
spmfunc(pageaddr_bakup, updater_pagewritecode, 0);
return result;
}
#endif
#if defined(UPDATECRC32)
#include "crccheck.c"
#endif
// #pragma GCC diagnostic ignored "-Wno-pointer-to-int-cast"
int main(void)
{
#if defined(UPDATECRC32)
uint32_t crcval;
#endif
size_t i;
uint8_t buffer[SPM_PAGESIZE];
MCUCSR = 0; /* do not care about previous reset - just disable the wdt */
wdt_disable();
cli();
#if defined(UPDATECRC32)
// check if new firmware-image is corrupted
crcval = D_32;
for (i=0;i<SIZEOF_new_firmware;i+=1) {
#if (FLASHEND > 65535)
crcval = update_crc_32(crcval, pgm_read_byte_far(FULLCORRECTFLASHADDRESS(&new_firmware[i])));
#else
crcval = update_crc_32(crcval, pgm_read_byte(FULLCORRECTFLASHADDRESS(&new_firmware[i])));
#endif
}
crcval ^= D_32;
// allow to change the firmware
if (crcval == ((uint32_t)UPDATECRC32)) {
#endif
// check if firmware would change...
buffer[0]=0;
for (i=0;i<SIZEOF_new_firmware;i+=2) {
uint16_t a, b;
#if (FLASHEND > 65535)
a=pgm_read_word_far(FULLCORRECTFLASHADDRESS(&new_firmware[i]));
b=pgm_read_word_far(NEW_BOOTLOADER_ADDRESS+i);
#else
a=pgm_read_word(FULLCORRECTFLASHADDRESS(&new_firmware[i]));
b=pgm_read_word(NEW_BOOTLOADER_ADDRESS+i);
#endif
if (a!=b) {
buffer[0]=1;
break;
}
}
// need to change the firmware...
if (buffer[0]) {
// A
// copy the current "bootloader__do_spm" to tempoary position via std. "bootloader__do_spm"
for (i=0;i<TEMP_SPM_BLKSIZE;i+=SPM_PAGESIZE) {
mypgm_WRITEpage(TEMP_SPM_PAGEADR+i, buffer, mypgm_readpage(funcaddr___bootloader__do_spm+i, buffer, sizeof(buffer)), do_spm);
}
// B
// start updating the firmware to "NEW_BOOTLOADER_ADDRESS" until at least "TEMP_SPM_BLKSIZE"-bytes after "NEW_SPM_ADDRESS" were written
// therefore use the tempoary "bootloader__do_spm" (since we most probably will overwrite the default do_spm)
for (i=0;;i+=SPM_PAGESIZE) {
#ifdef CONFIG_UPDATER_CLEANMEMCLEAR
memset((void*)buffer, 0xff, sizeof(buffer));
#endif
mymemcpy_PF((void*)buffer, (uint_farptr_t)(FULLCORRECTFLASHADDRESS(&new_firmware[i])), ((SIZEOF_new_firmware-i)>sizeof(buffer))?sizeof(buffer):(SIZEOF_new_firmware-i));
mypgm_WRITEpage(NEW_BOOTLOADER_ADDRESS+i, buffer, sizeof(buffer), temp_do_spm);
if ((NEW_BOOTLOADER_ADDRESS+i) > (NEW_SPM_ADDRESS+TEMP_SPM_BLKSIZE)) break;
}
// C
// continue writeing the new_firmware after "NEW_SPM_ADDRESS+TEMP_SPM_BLKSIZE" this time use the "new_do_spm"
for (;i<SIZEOF_new_firmware;i+=SPM_PAGESIZE) {
#ifdef CONFIG_UPDATER_CLEANMEMCLEAR
memset((void*)buffer, 0xff, sizeof(buffer));
#endif
mymemcpy_PF((void*)buffer, (uint_farptr_t)(FULLCORRECTFLASHADDRESS(&new_firmware[i])), ((SIZEOF_new_firmware-i)>sizeof(buffer))?sizeof(buffer):(SIZEOF_new_firmware-i));
mypgm_WRITEpage(NEW_BOOTLOADER_ADDRESS+i, buffer, sizeof(buffer), new_do_spm);
}
}
#if defined(UPDATECRC32)
}
#endif
return 0;
}