diff --git a/README.md b/README.md index e30965277..f6ae2f0ce 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,14 @@ If you would link your executable to `0x08000000` and then do then it would be written to the memory. +## Writing Option Bytes + +Example to read and write option bytes (currently writing only supported for STM32G0) +``` +./st-flash --debug --reset --format binary --flash=128k read option_bytes_dump.bin 0x1FFF7800 4 +./st-flash --debug --reset --format binary --flash=128k write option_bytes_dump.bin 0x1FFF7800 +``` + ## FAQ Q: My breakpoints do not work at all or only work once. diff --git a/include/stlink.h b/include/stlink.h index abacd127e..fedeb1bf4 100644 --- a/include/stlink.h +++ b/include/stlink.h @@ -11,6 +11,8 @@ #include #include +#include "stm32.h" + #ifdef __cplusplus extern "C" { #endif @@ -50,15 +52,6 @@ extern "C" { #define STLINK_DEBUG_APIV2_SWD_SET_FREQ 0x43 - /* cortex core ids */ - // TODO clean this up... -#define STM32VL_CORE_ID 0x1ba01477 -#define STM32F7_CORE_ID 0x5ba02477 - - // Constant STM32 memory map figures -#define STM32_FLASH_BASE 0x08000000 -#define STM32_SRAM_BASE 0x20000000 - // Baud rate divisors for SWDCLK #define STLINK_SWDCLK_4MHZ_DIVISOR 0 #define STLINK_SWDCLK_1P8MHZ_DIVISOR 1 @@ -73,8 +66,6 @@ extern "C" { #define STLINK_SWDCLK_15KHZ_DIVISOR 265 #define STLINK_SWDCLK_5KHZ_DIVISOR 798 - - /* Enough space to hold both a V2 command or a V1 command packaged as generic scsi*/ #define C_BUF_LEN 32 @@ -205,6 +196,7 @@ typedef struct flash_loader { uint8_t stlink_get_erased_pattern(stlink_t *sl); int stlink_mwrite_flash(stlink_t *sl, uint8_t* data, uint32_t length, stm32_addr_t addr); int stlink_fwrite_flash(stlink_t *sl, const char* path, stm32_addr_t addr); + int stlink_fwrite_option_bytes(stlink_t *sl, const char* path, stm32_addr_t addr); int stlink_mwrite_sram(stlink_t *sl, uint8_t* data, uint32_t length, stm32_addr_t addr); int stlink_fwrite_sram(stlink_t *sl, const char* path, stm32_addr_t addr); int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, uint32_t length); diff --git a/include/stm32.h b/include/stm32.h new file mode 100644 index 000000000..f5dbe4b2d --- /dev/null +++ b/include/stm32.h @@ -0,0 +1,19 @@ +/* + * File: stm32.h + * + * STM32 specific defines + */ + +#ifndef STM32_H +#define STM32_H + +// cortex core ids +#define STM32VL_CORE_ID 0x1ba01477 +#define STM32F7_CORE_ID 0x5ba02477 + +// Constant STM32 memory map figures +#define STM32_FLASH_BASE ((uint32_t)0x08000000) +#define STM32_SRAM_BASE ((uint32_t)0x20000000) +#define STM32_G0_OPTION_BYTES_BASE ((uint32_t)0x1FFF7800) + +#endif /* STM32_H */ diff --git a/src/common.c b/src/common.c index dab9a33c0..9bb2c2c4e 100644 --- a/src/common.c +++ b/src/common.c @@ -91,6 +91,20 @@ #define STM32G0_FLASH_PCROP1BER (STM32G0_FLASH_REGS_ADDR + 0x38) #define STM32G0_FLASH_SECR (STM32G0_FLASH_REGS_ADDR + 0x80) +// GO FLASH control register +#define STM32G0_FLASH_CR_PG 0 /* Program */ +#define STM32G0_FLASH_CR_PER 1 /* Page erase */ +#define STM32G0_FLASH_CR_MER1 2 /* Mass erase */ +#define STM32G0_FLASH_CR_PNB 3 /* Page number (5 bits) */ +#define STM32G0_FLASH_CR_STRT 16 /* Start */ +#define STM32G0_FLASH_CR_OPTSTRT 17 /* Start of modification of option bytes */ +#define STM32G0_FLASH_CR_FSTPG 18 /* Fast programming */ +#define STM32G0_FLASH_CR_EOPIE 24 /* End of operation interrupt enable */ +#define STM32G0_FLASH_CR_ERRIE 25 /* Error interrupt enable */ +#define STM32G0_FLASH_CR_OBL_LAUNCH 27 /* Forces the option byte loading */ +#define STM32G0_FLASH_CR_OPTLOCK 30 /* Options Lock */ +#define STM32G0_FLASH_CR_LOCK 31 /* FLASH_CR Lock*/ + //32L4 register base is at FLASH_REGS_ADDR (0x40022000) #define STM32L4_FLASH_KEYR (FLASH_REGS_ADDR + 0x08) #define STM32L4_FLASH_SR (FLASH_REGS_ADDR + 0x10) @@ -2407,3 +2421,117 @@ int stlink_fwrite_flash(stlink_t *sl, const char* path, stm32_addr_t addr) { unmap_file(&mf); return err; } + +/** + * Write option bytes + * @param sl + * @param addr of the memory mapped option bytes + * @param base option bytes to write + * @return 0 on success, -ve on failure. + */ +int stlink_write_option_bytes(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t len) { + + uint32_t val; + + if(len != 4) { + ELOG("Wrong length for writting option bytes, must be 4 is %d\n", len); + return -1; + } + + // Make sure we've loaded the context with the chip details + stlink_core_id(sl); + + /* Check if chip is supported and for correct address */ + if((sl->chip_id != STLINK_CHIPID_STM32_G0X1) || (addr != STM32_G0_OPTION_BYTES_BASE)) { + ELOG("Option bytes writing is currently only supported for the STM32G0\n"); + return -1; + } + + /* Unlock flash if necessary (ref manuel page 52) */ + stlink_read_debug32(sl, STM32G0_FLASH_CR, &val); + if ((val & (1 << STM32G0_FLASH_CR_LOCK))) { + + /* disable flash write protection. */ + stlink_write_debug32(sl, STM32G0_FLASH_KEYR, 0x45670123); + stlink_write_debug32(sl, STM32G0_FLASH_KEYR, 0xCDEF89AB); + + // check that the lock is no longer set. + stlink_read_debug32(sl, STM32G0_FLASH_CR, &val); + if ((val & (1 << STM32G0_FLASH_CR_LOCK))) { + ELOG("Flash unlock failed! System reset required to be able to unlock it again!\n"); + return -1; + } + } + + /* Unlock option bytes if necessary (ref manuel page 61) */ + stlink_read_debug32(sl, STM32G0_FLASH_CR, &val); + if ((val & (1 << STM32G0_FLASH_CR_OPTLOCK))) { + + /* disable option byte write protection. */ + stlink_write_debug32(sl, STM32G0_FLASH_OPTKEYR, 0x08192A3B); + stlink_write_debug32(sl, STM32G0_FLASH_OPTKEYR, 0x4C5D6E7F); + + /* check that the lock is no longer set. */ + stlink_read_debug32(sl, STM32G0_FLASH_CR, &val); + if ((val & (1 << STM32G0_FLASH_CR_OPTLOCK))) { + ELOG("Options bytes unlock failed! System reset required to be able to unlock it again!\n"); + return -1; + } + } + + /* Write options bytes */ + uint32_t data; + write_uint32((unsigned char*) &data, *(uint32_t*) (base)); + WLOG("Writing option bytes 0x%04x\n", data); + //stlink_write_debug32(sl, addr, data); + stlink_write_debug32(sl, STM32G0_FLASH_OPTR, data); + + /* Set Options Start bit */ + stlink_read_debug32(sl, STM32G0_FLASH_CR, &val); + val |= (1 << STM32G0_FLASH_CR_OPTSTRT); + stlink_write_debug32(sl, STM32G0_FLASH_CR, val); + + /* Wait for 'busy' bit in FLASH_SR to clear. */ + do { + stlink_read_debug32(sl, STM32G0_FLASH_SR, &val); + } while ((val & (1 << 16)) != 0); + + /* apply options bytes immediate */ + stlink_read_debug32(sl, STM32G0_FLASH_CR, &val); + val |= (1 << STM32G0_FLASH_CR_OBL_LAUNCH); + stlink_write_debug32(sl, STM32G0_FLASH_CR, val); + + /* Re-lock option bytes */ + stlink_read_debug32(sl, STM32G0_FLASH_CR, &val); + val |= (1 << STM32G0_FLASH_CR_OPTLOCK); + stlink_write_debug32(sl, STM32G0_FLASH_CR, val); + /* Re-lock flash. */ + stlink_read_debug32(sl, STM32G0_FLASH_CR, &val); + val |= (1 << STM32G0_FLASH_CR_LOCK); + stlink_write_debug32(sl, STM32G0_FLASH_CR, val); + + return 0; +} + +/** + * Write the given binary file with option bytes + * @param sl + * @param path readable file path, should be binary image + * @param addr of the memory mapped option bytes + * @return 0 on success, -ve on failure. + */ +int stlink_fwrite_option_bytes(stlink_t *sl, const char* path, stm32_addr_t addr) { + /* write the file in flash at addr */ + int err; + mapped_file_t mf = MAPPED_FILE_INITIALIZER; + + if (map_file(&mf, path) == -1) { + ELOG("map_file() == -1\n"); + return -1; + } + + err = stlink_write_option_bytes(sl, addr, mf.base, (uint32_t) mf.len); + stlink_fwrite_finalize(sl, addr); + unmap_file(&mf); + return err; +} diff --git a/src/tools/flash.c b/src/tools/flash.c index 508c4aa84..768e9834f 100644 --- a/src/tools/flash.c +++ b/src/tools/flash.c @@ -162,6 +162,14 @@ int main(int ac, char** av) goto on_error; } } + else if (o.addr == STM32_G0_OPTION_BYTES_BASE) { + err = stlink_fwrite_option_bytes(sl, o.filename, o.addr); + if (err == -1) + { + printf("stlink_fwrite_option_bytes() == -1\n"); + goto on_error; + } + } else { err = -1; printf("Unknown memory region\n");