From f4952aaa63bc127bef46cd92d04fa7013942b107 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Mon, 26 Oct 2020 18:54:00 +0100 Subject: [PATCH] Implement a virtual bootloader section - patch reset vector while programming flash page0 Let the reset vector always point to the twiboot start. - Use another (must be unused!) ISR vector to store the original reset vector as jump to the application. - Cache the values for the verification read - currently works only for devices < 8kB flash (2 byte vector entries) - using a attiny85 as target This change is heavily based on the optiboot bootloader. --- Makefile | 2 +- main.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 0233b45..2646923 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ AVRDUDE_MCU=t85 AVRDUDE_FUSES=lfuse:w:0xe2:m hfuse:w:0xdd:m efuse:w:0xfe:m BOOTLOADER_START=0x1C00 -CFLAGS_TARGET=-DUSE_CLOCKSTRETCH=1 +CFLAGS_TARGET=-DUSE_CLOCKSTRETCH=1 -DVIRTUAL_BOOT_SECTION=1 endif # --------------------------------------------------------------------------- diff --git a/main.c b/main.c index f51e3a3..1ec8249 100644 --- a/main.c +++ b/main.c @@ -29,6 +29,10 @@ #define USE_CLOCKSTRETCH 0 #endif +#ifndef VIRTUAL_BOOT_SECTION +#define VIRTUAL_BOOT_SECTION 0 +#endif + #ifndef TWI_ADDRESS #define TWI_ADDRESS 0x29 #endif @@ -86,6 +90,23 @@ #define USI_ENABLE_SCL_HOLD 0x40 /* Hold SCL low after clock overflow */ #endif /* !defined(TWCR) && defined(USICR) */ +#if (VIRTUAL_BOOT_SECTION) +/* unused vector to store application start address */ +#define APPVECT_NUM EE_RDY_vect_num + +/* each vector table entry is a 2byte RJMP opcode */ +#define RSTVECT_ADDR 0x0000 +#define APPVECT_ADDR (APPVECT_NUM * 2) +#define RSTVECT_PAGE_OFFSET (RSTVECT_ADDR % SPM_PAGESIZE) +#define APPVECT_PAGE_OFFSET (APPVECT_ADDR % SPM_PAGESIZE) + +/* create RJMP opcode for the vector table */ +#define OPCODE_RJMP(addr) (((addr) & 0x0FFF) | 0xC000) + +#elif (!defined(ASRE) && !defined (RWWSRE)) +#error "Device without bootloader section requires VIRTUAL_BOOT_SECTION" +#endif + /* SLA+R */ #define CMD_WAIT 0x00 #define CMD_READ_VERSION 0x01 @@ -165,6 +186,12 @@ static uint8_t cmd = CMD_WAIT; static uint8_t buf[SPM_PAGESIZE]; static uint16_t addr; +#if (VIRTUAL_BOOT_SECTION) +/* reset/application vectors received from host, needed for verify read */ +static uint8_t rstvect_save[2]; +static uint8_t appvect_save[2]; +#endif /* (VIRTUAL_BOOT_SECTION) */ + /* ************************************************************************* * write_flash_page * ************************************************************************* */ @@ -174,6 +201,29 @@ static void write_flash_page(void) uint8_t size = SPM_PAGESIZE; uint8_t *p = buf; +#if (VIRTUAL_BOOT_SECTION) + if (pagestart == (RSTVECT_ADDR & ~(SPM_PAGESIZE -1))) + { + /* save original vectors for verify read */ + rstvect_save[0] = buf[RSTVECT_PAGE_OFFSET]; + rstvect_save[1] = buf[RSTVECT_PAGE_OFFSET + 1]; + appvect_save[0] = buf[APPVECT_PAGE_OFFSET]; + appvect_save[1] = buf[APPVECT_PAGE_OFFSET + 1]; + + /* replace reset vector with jump to bootloader address */ + uint16_t rst_vector = OPCODE_RJMP(BOOTLOADER_START -1); + buf[RSTVECT_PAGE_OFFSET] = (rst_vector & 0xFF); + buf[RSTVECT_PAGE_OFFSET + 1] = (rst_vector >> 8) & 0xFF; + + /* replace application vector with jump to original reset vector */ + uint16_t app_vector = rstvect_save[0] | (rstvect_save[1] << 8); + app_vector = OPCODE_RJMP(app_vector - APPVECT_NUM); + + buf[APPVECT_PAGE_OFFSET] = (app_vector & 0xFF); + buf[APPVECT_PAGE_OFFSET + 1] = (app_vector >> 8) & 0xFF; + } +#endif /* (VIRTUAL_BOOT_SECTION) */ + if (pagestart < BOOTLOADER_START) { boot_page_erase(pagestart); @@ -399,7 +449,33 @@ static uint8_t TWI_data_read(uint8_t bcnt) break; case CMD_ACCESS_FLASH: - data = pgm_read_byte_near(addr++); + switch (addr) + { +/* return cached values for verify read */ +#if (VIRTUAL_BOOT_SECTION) + case RSTVECT_ADDR: + data = rstvect_save[0]; + break; + + case (RSTVECT_ADDR + 1): + data = rstvect_save[1]; + break; + + case APPVECT_ADDR: + data = appvect_save[0]; + break; + + case (APPVECT_ADDR + 1): + data = appvect_save[1]; + break; +#endif /* (VIRTUAL_BOOT_SECTION) */ + + default: + data = pgm_read_byte_near(addr); + break; + } + + addr++; break; #if (EEPROM_SUPPORT) @@ -680,7 +756,11 @@ static void TIMER0_OVF_vect(void) } /* TIMER0_OVF_vect */ -static void (*jump_to_app)(void) __attribute__ ((noreturn)) = 0x0000; +#if (VIRTUAL_BOOT_SECTION) +static void (*jump_to_app)(void) __attribute__ ((noreturn)) = (void*)APPVECT_ADDR; +#else +static void (*jump_to_app)(void) __attribute__ ((noreturn)) = (void*)0x0000; +#endif /* ************************************************************************* @@ -731,6 +811,14 @@ int main(void) LED_INIT(); LED_GN_ON(); +#if (VIRTUAL_BOOT_SECTION) + /* load current values (for reading flash) */ + rstvect_save[0] = pgm_read_byte_near(RSTVECT_ADDR); + rstvect_save[1] = pgm_read_byte_near(RSTVECT_ADDR + 1); + appvect_save[0] = pgm_read_byte_near(APPVECT_ADDR); + appvect_save[1] = pgm_read_byte_near(APPVECT_ADDR + 1); +#endif /* (VIRTUAL_BOOT_SECTION) */ + /* timer0: running with F_CPU/1024 */ #if defined (TCCR0) TCCR0 = (1<