summaryrefslogtreecommitdiff
path: root/src/stlink-lib/option_bytes.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stlink-lib/option_bytes.c')
-rw-r--r--src/stlink-lib/option_bytes.c1180
1 files changed, 1180 insertions, 0 deletions
diff --git a/src/stlink-lib/option_bytes.c b/src/stlink-lib/option_bytes.c
new file mode 100644
index 0000000..d49c346
--- /dev/null
+++ b/src/stlink-lib/option_bytes.c
@@ -0,0 +1,1180 @@
+/*
+ * File: option_bytes.c
+ *
+ * Read and write option bytes and option control registers
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <stlink.h>
+#include "option_bytes.h"
+
+#include "common_flash.h"
+#include "flash_loader.h"
+#include "logging.h"
+#include "map_file.h"
+#include "md5.h"
+#include "read_write.h"
+
+/**
+ * Read option control register C0
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_read_option_control_register_c0(stlink_t *sl, uint32_t *option_byte) {
+ return stlink_read_debug32(sl, FLASH_C0_OPTR, option_byte);
+}
+
+/**
+ * Read option bytes C0
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_read_option_bytes_c0(stlink_t *sl, uint32_t *option_byte) {
+ return stlink_read_option_control_register_c0(sl, option_byte);
+}
+
+/**
+ * Write option control register C0
+ * @param sl
+ * @param option_cr
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_control_register_c0(stlink_t *sl, uint32_t option_cr) {
+ int32_t ret = 0;
+
+ clear_flash_error(sl);
+
+ if ((ret = stlink_write_debug32(sl, FLASH_C0_OPTR, option_cr)))
+ return ret;
+
+ wait_flash_busy(sl);
+
+ uint32_t cr_reg = (1 << FLASH_C0_CR_OPTSTRT);
+ if ((ret = stlink_write_debug32(sl, FLASH_C0_CR, cr_reg)))
+ return ret;
+
+ wait_flash_busy(sl);
+
+ if ((ret = check_flash_error(sl)))
+ return ret;
+
+ // trigger the load of option bytes into option registers
+ cr_reg = (1 << FLASH_C0_CR_OBL_LAUNCH);
+ stlink_write_debug32(sl, FLASH_C0_CR, cr_reg);
+
+ return ret;
+}
+
+/**
+ * Write option bytes C0
+ * @param sl
+ * @param addr of the memory mapped option bytes
+ * @param base option bytes
+ * @param len of option bytes
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_bytes_c0(stlink_t *sl, stm32_addr_t addr, uint8_t *base, uint32_t len) {
+ (void)addr;
+ (void)len;
+
+ return stlink_write_option_control_register_c0(sl, *(uint32_t*)base);
+}
+
+/**
+ * Read option control register F0
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_control_register_f0(stlink_t *sl, uint32_t *option_byte) {
+ DLOG("@@@@ Read option control register byte from %#10x\n", FLASH_OBR);
+ return stlink_read_debug32(sl, FLASH_OBR, option_byte);
+}
+
+/**
+ * Write option bytes F0
+ * @param sl
+ * @param addr of the memory mapped option bytes
+ * @param base option bytes
+ * @param len of option bytes
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_bytes_f0(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t len) {
+ int32_t ret = 0;
+
+ if (len < 12 || addr != STM32_F0_OPTION_BYTES_BASE) {
+ WLOG("Only full write of option bytes area is supported\n");
+ return -1;
+ }
+
+ clear_flash_error(sl);
+
+ WLOG("Erasing option bytes\n");
+
+ /* erase option bytes */
+ stlink_write_debug32(sl, FLASH_CR, (1 << FLASH_CR_OPTER) | (1 << FLASH_CR_OPTWRE));
+ ret = stlink_write_debug32(sl, FLASH_CR, (1 << FLASH_CR_OPTER) | (1 << FLASH_CR_STRT) | (1 << FLASH_CR_OPTWRE));
+ if (ret) {
+ return ret;
+ }
+
+ wait_flash_busy(sl);
+
+ ret = check_flash_error(sl);
+ if (ret) {
+ return ret;
+ }
+
+ WLOG("Writing option bytes to %#10x\n", addr);
+
+ /* Set the Option PG bit to enable programming */
+ stlink_write_debug32(sl, FLASH_CR, (1 << FLASH_CR_OPTPG) | (1 << FLASH_CR_OPTWRE));
+
+ /* Use flash loader for write OP
+ * because flash memory writable by half word */
+ flash_loader_t fl;
+ ret = stlink_flash_loader_init(sl, &fl);
+ if (ret) {
+ return ret;
+ }
+ ret = stlink_flash_loader_run(sl, &fl, addr, base, len);
+ if (ret) {
+ return ret;
+ }
+
+ /* Reload option bytes */
+ stlink_write_debug32(sl, FLASH_CR, (1 << FLASH_CR_OBL_LAUNCH));
+
+ return check_flash_error(sl);
+}
+
+/**
+ * Write option control register F0
+ * @param sl
+ * @param option_cr
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_control_register_f0(stlink_t *sl, uint32_t option_cr) {
+ int32_t ret = 0;
+ uint16_t opt_val[8];
+ uint32_t protection, optiondata;
+ uint16_t user_options, user_data, rdp;
+ uint32_t option_offset, user_data_offset;
+
+ ILOG("Asked to write option control register %#10x to %#010x.\n", option_cr, FLASH_OBR);
+
+ /* Clear errors */
+ clear_flash_error(sl);
+
+ /* Retrieve current values */
+ ret = stlink_read_debug32(sl, FLASH_OBR, &optiondata);
+ if (ret) {
+ return ret;
+ }
+ ret = stlink_read_debug32(sl, FLASH_WRPR, &protection);
+ if (ret) {
+ return ret;
+ }
+
+ /* Translate OBR value to flash store structure
+ * F0: RM0091, Option byte description, pp. 75-78
+ * F1: PM0075, Option byte description, pp. 19-22
+ * F3: RM0316, Option byte description, pp. 85-87 */
+ switch(sl->chip_id)
+ {
+ case 0x422: /* STM32F30x */
+ case 0x432: /* STM32F37x */
+ case 0x438: /* STM32F303x6/8 and STM32F328 */
+ case 0x446: /* STM32F303xD/E and STM32F398xE */
+ case 0x439: /* STM32F302x6/8 */
+ case 0x440: /* STM32F05x */
+ case 0x444: /* STM32F03x */
+ case 0x445: /* STM32F04x */
+ case 0x448: /* STM32F07x */
+ case 0x442: /* STM32F09x */
+ option_offset = 6;
+ user_data_offset = 16;
+ rdp = 0x55AA;
+ break;
+ default:
+ option_offset = 0;
+ user_data_offset = 10;
+ rdp = 0x5AA5;
+ break;
+ }
+
+ user_options = (option_cr >> option_offset >> 2) & 0xFFFF;
+ user_data = (option_cr >> user_data_offset) & 0xFFFF;
+
+#define VAL_WITH_COMPLEMENT(v) (uint16_t)(((v)&0xFF) | (((~(v))<<8)&0xFF00))
+
+ opt_val[0] = (option_cr & (1 << 1/*OPT_READOUT*/)) ? 0xFFFF : rdp;
+ opt_val[1] = VAL_WITH_COMPLEMENT(user_options);
+ opt_val[2] = VAL_WITH_COMPLEMENT(user_data);
+ opt_val[3] = VAL_WITH_COMPLEMENT(user_data >> 8);
+ opt_val[4] = VAL_WITH_COMPLEMENT(protection);
+ opt_val[5] = VAL_WITH_COMPLEMENT(protection >> 8);
+ opt_val[6] = VAL_WITH_COMPLEMENT(protection >> 16);
+ opt_val[7] = VAL_WITH_COMPLEMENT(protection >> 24);
+
+#undef VAL_WITH_COMPLEMENT
+
+ /* Write bytes and check errors */
+ ret = stlink_write_option_bytes_f0(sl, STM32_F0_OPTION_BYTES_BASE, (uint8_t*)opt_val, sizeof(opt_val));
+ if (ret)
+ return ret;
+
+ ret = check_flash_error(sl);
+ if (!ret) {
+ ILOG("Wrote option bytes %#010x to %#010x!\n", option_cr,
+ FLASH_OBR);
+ }
+
+ return ret;
+}
+
+/**
+ * Read option control register F2
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_control_register_f2(stlink_t *sl, uint32_t *option_byte) {
+ return stlink_read_debug32(sl, FLASH_F2_OPT_CR, option_byte);
+}
+
+/**
+ * Read option bytes F2
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_bytes_f2(stlink_t *sl, uint32_t *option_byte) {
+ return stlink_read_option_control_register_f2(sl, option_byte);
+}
+
+/**
+ * Read option control register F4
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_control_register_f4(stlink_t *sl, uint32_t *option_byte) {
+ return stlink_read_debug32(sl, FLASH_F4_OPTCR, option_byte);
+}
+
+/**
+ * Read option bytes F4
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_bytes_f4(stlink_t *sl, uint32_t *option_byte) {
+ return stlink_read_option_control_register_f4(sl, option_byte);
+}
+
+/**
+ * Write option bytes F4
+ * @param sl
+ * @param addr of the memory mapped option bytes
+ * @param base option bytes
+ * @param len of option bytes
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_bytes_f4(stlink_t *sl, stm32_addr_t addr, uint8_t *base, uint32_t len) {
+ uint32_t option_byte;
+ int32_t ret = 0;
+ (void)addr;
+ (void)len;
+
+ // Clear errors
+ clear_flash_error(sl);
+
+ write_uint32((unsigned char *)&option_byte, *(uint32_t *)(base));
+
+ // write option byte, ensuring we dont lock opt, and set strt bit
+ stlink_write_debug32(sl, FLASH_F4_OPTCR,
+ (option_byte & ~(1 << FLASH_F4_OPTCR_LOCK)) |
+ (1 << FLASH_F4_OPTCR_START));
+
+ wait_flash_busy(sl);
+ ret = check_flash_error(sl);
+
+ // option bytes are reloaded at reset only, no obl. */
+ return (ret);
+}
+
+/**
+ * Read option bytes F7
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+// Since multiple bytes can be read, we read and print32_t all, but one here
+// and then return the last one just like other devices.
+int32_t stlink_read_option_bytes_f7(stlink_t *sl, uint32_t *option_byte) {
+ int32_t err = -1;
+ for (uint32_t counter = 0; counter < (sl->option_size / 4 - 1); counter++) {
+ err = stlink_read_debug32(sl, sl->option_base + counter * sizeof(uint32_t), option_byte);
+ if (err == -1) {
+ return err;
+ } else {
+ printf("%08x\n", *option_byte);
+ }
+ }
+
+ return stlink_read_debug32(
+ sl,
+ sl->option_base + (uint32_t)(sl->option_size / 4 - 1) * sizeof(uint32_t),
+ option_byte);
+}
+
+/**
+ * Write option bytes F7
+ * @param sl
+ * @param addr of the memory mapped option bytes
+ * @param base option bytes
+ * @param len of option bytes
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_bytes_f7(stlink_t *sl, stm32_addr_t addr, uint8_t *base, uint32_t len) {
+ uint32_t option_byte;
+ int32_t ret = 0;
+
+ // Clear errors
+ clear_flash_error(sl);
+
+ ILOG("Asked to write option byte %#10x to %#010x.\n", *(uint32_t *)(base), addr);
+ write_uint32((unsigned char *)&option_byte, *(uint32_t *)(base));
+ ILOG("Write %d option bytes %#010x to %#010x!\n", len, option_byte, addr);
+
+ if (addr == 0) {
+ addr = FLASH_F7_OPTCR;
+ ILOG("No address provided, using %#10x\n", addr);
+ }
+
+ if (addr == FLASH_F7_OPTCR) {
+ /* write option byte, ensuring we dont lock opt, and set strt bit */
+ stlink_write_debug32(sl, FLASH_F7_OPTCR,
+ (option_byte & ~(1 << FLASH_F7_OPTCR_LOCK)) |
+ (1 << FLASH_F7_OPTCR_START));
+ } else if (addr == FLASH_F7_OPTCR1) {
+ // Read FLASH_F7_OPTCR
+ uint32_t oldvalue;
+ stlink_read_debug32(sl, FLASH_F7_OPTCR, &oldvalue);
+ /* write option byte */
+ stlink_write_debug32(sl, FLASH_F7_OPTCR1, option_byte);
+ // Write FLASH_F7_OPTCR lock and start address
+ stlink_write_debug32(sl, FLASH_F7_OPTCR,
+ (oldvalue & ~(1 << FLASH_F7_OPTCR_LOCK)) |
+ (1 << FLASH_F7_OPTCR_START));
+ } else {
+ WLOG("WIP: write %#010x to address %#010x\n", option_byte, addr);
+ stlink_write_debug32(sl, addr, option_byte);
+ }
+
+ wait_flash_busy(sl);
+
+ ret = check_flash_error(sl);
+ if (!ret)
+ ILOG("Wrote %d option bytes %#010x to %#010x!\n", len, *(uint32_t *)base, addr);
+
+ /* option bytes are reloaded at reset only, no obl. */
+
+ return ret;
+}
+
+/**
+ * Read option control register F7
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_control_register_f7(stlink_t *sl, uint32_t *option_byte) {
+ DLOG("@@@@ Read option control register byte from %#10x\n", FLASH_F7_OPTCR);
+ return stlink_read_debug32(sl, FLASH_F7_OPTCR, option_byte);
+}
+
+/**
+ * Write option control register F7
+ * @param sl
+ * @param option_cr
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_control_register_f7(stlink_t *sl, uint32_t option_cr) {
+ int32_t ret = 0;
+
+ // Clear errors
+ clear_flash_error(sl);
+
+ ILOG("Asked to write option control register 1 %#10x to %#010x.\n",
+ option_cr, FLASH_F7_OPTCR);
+
+ /* write option byte, ensuring we dont lock opt, and set strt bit */
+ stlink_write_debug32(sl, FLASH_F7_OPTCR,
+ (option_cr & ~(1 << FLASH_F7_OPTCR_LOCK)) |
+ (1 << FLASH_F7_OPTCR_START));
+
+ wait_flash_busy(sl);
+
+ ret = check_flash_error(sl);
+ if (!ret)
+ ILOG("Wrote option bytes %#010x to %#010x!\n", option_cr,
+ FLASH_F7_OPTCR);
+
+ return ret;
+}
+
+/**
+ * Read option control register1 F7
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_control_register1_f7(stlink_t *sl, uint32_t *option_byte) {
+ DLOG("@@@@ Read option control register 1 byte from %#10x\n",
+ FLASH_F7_OPTCR1);
+ return stlink_read_debug32(sl, FLASH_F7_OPTCR1, option_byte);
+}
+
+/**
+ * Write option control register1 F7
+ * @param sl
+ * @param option_cr1
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_control_register1_f7(stlink_t *sl, uint32_t option_cr1) {
+ int32_t ret = 0;
+
+ // Clear errors
+ clear_flash_error(sl);
+
+ ILOG("Asked to write option control register 1 %#010x to %#010x.\n",
+ option_cr1, FLASH_F7_OPTCR1);
+
+ /* write option byte, ensuring we dont lock opt, and set strt bit */
+ uint32_t current_control_register_value;
+ stlink_read_debug32(sl, FLASH_F7_OPTCR, &current_control_register_value);
+
+ /* write option byte */
+ stlink_write_debug32(sl, FLASH_F7_OPTCR1, option_cr1);
+ stlink_write_debug32(
+ sl, FLASH_F7_OPTCR,
+ (current_control_register_value & ~(1 << FLASH_F7_OPTCR_LOCK)) |
+ (1 << FLASH_F7_OPTCR_START));
+
+ wait_flash_busy(sl);
+
+ ret = check_flash_error(sl);
+ if (!ret)
+ ILOG("Wrote option bytes %#010x to %#010x!\n", option_cr1, FLASH_F7_OPTCR1);
+
+ return ret;
+}
+
+/**
+ * Read option bytes boot address F7
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_bytes_boot_add_f7(stlink_t *sl, uint32_t *option_byte) {
+ DLOG("@@@@ Read option byte boot address\n");
+ return stlink_read_option_control_register1_f7(sl, option_byte);
+}
+
+/**
+ * Write option bytes boot address F7
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_bytes_boot_add_f7(stlink_t *sl, uint32_t option_byte_boot_add) {
+ ILOG("Asked to write option byte boot add %#010x.\n", option_byte_boot_add);
+ return stlink_write_option_control_register1_f7(sl, option_byte_boot_add);
+}
+
+/**
+ * Read option control register Gx
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_control_register_gx(stlink_t *sl, uint32_t *option_byte) {
+ return stlink_read_debug32(sl, FLASH_Gx_OPTR, option_byte);
+}
+
+/**
+ * Read option bytes Gx
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_bytes_gx(stlink_t *sl, uint32_t *option_byte) {
+ return stlink_read_option_control_register_gx(sl, option_byte);
+}
+
+/**
+ * Write option bytes Gx
+ * @param sl
+ * @param addr of the memory mapped option bytes
+ * @param base option bytes
+ * @param len of option bytes
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_bytes_gx(stlink_t *sl, stm32_addr_t addr, uint8_t *base, uint32_t len) {
+ /* Write options bytes */
+ uint32_t val;
+ int32_t ret = 0;
+ (void)len;
+ uint32_t data;
+
+ clear_flash_error(sl);
+
+ write_uint32((unsigned char *)&data, *(uint32_t *)(base));
+ WLOG("Writing option bytes %#10x to %#10x\n", data, addr);
+ stlink_write_debug32(sl, FLASH_Gx_OPTR, data);
+
+ // Set Options Start bit
+ stlink_read_debug32(sl, FLASH_Gx_CR, &val);
+ val |= (1 << FLASH_Gx_CR_OPTSTRT);
+ stlink_write_debug32(sl, FLASH_Gx_CR, val);
+
+ wait_flash_busy(sl);
+
+ ret = check_flash_error(sl);
+
+ // Reload options
+ stlink_read_debug32(sl, FLASH_Gx_CR, &val);
+ val |= (1 << FLASH_Gx_CR_OBL_LAUNCH);
+ stlink_write_debug32(sl, FLASH_Gx_CR, val);
+
+ return (ret);
+}
+
+/**
+ * Write option bytes H7
+ * @param sl
+ * @param addr of the memory mapped option bytes
+ * @param base option bytes
+ * @param len of option bytes
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_bytes_h7(stlink_t *sl, stm32_addr_t addr, uint8_t *base, uint32_t len) {
+ uint32_t val;
+ uint32_t data;
+
+ // Wait until previous flash option has completed
+ wait_flash_busy(sl);
+
+ // Clear previous error
+ stlink_write_debug32(sl, FLASH_H7_OPTCCR,
+ 1 << FLASH_H7_OPTCCR_CLR_OPTCHANGEERR);
+
+ while (len != 0) {
+ switch (addr) {
+ case FLASH_H7_REGS_ADDR + 0x20: // FLASH_OPTSR_PRG
+ case FLASH_H7_REGS_ADDR + 0x2c: // FLASH_PRAR_PRG1
+ case FLASH_H7_REGS_ADDR + 0x34: // FLASH_SCAR_PRG1
+ case FLASH_H7_REGS_ADDR + 0x3c: // FLASH_WPSN_PRG1
+ case FLASH_H7_REGS_ADDR + 0x44: // FLASH_BOOT_PRG
+ /* Write to FLASH_xxx_PRG registers */
+ write_uint32((unsigned char *)&data, *(uint32_t *)(base)); // write options bytes
+
+ WLOG("Writing option bytes %#10x to %#10x\n", data, addr);
+
+ /* Skip if the value in the CUR register is identical */
+ stlink_read_debug32(sl, addr - 4, &val);
+ if (val == data) {
+ break;
+ }
+
+ /* Write new option byte values and start modification */
+ stlink_write_debug32(sl, addr, data);
+ stlink_read_debug32(sl, FLASH_H7_OPTCR, &val);
+ val |= (1 << FLASH_H7_OPTCR_OPTSTART);
+ stlink_write_debug32(sl, FLASH_H7_OPTCR, val);
+
+ /* Wait for the option bytes modification to complete */
+ do {
+ stlink_read_debug32(sl, FLASH_H7_OPTSR_CUR, &val);
+ } while ((val & (1 << FLASH_H7_OPTSR_OPT_BUSY)) != 0);
+
+ /* Check for errors */
+ if ((val & (1 << FLASH_H7_OPTSR_OPTCHANGEERR)) != 0) {
+ stlink_write_debug32(sl, FLASH_H7_OPTCCR, 1 << FLASH_H7_OPTCCR_CLR_OPTCHANGEERR);
+ return -1;
+ }
+ break;
+
+ default:
+ /* Skip non-programmable registers */
+ break;
+ }
+
+ len -= 4;
+ addr += 4;
+ base += 4;
+ }
+
+ return 0;
+}
+
+/**
+ * Write option bytes L0
+ * @param sl
+ * @param addr of the memory mapped option bytes
+ * @param base option bytes
+ * @param len of option bytes
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_bytes_l0(stlink_t *sl, stm32_addr_t addr, uint8_t *base, uint32_t len) {
+ uint32_t flash_base = get_stm32l0_flash_base(sl);
+ uint32_t val;
+ uint32_t data;
+ int32_t ret = 0;
+
+ // Clear errors
+ clear_flash_error(sl);
+
+ while (len != 0) {
+ write_uint32((unsigned char *)&data,
+ *(uint32_t *)(base)); // write options bytes
+
+ WLOG("Writing option bytes %#10x to %#10x\n", data, addr);
+ stlink_write_debug32(sl, addr, data);
+ wait_flash_busy(sl);
+
+ if ((ret = check_flash_error(sl))) {
+ break;
+ }
+
+ len -= 4;
+ addr += 4;
+ base += 4;
+ }
+
+ // Reload options
+ stlink_read_debug32(sl, flash_base + FLASH_PECR_OFF, &val);
+ val |= (1 << FLASH_L0_OBL_LAUNCH);
+ stlink_write_debug32(sl, flash_base + FLASH_PECR_OFF, val);
+
+ return (ret);
+}
+
+/**
+ * Write option bytes L4
+ * @param sl
+ * @param addr of the memory mapped option bytes
+ * @param base option bytes
+ * @param len of option bytes
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_bytes_l4(stlink_t *sl, stm32_addr_t addr, uint8_t *base, uint32_t len) {
+
+ uint32_t val;
+ int32_t ret = 0;
+ (void)addr;
+ (void)len;
+
+ // Clear errors
+ clear_flash_error(sl);
+
+ // 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, FLASH_L4_OPTR, data);
+
+ // set options start bit
+ stlink_read_debug32(sl, FLASH_L4_CR, &val);
+ val |= (1 << FLASH_L4_CR_OPTSTRT);
+ stlink_write_debug32(sl, FLASH_L4_CR, val);
+
+ wait_flash_busy(sl);
+ ret = check_flash_error(sl);
+
+ // apply options bytes immediate
+ stlink_read_debug32(sl, FLASH_L4_CR, &val);
+ val |= (1 << FLASH_L4_CR_OBL_LAUNCH);
+ stlink_write_debug32(sl, FLASH_L4_CR, val);
+
+ return (ret);
+}
+
+/**
+ * Write option bytes WB
+ * @param sl
+ * @param addr of the memory mapped option bytes
+ * @param base option bytes
+ * @param len of option bytes
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_bytes_wb(stlink_t *sl, stm32_addr_t addr, uint8_t *base, uint32_t len) {
+ /* Write options bytes */
+ uint32_t val;
+ int32_t ret = 0;
+ (void)len;
+ uint32_t data;
+
+ clear_flash_error(sl);
+
+ while (len != 0) {
+ write_uint32((unsigned char *)&data, *(uint32_t *)(base)); // write options bytes
+
+ WLOG("Writing option bytes %#10x to %#10x\n", data, addr);
+ stlink_write_debug32(sl, addr, data);
+ wait_flash_busy(sl);
+
+ if ((ret = check_flash_error(sl))) {
+ break;
+ }
+
+ len -= 4;
+ addr += 4;
+ base += 4;
+ }
+
+ // Set Options Start bit
+ stlink_read_debug32(sl, FLASH_WB_CR, &val);
+ val |= (1 << FLASH_WB_CR_OPTSTRT);
+ stlink_write_debug32(sl, FLASH_WB_CR, val);
+
+ wait_flash_busy(sl);
+
+ ret = check_flash_error(sl);
+
+ // Reload options
+ stlink_read_debug32(sl, FLASH_WB_CR, &val);
+ val |= (1 << FLASH_WB_CR_OBL_LAUNCH);
+ stlink_write_debug32(sl, FLASH_WB_CR, val);
+
+ return (ret);
+}
+
+/**
+ * Read option control register WB
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_control_register_wb(stlink_t *sl, uint32_t *option_byte) {
+ DLOG("@@@@ Read option control register byte from %#10x\n", FLASH_WB_OPTR);
+ return stlink_read_debug32(sl, FLASH_WB_OPTR, option_byte);
+}
+
+/**
+ * Write option control register WB
+ * @param sl
+ * @param option_cr
+ * @return 0 on success, -ve on failure.
+ */
+static int32_t stlink_write_option_control_register_wb(stlink_t *sl, uint32_t option_cr) {
+ int32_t ret = 0;
+
+ // Clear errors
+ clear_flash_error(sl);
+
+ ILOG("Asked to write option control register 1 %#10x to %#010x.\n",
+ option_cr, FLASH_WB_OPTR);
+
+ /* write option byte, ensuring we dont lock opt, and set strt bit */
+ stlink_write_debug32(sl, FLASH_WB_OPTR, option_cr);
+
+ wait_flash_busy(sl);
+
+ // Set Options Start bit
+ uint32_t val = (1 << FLASH_WB_CR_OPTSTRT);
+ stlink_write_debug32(sl, FLASH_WB_CR, val);
+
+ wait_flash_busy(sl);
+
+ ret = check_flash_error(sl);
+ if (!ret)
+ ILOG("Wrote option bytes %#010x to %#010x!\n", option_cr, FLASH_WB_OPTR);
+
+ return ret;
+}
+
+/**
+ * Read option bytes generic
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_bytes_generic(stlink_t *sl, uint32_t *option_byte) {
+ DLOG("@@@@ Read option bytes boot address from %#10x\n", sl->option_base);
+ return stlink_read_debug32(sl, sl->option_base, option_byte);
+}
+
+/**
+ * Write option bytes
+ * @param sl
+ * @param addr of the memory mapped option bytes
+ * @param base option bytes
+ * @param len of option bytes
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_write_option_bytes(stlink_t *sl, stm32_addr_t addr, uint8_t *base, uint32_t len) {
+ int32_t ret = -1;
+
+ if (sl->option_base == 0) {
+ ELOG("Option bytes writing is currently not supported for connected chip\n");
+ return (-1);
+ }
+
+ if ((addr < sl->option_base) || addr > sl->option_base + sl->option_size) {
+ ELOG("Option bytes start address out of Option bytes range\n");
+ return (-1);
+ }
+
+ if (addr + len > sl->option_base + sl->option_size) {
+ ELOG("Option bytes data too long\n");
+ return (-1);
+ }
+
+ wait_flash_busy(sl);
+
+ if (unlock_flash_if(sl)) {
+ ELOG("Flash unlock failed! System reset required to be able to unlock it again!\n");
+ return (-1);
+ }
+
+ if (unlock_flash_option_if(sl)) {
+ ELOG("Flash option unlock failed!\n");
+ return (-1);
+ }
+
+ switch (sl->flash_type) {
+ case STM32_FLASH_TYPE_C0:
+ ret = stlink_write_option_bytes_c0(sl, addr, base, len);
+ break;
+ case STM32_FLASH_TYPE_F0_F1_F3:
+ case STM32_FLASH_TYPE_F1_XL:
+ ret = stlink_write_option_bytes_f0(sl, addr, base, len);
+ break;
+ case STM32_FLASH_TYPE_F2_F4:
+ ret = stlink_write_option_bytes_f4(sl, addr, base, len);
+ break;
+ case STM32_FLASH_TYPE_F7:
+ ret = stlink_write_option_bytes_f7(sl, addr, base, len);
+ break;
+ case STM32_FLASH_TYPE_L0_L1:
+ ret = stlink_write_option_bytes_l0(sl, addr, base, len);
+ break;
+ case STM32_FLASH_TYPE_L4:
+ ret = stlink_write_option_bytes_l4(sl, addr, base, len);
+ break;
+ case STM32_FLASH_TYPE_G0:
+ case STM32_FLASH_TYPE_G4:
+ ret = stlink_write_option_bytes_gx(sl, addr, base, len);
+ break;
+ case STM32_FLASH_TYPE_H7:
+ ret = stlink_write_option_bytes_h7(sl, addr, base, len);
+ break;
+ case STM32_FLASH_TYPE_WB_WL:
+ ret = stlink_write_option_bytes_wb(sl, addr, base, len);
+ break;
+ default:
+ ELOG("Option bytes writing is currently not implemented for connected chip\n");
+ break;
+ }
+
+ if (ret) {
+ ELOG("Flash option write failed!\n");
+ } else {
+ ILOG("Wrote %d option bytes to %#010x!\n", len, addr);
+ }
+
+ /* Re-lock flash. */
+ lock_flash_option(sl);
+ lock_flash(sl);
+
+ return ret;
+}
+
+/**
+ * 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.
+ */
+int32_t stlink_fwrite_option_bytes(stlink_t *sl, const char *path, stm32_addr_t addr) {
+ /* Write the file in flash at addr */
+ int32_t err;
+ mapped_file_t mf = MAPPED_FILE_INITIALIZER;
+
+ if (map_file(&mf, path) == -1) {
+ ELOG("map_file() == -1\n");
+ return (-1);
+ }
+
+ printf("file %s ", path);
+ md5_calculate(&mf);
+ stlink_checksum(&mf);
+
+ err = stlink_write_option_bytes(sl, addr, mf.base, (uint32_t)mf.len);
+ stlink_fwrite_finalize(sl, addr);
+ unmap_file(&mf);
+
+ return (err);
+}
+
+/**
+ * Read option control register 32
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_control_register32(stlink_t *sl, uint32_t *option_byte) {
+ if (sl->option_base == 0) {
+ ELOG("Option bytes read is currently not supported for connected chip\n");
+ return -1;
+ }
+
+ switch (sl->flash_type) {
+ case STM32_FLASH_TYPE_C0:
+ return stlink_read_option_control_register_c0(sl, option_byte);
+ case STM32_FLASH_TYPE_F0_F1_F3:
+ case STM32_FLASH_TYPE_F1_XL:
+ return stlink_read_option_control_register_f0(sl, option_byte);
+ case STM32_FLASH_TYPE_F7:
+ return stlink_read_option_control_register_f7(sl, option_byte);
+ case STM32_FLASH_TYPE_WB_WL:
+ return stlink_read_option_control_register_wb(sl, option_byte);
+ default:
+ return -1;
+ }
+}
+
+/**
+ * Write option control register 32
+ * @param sl
+ * @param option_cr
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_write_option_control_register32(stlink_t *sl, uint32_t option_cr) {
+ int32_t ret = -1;
+
+ wait_flash_busy(sl);
+
+ if (unlock_flash_if(sl)) {
+ ELOG("Flash unlock failed! System reset required to be able to unlock it again!\n");
+ return -1;
+ }
+
+ if (unlock_flash_option_if(sl)) {
+ ELOG("Flash option unlock failed!\n");
+ return -1;
+ }
+
+ switch (sl->flash_type) {
+ case STM32_FLASH_TYPE_C0:
+ ret = stlink_write_option_control_register_c0(sl, option_cr);
+ break;
+ case STM32_FLASH_TYPE_F0_F1_F3:
+ case STM32_FLASH_TYPE_F1_XL:
+ ret = stlink_write_option_control_register_f0(sl, option_cr);
+ break;
+ case STM32_FLASH_TYPE_F7:
+ ret = stlink_write_option_control_register_f7(sl, option_cr);
+ break;
+ case STM32_FLASH_TYPE_WB_WL:
+ ret =
+ stlink_write_option_control_register_wb(sl, option_cr);
+ break;
+ default:
+ ELOG("Option control register writing is currently not implemented for connected chip\n");
+ break;
+ }
+
+ if (ret)
+ ELOG("Flash option write failed!\n");
+ else
+ ILOG("Wrote option control register %#010x!\n", option_cr);
+
+ /* Re-lock flash. */
+ lock_flash_option(sl);
+ lock_flash(sl);
+
+ return ret;
+}
+
+/**
+ * Read option control register1 32
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_control_register1_32(stlink_t *sl, uint32_t *option_byte) {
+ if (sl->option_base == 0) {
+ ELOG("Option bytes read is currently not supported for connected chip\n");
+ return -1;
+ }
+
+ switch (sl->flash_type) {
+ case STM32_FLASH_TYPE_F7:
+ return stlink_read_option_control_register1_f7(sl, option_byte);
+ default:
+ return -1;
+ // return stlink_read_option_control_register1_generic(sl, option_byte);
+ }
+}
+
+/**
+ * Write option control register1 32
+ * @param sl
+ * @param option_cr
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_write_option_control_register1_32(stlink_t *sl, uint32_t option_cr1) {
+ int32_t ret = -1;
+
+ wait_flash_busy(sl);
+
+ if (unlock_flash_if(sl)) {
+ ELOG("Flash unlock failed! System reset required to be able to unlock it again!\n");
+ return -1;
+ }
+
+ if (unlock_flash_option_if(sl)) {
+ ELOG("Flash option unlock failed!\n");
+ return -1;
+ }
+
+ switch (sl->flash_type) {
+ case STM32_FLASH_TYPE_F7:
+ ret =
+ stlink_write_option_control_register1_f7(sl, option_cr1);
+ break;
+ default:
+ ELOG("Option control register 1 writing is currently not implemented for "
+ "connected chip\n");
+ break;
+ }
+
+ if (ret)
+ ELOG("Flash option write failed!\n");
+ else
+ ILOG("Wrote option control register 1 %#010x!\n", option_cr1);
+
+ lock_flash_option(sl);
+ lock_flash(sl);
+
+ return (ret);
+}
+
+/**
+ * Read option bytes 32
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_bytes32(stlink_t *sl, uint32_t *option_byte) {
+ if (sl->option_base == 0) {
+ ELOG("Option bytes read is currently not supported for connected chip\n");
+ return (-1);
+ }
+
+ switch (sl->chip_id) {
+ case STM32_CHIPID_C011xx:
+ case STM32_CHIPID_C031xx:
+ return stlink_read_option_bytes_c0(sl, option_byte);
+ case STM32_CHIPID_F2:
+ return stlink_read_option_bytes_f2(sl, option_byte);
+ case STM32_CHIPID_F4:
+ case STM32_CHIPID_F446:
+ return stlink_read_option_bytes_f4(sl, option_byte);
+ case STM32_CHIPID_F76xxx:
+ return stlink_read_option_bytes_f7(sl, option_byte);
+ case STM32_CHIPID_G0_CAT1:
+ case STM32_CHIPID_G0_CAT2:
+ case STM32_CHIPID_G4_CAT2:
+ case STM32_CHIPID_G4_CAT3:
+ case STM32_CHIPID_G4_CAT4:
+ return stlink_read_option_bytes_gx(sl, option_byte);
+ default:
+ return stlink_read_option_bytes_generic(sl, option_byte);
+ }
+}
+
+/**
+ * Write option bytes 32
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_write_option_bytes32(stlink_t *sl, uint32_t option_byte) {
+ WLOG("About to write option byte %#10x to %#10x.\n", option_byte,
+ sl->option_base);
+ return stlink_write_option_bytes(sl, sl->option_base, (uint8_t *)&option_byte, 4);
+}
+
+/**
+ * Read option bytes boot address 32
+ * @param sl
+ * @param option_byte
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_read_option_bytes_boot_add32(stlink_t *sl, uint32_t *option_byte) {
+ if (sl->option_base == 0) {
+ ELOG("Option bytes boot address read is currently not supported for connected chip\n");
+ return -1;
+ }
+
+ switch (sl->flash_type) {
+ case STM32_FLASH_TYPE_F7:
+ return stlink_read_option_bytes_boot_add_f7(sl, option_byte);
+ default:
+ return -1;
+ // return stlink_read_option_bytes_boot_add_generic(sl, option_byte);
+ }
+}
+
+/**
+ * Write option bytes boot address 32
+ * @param sl
+ * @param option_bytes_boot_add
+ * @return 0 on success, -ve on failure.
+ */
+int32_t stlink_write_option_bytes_boot_add32(stlink_t *sl, uint32_t option_bytes_boot_add) {
+ int32_t ret = -1;
+
+ wait_flash_busy(sl);
+
+ if (unlock_flash_if(sl)) {
+ ELOG("Flash unlock failed! System reset required to be able to unlock it again!\n");
+ return -1;
+ }
+
+ if (unlock_flash_option_if(sl)) {
+ ELOG("Flash option unlock failed!\n");
+ return -1;
+ }
+
+ switch (sl->flash_type) {
+ case STM32_FLASH_TYPE_F7:
+ ret = stlink_write_option_bytes_boot_add_f7(sl, option_bytes_boot_add);
+ break;
+ default:
+ ELOG("Option bytes boot address writing is currently not implemented for connected chip\n");
+ break;
+ }
+
+ if (ret)
+ ELOG("Flash option write failed!\n");
+ else
+ ILOG("Wrote option bytes boot address %#010x!\n", option_bytes_boot_add);
+
+ /* Re-lock flash. */
+ lock_flash_option(sl);
+ lock_flash(sl);
+
+ return ret;
+}