/* Flash geometry and jump sequence derived from climate's F407 bootloader. * Sector 3 is a durable commit record; sectors 4..7 hold the application. * Reset checks vectors like climate; USB END always verifies Flash. */ #include "boot_port.h" #include "boot_image.h" #include "stm32f4xx_hal.h" #include #define META 0x0800C000UL #define COMMIT 0x53455442UL #define ERRORS (FLASH_SR_PGSERR|FLASH_SR_PGPERR|FLASH_SR_PGAERR|FLASH_SR_WRPERR|FLASH_SR_SOP) static void unlock(void) { if(FLASH->CR & FLASH_CR_LOCK) { FLASH->KEYR=0x45670123; FLASH->KEYR=0xCDEF89AB; } FLASH->SR=ERRORS; } static bool finish(void) { while(FLASH->SR & FLASH_SR_BSY) {} return !(FLASH->SR & ERRORS); } static void cache_reset(void) { FLASH->ACR &= ~(FLASH_ACR_ICEN|FLASH_ACR_DCEN); FLASH->ACR |= FLASH_ACR_ICRST|FLASH_ACR_DCRST; FLASH->ACR &= ~(FLASH_ACR_ICRST|FLASH_ACR_DCRST); FLASH->ACR |= FLASH_ACR_ICEN|FLASH_ACR_DCEN; __DSB(); __ISB(); } static bool erase_sector(unsigned sector) { bool ok; unlock(); FLASH->CR=(FLASH->CR & ~(FLASH_CR_SNB|FLASH_CR_PSIZE)) | FLASH_CR_SER | (2UL<CR|=FLASH_CR_STRT; ok=finish(); FLASH->CR &= ~FLASH_CR_SER; FLASH->CR |= FLASH_CR_LOCK; cache_reset(); return ok; } static bool program(uint32_t address, const uint8_t *p, uint32_t n) { bool ok=true; unlock(); /* Byte programming supports arbitrary SET block boundaries and final tails. */ FLASH->CR=(FLASH->CR & ~FLASH_CR_PSIZE)|FLASH_CR_PG; while(n--) { *(volatile uint8_t *)address=*p; if(!finish()) { ok=false; break; } ++address; ++p; } FLASH->CR &= ~FLASH_CR_PG; FLASH->CR |= FLASH_CR_LOCK; cache_reset(); return ok; } static bool invalidate(void *u) { (void)u; return erase_sector(3); } static bool erase(void *u, uint32_t size) { unsigned s; (void)u; if(size<8 || size>F407_APP_SIZE) return false; for(s=4;s<=7;++s) { uint32_t start=s==4 ? F407_APP_BASE : 0x08020000UL+(s-5)*0x20000UL; if(start>=F407_APP_BASE+size) break; if(!erase_sector(s)) return false; } return true; } static bool write(void *u,uint32_t off,const uint8_t *p,uint16_t n) { (void)u; if(!n || off>F407_APP_SIZE || n>F407_APP_SIZE-off) return false; return program(F407_APP_BASE+off,p,n) && !memcmp((const void *)(F407_APP_BASE+off),p,n); } static bool verify(void *u,uint32_t size,uint32_t crc) { (void)u; return f407_vectors_valid((const uint8_t *)F407_APP_BASE,size) && setp_crc32((const uint8_t *)F407_APP_BASE,size)==crc; } static bool commit(void *u,uint32_t size,uint32_t crc) { uint32_t record[4]={size,crc,~size,COMMIT}; (void)u; return program(META,(const uint8_t *)record,sizeof(record)) && !memcmp((const void *)META,record,sizeof(record)); } bool f407_app_valid(void) { /* Like climate: direct Keil/SWD writes need no USB completion record. */ return f407_vectors_valid((const uint8_t *)F407_APP_BASE,F407_APP_SIZE); } static void activate(void *u) { (void)u; /* main waits for USB response before reset */ } const setp_boot_ops f407_boot_ops={invalidate,erase,write,verify,commit,activate}; __attribute__((naked,noreturn)) static void enter(uint32_t sp,uint32_t pc) { __asm volatile("msr msp, r0\nmovs r2, #0\nmsr control, r2\ndsb\nisb\ncpsie i\nbx r1\n"); } void f407_jump_app(void) { unsigned i; uint32_t sp=*(const uint32_t *)F407_APP_BASE; uint32_t pc=*(const uint32_t *)(F407_APP_BASE+4); __disable_irq(); SysTick->CTRL=0; for(i=0;i<8;++i) { NVIC->ICER[i]=0xffffffff; NVIC->ICPR[i]=0xffffffff; } SCB->ICSR=SCB_ICSR_PENDSTCLR_Msk|SCB_ICSR_PENDSVCLR_Msk; SCB->VTOR=F407_APP_BASE; enter(sp,pc); }