This is an automated email from Gerrit. "Ahmed Haoues <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9697
-- gerrit commit 65c1c0790bbd15f37e473c845e6105016671bb90 Author: fedi BOUZAZI <[email protected]> Date: Mon Aug 15 14:19:24 2022 +0100 flash/nor: support STM32CubeProgrammer flashloader aka stldr add a flash driver to support STM32CubeProgrammer flashloaders provided as elf files with .stdlr extension. to get the latest external memories support free of charge Checkpatch-ignore: CAMELCASE, NEW_TYPEDEFS, SPACING Change-Id: Ifdbc2044afd9d6b5adc517d9e2051363d0549829 Signed-off-by: fedi BOUZAZI <[email protected]> Signed-off-by: HAOUES Ahmed <[email protected]> Signed-off-by: Tarek BOCHKATI <[email protected]> diff --git a/doc/openocd.texi b/doc/openocd.texi index 89900b06a7..8517950f73 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -8310,6 +8310,46 @@ applied to all of them. @end deffn @end deffn +@deffn {Flash Driver} {stldr} +This driver supports STM32CubeProgrammer Memory Loaders (aka STLDR). +This driver permits re-using these loaders to support both internal and external memories. + +@example +flash bank $_FLASHNAME stldr 0 0 0 0 $_TARGETNAME /path/to/loader.xldr +@end example + +This driver provides these specific commands: + +@deffn {Command} {stldr set_loader} num path_to_loader +Sets the loader path + +The @var{num} parameter is a value shown by @command{flash banks}. + +Hint: use a complete path name for @var{path_to_loader}, so you don't depend +on the directory used to start the OpenOCD server. +@end deffn + +@deffn {Command} {stldr set_size} num size +Set bank size + +The @var{num} parameter is a value shown by @command{flash banks}. +The @var{size} is the bank size. +@end deffn + +@deffn {Command} {stldr init} num +Initialize memory + +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + +@deffn {Command} {stldr mass_erase} num +Mass erases the entire flash bank. + +The @var{num} parameter is a value shown by @command{flash banks}. +@end deffn + +@end deffn + @deffn {Flash Driver} {stm32f1x} This driver supports the STM32F0, STM32F1 and STM32F3 microcontroller series from STMicroelectronics. The driver is also compatible with the GD32F1, GD32VF103 (RISC-V core), GD32F3 and GD32E23 microcontroller series from GigaDevice. diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 5c4737f7ac..86ccc28ba3 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -71,6 +71,7 @@ NOR_DRIVERS = \ %D%/stmsmi.c \ %D%/stmqspi.c \ %D%/stellaris.c \ + %D%/stldr_driver.c \ %D%/stm32f1x.c \ %D%/stm32f2x.c \ %D%/stm32lx.c \ diff --git a/src/flash/nor/driver.h b/src/flash/nor/driver.h index afb73a3fb2..2f28f182f7 100644 --- a/src/flash/nor/driver.h +++ b/src/flash/nor/driver.h @@ -300,6 +300,7 @@ extern const struct flash_driver rsl10_flash; extern const struct flash_driver sh_qspi_flash; extern const struct flash_driver sim3x_flash; extern const struct flash_driver stellaris_flash; +extern const struct flash_driver stldr_flash; extern const struct flash_driver stm32f1x_flash; extern const struct flash_driver stm32f2x_flash; extern const struct flash_driver stm32h7x_flash; diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 094b5a76ba..2555b27097 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -79,6 +79,7 @@ static const struct flash_driver * const flash_drivers[] = { &sh_qspi_flash, &sim3x_flash, &stellaris_flash, + &stldr_flash, &stm32f1x_flash, &stm32f2x_flash, &stm32h7x_flash, diff --git a/src/flash/nor/stldr_driver.c b/src/flash/nor/stldr_driver.c new file mode 100644 index 0000000000..4bbbd101be --- /dev/null +++ b/src/flash/nor/stldr_driver.c @@ -0,0 +1,1201 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Copyright (C) 2022-2026, STMicroelectronics * + * [email protected] * + * [email protected] * + * [email protected] * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <helper/replacements.h> + +#ifdef HAVE_ELF_H +#include <elf.h> +#endif + +#include <helper/binarybuffer.h> +#include <target/algorithm.h> +#include <target/breakpoints.h> +#include <target/cortex_m.h> +#include <sys/stat.h> + +#include "imp.h" +#define ROUND_TO_DWORD(x) (((size_t)(x) + 7) & ~0x07UL) + +/* stldr functions return values */ +#define STLDR_FUNC_FAILURE 0 +#define STLDR_FUNC_SUCCESS 1 + +#define STLDR_FUNC_ADDR_UNKNOWN 0xFFFFFFFF + +/* offsets to mimic STM32CubeProgrammer memory usage */ +#define STACK_SIZE 0x200 +#define STACK_OFFSET 0x400 +#define WRITE_BUFFER_OFFSET 0x408 + +/* stldr functions timeouts in milliseconds */ +#define STLDR_INIT_TIMEOUT 100000 +#define STLDR_SECTOR_ERASE_TIMEOUT 1000000 +#define STLDR_MASS_ERASE_INT_TIMEOUT 10000 /* for MCU internal flash */ +#define STLDR_MASS_ERASE_EXT_TIMEOUT 600000 /* for other (external) flash types */ +#define STLDR_READ_TIMEOUT 1000 +#define STLDR_WRITE_TIMEOUT 1000 + +/* size and offset of StorageInfo members */ +#define STORAGE_INFO_NAME_OFFSET 0 +#define STORAGE_INFO_NAME_SIZE 100 +#define STORAGE_INFO_TYPE_OFFSET (STORAGE_INFO_NAME_OFFSET + STORAGE_INFO_NAME_SIZE) +#define STORAGE_INFO_BASE_ADDR_OFFSET (STORAGE_INFO_TYPE_OFFSET + 4) +#define STORAGE_INFO_SIZE_OFFSET (STORAGE_INFO_BASE_ADDR_OFFSET + 4) +#define STORAGE_INFO_PAGE_SIZE_OFFSET (STORAGE_INFO_SIZE_OFFSET + 4) +#define STORAGE_INFO_ERASE_VALUE_OFFSET (STORAGE_INFO_PAGE_SIZE_OFFSET + 4) +#define STORAGE_INFO_SECTORS_OFFSET (STORAGE_INFO_ERASE_VALUE_OFFSET + 4) +#define STORAGE_INFO_SECTORS_MAX_ITEMS 10 + +/* size and offset of DeviceSectors members */ +#define DEVICE_SECTORS_SIZE 8 +#define DEVICE_SECTORS_COUNT_OFFSET 0 +#define DEVICE_SECTORS_SIZE_OFFSET 4 + +enum stldr_func_id { + FUNC_ID_INIT, + FUNC_ID_READ, + FUNC_ID_SECTOR_ERASE, + FUNC_ID_MASS_ERASE, + FUNC_ID_WRITE, + FUNC_ID_CHECKSUM, + FUNC_ID_VERIFY, + FUNC_ID_COUNT +}; + +enum stldr_func_type { + STLDR_FUNC_OPTIONAL, + STLDR_FUNC_MANDATORY +}; + +static const struct { + enum stldr_func_id id; + const char *name; + enum stldr_func_type type; +} stldr_functions[] = { + { FUNC_ID_INIT, "Init", STLDR_FUNC_MANDATORY }, + { FUNC_ID_READ, "Read", STLDR_FUNC_OPTIONAL }, + { FUNC_ID_SECTOR_ERASE, "SectorErase", STLDR_FUNC_MANDATORY }, + { FUNC_ID_MASS_ERASE, "MassErase", STLDR_FUNC_OPTIONAL }, + { FUNC_ID_WRITE, "Write", STLDR_FUNC_MANDATORY }, + { FUNC_ID_CHECKSUM, "CheckSum", STLDR_FUNC_OPTIONAL }, + { FUNC_ID_VERIFY, "Verify", STLDR_FUNC_OPTIONAL } +}; + +struct stldr_section { + uint32_t addr; + uint32_t size; + uint8_t *content; + bool do_write; + Elf32_Section idx; + struct list_head lh; +}; + +struct stldr_loader { + bool parsed; + bool relocatable; + bool user_flash_base_addr; + bool user_flash_size; + bool sections_initialized; + uint32_t return_addr; + uint32_t offset; + uint32_t size; + uint32_t func_addr[FUNC_ID_COUNT]; + struct list_head sections; + struct working_area *work_area; +}; + +enum stldr_type { + STLDR_TYPE_MCU_FLASH = 1, + STLDR_TYPE_NAND_FLASH, + STLDR_TYPE_NOR_FLASH, + STLDR_TYPE_SRAM, + STLDR_TYPE_PSRAM, + STLDR_TYPE_PC_CARD, + STLDR_TYPE_SPI_FLASH, + STLDR_TYPE_I2C_FLASH, + STLDR_TYPE_SDRAM, + STLDR_TYPE_I2C_EEPROM +}; + +struct stldr_dev_sector { + uint32_t count; + uint32_t size; +}; + +struct stldr_dev_info { + char name[STORAGE_INFO_NAME_SIZE + 1]; + enum stldr_type type; + uint32_t base_addr; + uint32_t size; + uint32_t page_size; + uint8_t erase_value; + struct stldr_dev_sector sectors[STORAGE_INFO_SECTORS_MAX_ITEMS]; + unsigned int n_sectors; +}; + +struct stldr_flash_bank { + bool probed; + uint32_t data_width; + struct stldr_loader loader; + struct stldr_dev_info dev_info; +}; + +struct stldr_func_args { + uint32_t arg_0; + uint32_t arg_1; + uint32_t arg_2; + uint32_t arg_3; + uint32_t ret; +}; + +static inline void stldr_func_args_set(struct stldr_func_args *args, uint32_t arg_0, + uint32_t arg_1, uint32_t arg_2, uint32_t arg_3) +{ + args->ret = STLDR_FUNC_FAILURE; + args->arg_0 = arg_0; + args->arg_1 = arg_1; + args->arg_2 = arg_2; + args->arg_3 = arg_3; +} + +static inline void stldr_func_args_init(struct stldr_func_args *args) +{ + stldr_func_args_set(args, 0, 0, 0, 0); +} + +static int stldr_parse_dev_info(struct flash_bank *bank, uint8_t *data, int size) +{ + if (size < STORAGE_INFO_SECTORS_OFFSET + (STORAGE_INFO_SECTORS_MAX_ITEMS * DEVICE_SECTORS_SIZE)) { + LOG_ERROR("stldr: invalid StorageInfo size (%d)", size); + return ERROR_FAIL; + } + + struct stldr_flash_bank *stldr_info = bank->driver_priv; + struct stldr_dev_info *dev_info = &stldr_info->dev_info; + + memcpy(dev_info->name, data, STORAGE_INFO_NAME_SIZE); + /* guarantee we have a zero-terminated string */ + dev_info->name[STORAGE_INFO_NAME_SIZE] = 0; + LOG_DEBUG("name %s", dev_info->name); + + dev_info->type = le_to_h_u16(data + STORAGE_INFO_TYPE_OFFSET); + LOG_DEBUG("flash type %x", (unsigned int)dev_info->type); + + dev_info->base_addr = le_to_h_u32(data + STORAGE_INFO_BASE_ADDR_OFFSET); + LOG_DEBUG("base_addr %x", dev_info->base_addr); + + dev_info->size = le_to_h_u32(data + STORAGE_INFO_SIZE_OFFSET); + LOG_DEBUG("size %x", dev_info->size); + + dev_info->erase_value = data[STORAGE_INFO_ERASE_VALUE_OFFSET]; + LOG_DEBUG("erase value %x", dev_info->erase_value); + + dev_info->page_size = le_to_h_u32(data + STORAGE_INFO_PAGE_SIZE_OFFSET); + LOG_DEBUG("page size %x", dev_info->page_size); + + dev_info->n_sectors = STORAGE_INFO_SECTORS_MAX_ITEMS; + LOG_DEBUG("n_sectors %x", dev_info->n_sectors); + + for (int i = 0; i < STORAGE_INFO_SECTORS_MAX_ITEMS; i++) { + const int offset = i * DEVICE_SECTORS_SIZE + STORAGE_INFO_SECTORS_OFFSET; + const uint32_t count = le_to_h_u32(data + offset + DEVICE_SECTORS_COUNT_OFFSET); + if (!count) { + dev_info->n_sectors = i; + break; + } + + dev_info->sectors[i].count = count; + dev_info->sectors[i].size = le_to_h_u32(data + offset + DEVICE_SECTORS_SIZE_OFFSET); + } + + return ERROR_OK; +} + +static void stldr_free_sections(struct stldr_flash_bank *stldr_info) +{ + struct stldr_section *section, *tmp; + + if (!stldr_info->loader.sections_initialized) { + INIT_LIST_HEAD(&stldr_info->loader.sections); + stldr_info->loader.sections_initialized = true; + } else { + list_for_each_entry_safe(section, tmp, &stldr_info->loader.sections, lh) { + list_del(§ion->lh); + free(section->content); + free(section); + } + + INIT_LIST_HEAD(&stldr_info->loader.sections); + } + + stldr_info->loader.parsed = false; + stldr_info->loader.offset = 0; + stldr_info->loader.size = 0; + stldr_info->loader.relocatable = false; +} + +static int stldr_parse(struct flash_bank *bank, const char *stldr_path) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + Elf32_Ehdr elf_header; + Elf32_Shdr section_header; + Elf32_Shdr symtab_sh; + Elf32_Shdr str_sym; + FILE *fp = NULL; + char *symbol_names = NULL; + bool symtab_set = false; + int retval = ERROR_FAIL; + + stldr_free_sections(stldr_info); + + /* Check that path exists and is a regular file, not a directory */ + struct stat st; + if (stat(stldr_path, &st) != 0) { + LOG_ERROR("stldr: cannot access '%s'", stldr_path); + goto out; + } + + if (!S_ISREG(st.st_mode)) { + LOG_ERROR("stldr: '%s' is not a regular file", stldr_path); + goto out; + } + + /* Open file in read mode */ + fp = fopen(stldr_path, "rb"); + if (!fp) { + LOG_ERROR("cannot open '%s'", stldr_path); + goto out; + } + + /* Extract the ELF header */ + if (fread(&elf_header, sizeof(Elf32_Ehdr), 1, fp) != 1) { + LOG_ERROR("stldr: failed to read ELF header"); + goto out; + } + + /* Basic ELF sanity checks (avoid OOB seeks/allocations on malformed files) */ + if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0 || + elf_header.e_ident[EI_CLASS] != ELFCLASS32 || + elf_header.e_shentsize != sizeof(Elf32_Shdr) || + elf_header.e_shnum == 0 || + elf_header.e_shstrndx >= elf_header.e_shnum || + (uint64_t)elf_header.e_shoff + + (uint64_t)elf_header.e_shnum * elf_header.e_shentsize > + (uint64_t)st.st_size) { + LOG_ERROR("stldr: unsupported or malformed ELF file '%s'", stldr_path); + goto out; + } + + /* Extract the section header string table offset */ + Elf32_Off offset_shst = elf_header.e_shoff + + elf_header.e_shstrndx * sizeof(Elf32_Shdr); + + /* Read the section header string table */ + if (fseek(fp, offset_shst, SEEK_SET) != 0) { + LOG_ERROR("stldr: failed to seek to section header string table"); + goto out; + } + if (fread(§ion_header, 1, sizeof(section_header), fp) != sizeof(section_header)) { + LOG_ERROR("stldr: failed to read section header string table header"); + goto out; + } + + /* Iterate all sections and get the loader binary and functions */ + for (unsigned int idx = 0; idx < elf_header.e_shnum; idx++) { + if (fseek(fp, elf_header.e_shoff + idx * sizeof(section_header), SEEK_SET) != 0) { + LOG_ERROR("stldr: failed to seek to section header %u", idx); + goto out; + } + if (fread(§ion_header, 1, sizeof(section_header), fp) != sizeof(section_header)) { + LOG_ERROR("stldr: failed to read section header %u", idx); + goto out; + } + + /* Ignore non loadable sections */ + if ((section_header.sh_flags & SHF_ALLOC) == 0) + continue; + + uint8_t *content = (section_header.sh_type == 8 /* SHT_NOBITS */) ? + calloc(1, section_header.sh_size) : malloc(section_header.sh_size); + if (!content) { + LOG_ERROR("Out of memory"); + goto out; + } + + if (section_header.sh_type != 8 /* SHT_NOBITS */) { + if (fseek(fp, section_header.sh_offset, SEEK_SET) != 0) { + LOG_ERROR("stldr: failed to seek to section content"); + free(content); + goto out; + } + if (fread(content, 1, section_header.sh_size, fp) != section_header.sh_size) { + LOG_ERROR("stldr: failed to read section content"); + free(content); + goto out; + } + } + + struct stldr_section *section = calloc(1, sizeof(struct stldr_section)); + if (!section) { + free(content); + LOG_ERROR("Out of memory"); + goto out; + } + + section->addr = section_header.sh_addr; + section->size = section_header.sh_size; + section->content = content; + section->idx = idx; + /* sh_flags already checked against SHF_ALLOC */ + section->do_write = (section_header.sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR; + + list_add_tail(§ion->lh, &stldr_info->loader.sections); + + /* check if the flash loader is relocatable */ + if ((section->addr & 0xFF000000) == 0 && section->do_write) + stldr_info->loader.relocatable = true; + + LOG_DEBUG("Loader Section found { addr : 0x%08X , size : 0x%08X }", + section->addr, section->size); + } + + /* get symtab */ + symtab_set = false; + for (unsigned int idx = 0; idx < elf_header.e_shnum; idx++) { + if (fseek(fp, elf_header.e_shoff + idx * sizeof(section_header), SEEK_SET) != 0) { + LOG_ERROR("stldr: failed to seek to section header %u", idx); + goto out; + } + if (fread(§ion_header, 1, sizeof(section_header), fp) != sizeof(section_header)) { + LOG_ERROR("stldr: failed to read section header %u", idx); + goto out; + } + + if (section_header.sh_type == SHT_SYMTAB) { + symtab_sh = section_header; + symtab_set = true; + break; + } + } + + if (!symtab_set) { + LOG_ERROR("symtab not found"); + goto out; + } + + /* Get the symbol table string section header */ + if (fseek(fp, elf_header.e_shoff + symtab_sh.sh_link * sizeof(symtab_sh), SEEK_SET) != 0) { + LOG_ERROR("stldr: failed to seek to symbol string table header"); + goto out; + } + if (fread(&str_sym, 1, sizeof(str_sym), fp) != sizeof(str_sym)) { + LOG_ERROR("stldr: failed to read symbol string table header"); + goto out; + } + + /* Create the table of symbol names */ + symbol_names = malloc(str_sym.sh_size); + if (!symbol_names) { + LOG_ERROR("Out of memory"); + goto out; + } + if (fseek(fp, str_sym.sh_offset, SEEK_SET) != 0) { + LOG_ERROR("stldr: failed to seek to symbol string table"); + goto out; + } + if (fread(symbol_names, 1, str_sym.sh_size, fp) != str_sym.sh_size) { + LOG_ERROR("stldr: failed to read symbol string table"); + goto out; + } + + /* Iterate all symbols */ + for (int f = 0; f < FUNC_ID_COUNT; f++) + stldr_info->loader.func_addr[stldr_functions[f].id] = STLDR_FUNC_ADDR_UNKNOWN; + + if (symtab_sh.sh_entsize == 0) { + LOG_ERROR("stldr: invalid symtab entry size (0)"); + goto out; + } + + const unsigned int symbol_count = symtab_sh.sh_size / symtab_sh.sh_entsize; + for (unsigned int j = 0; j < symbol_count; j++) { + Elf32_Sym symbol; + + if (fseek(fp, symtab_sh.sh_offset + j * symtab_sh.sh_entsize, SEEK_SET) != 0) { + LOG_ERROR("stldr: failed to seek to symbol %u", j); + goto out; + } + if (fread(&symbol, sizeof(symbol), 1, fp) != 1) { + LOG_ERROR("stldr: failed to read symbol %u", j); + goto out; + } + + if (symbol.st_name >= str_sym.sh_size) + continue; + + const char *symbol_name = symbol_names + symbol.st_name; + + if (!memchr(symbol_name, '\0', str_sym.sh_size - symbol.st_name)) + continue; + /* check for loader descriptor */ + if (strcmp("StorageInfo", symbol_name) == 0) { + uint8_t *storage_info = malloc(symbol.st_size); + if (!storage_info) { + LOG_ERROR("Out of memory"); + goto out; + } + + struct stldr_section *tmp_sec; + bool found_section = false; + + list_for_each_entry(tmp_sec, &stldr_info->loader.sections, lh) { + LOG_DEBUG("This section starts in %x with size %x", tmp_sec->addr, tmp_sec->size); + + if ((stldr_info->loader.size > tmp_sec->size + tmp_sec->addr) && tmp_sec->do_write) + stldr_info->loader.size = tmp_sec->size + tmp_sec->addr; + + if (tmp_sec->idx == symbol.st_shndx) { + found_section = true; + + if (symbol.st_value < tmp_sec->addr || + (symbol.st_value - tmp_sec->addr) + symbol.st_size > tmp_sec->size) { + free(storage_info); + LOG_ERROR("stldr: StorageInfo symbol out of section bounds"); + goto out; + } + + const uint32_t sym_off = symbol.st_value - tmp_sec->addr; + memcpy(storage_info, tmp_sec->content + sym_off, symbol.st_size); + + retval = stldr_parse_dev_info(bank, storage_info, symbol.st_size); + free(storage_info); + if (retval != ERROR_OK) + goto out; + + break; + } + } + + if (!found_section) { + free(storage_info); + LOG_ERROR("stldr: StorageInfo symbol section not found"); + goto out; + } + } + + /* get loader functions */ + for (int f = 0; f < FUNC_ID_COUNT; f++) { + if (strcmp(stldr_functions[f].name, symbol_name) == 0) { + LOG_DEBUG("Loader Function '%s' found at 0x%08X", symbol_name, symbol.st_value); + stldr_info->loader.func_addr[stldr_functions[f].id] = symbol.st_value; + break; + } + } + } + + /* Check that mandatory functions are found */ + for (int f = 0; f < FUNC_ID_COUNT; f++) { + if (stldr_functions[f].type == STLDR_FUNC_OPTIONAL) + continue; + if (stldr_info->loader.func_addr[stldr_functions[f].id] == STLDR_FUNC_ADDR_UNKNOWN) { + LOG_ERROR("Loader function %s not found", stldr_functions[f].name); + goto out; + } + } + + /* Compute loader size */ + { + struct stldr_section *section; + uint32_t last_section_addr = 0; + uint32_t count_do_write = 0; + + list_for_each_entry(section, &stldr_info->loader.sections, lh) { + if (!section->do_write) + continue; + + count_do_write++; + if (section->addr >= last_section_addr) { + last_section_addr = section->addr; + stldr_info->loader.size += section->size; + } + } + + if (count_do_write > 1) { + LOG_ERROR("FlashLoader contains multiple execute sections"); + goto out; + } + } + + stldr_info->loader.parsed = true; + retval = ERROR_OK; + +out: + free(symbol_names); + + if (fp) + fclose(fp); + + if (retval != ERROR_OK) + stldr_free_sections(stldr_info); + + return retval; +} + +/* flash bank stldr <base> <size> 0 0 <target#> [stldr_path] */ + +FLASH_BANK_COMMAND_HANDLER(stldr_flash_bank_command) +{ + struct stldr_flash_bank *stldr_info; + uint32_t chip_width = 0; + + if (CMD_ARGC != 6 && CMD_ARGC != 7) + return ERROR_COMMAND_SYNTAX_ERROR; + + stldr_info = calloc(sizeof(struct stldr_flash_bank), 1); + if (!stldr_info) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + + bank->driver_priv = stldr_info; + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], chip_width); + if (chip_width == 0) { + chip_width = 32; + LOG_DEBUG("The chip_width argument is set to 32"); + } else if (chip_width & (chip_width - 1)) { + LOG_ERROR("chip_width must be a power of two (bytes)"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + bank->write_start_alignment = chip_width; + bank->write_end_alignment = chip_width; + + stldr_info->loader.user_flash_base_addr = bank->base != 0; + stldr_info->loader.user_flash_size = bank->size != 0; + stldr_info->loader.parsed = false; + stldr_info->probed = false; + + if (CMD_ARGC == 7) /* the stldr_path is specified */ + return stldr_parse(bank, CMD_ARGV[6]); + + return ERROR_OK; +} + +static void stldr_free_driver_priv(struct flash_bank *bank) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + + if (stldr_info->loader.sections.prev || stldr_info->loader.sections.next) { + struct stldr_section *tmp, *section; + list_for_each_entry_safe(section, tmp, &stldr_info->loader.sections, lh) { + free(section->content); + free(section); + } + } + + free(bank->driver_priv); + bank->driver_priv = NULL; +} + +static int stldr_write_loader(struct flash_bank *bank) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + struct target *target = bank->target; + struct stldr_section *section; + uint32_t working_area = target->working_area_phys; + uint32_t working_area_size = target->working_area_size; + + if (stldr_info->loader.relocatable) { + if (target_alloc_working_area_try(bank->target, + stldr_info->loader.size + 4 + STACK_OFFSET, + &stldr_info->loader.work_area) != ERROR_OK) { + LOG_WARNING("no large enough working area available"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* update loader functions offset */ + stldr_info->loader.return_addr = stldr_info->loader.work_area->address; + stldr_info->loader.offset = stldr_info->loader.work_area->address + 4; + LOG_INFO("loader offset 0x%08X", stldr_info->loader.offset); + } + + list_for_each_entry(section, &stldr_info->loader.sections, lh) { + if (!section->do_write) + continue; + + if (!stldr_info->loader.relocatable) { + const uint32_t wa_start = working_area; + const uint32_t wa_end = working_area + working_area_size; + const uint32_t sec_start = section->addr; + const uint32_t sec_end = section->addr + section->size; + if (sec_start < wa_end && sec_end > wa_start) { + LOG_ERROR("loader section overlaps target work area"); + return ERROR_FAIL; + } + + int retval = target_write_buffer(target, section->addr, section->size, section->content); + if (retval != ERROR_OK) + return retval; + + /* update loader functions offset */ + stldr_info->loader.return_addr = section->addr - 4; + stldr_info->loader.offset = section->addr; + LOG_INFO("loader offset 0x%08X", stldr_info->loader.offset); + } else { + int retval = target_write_buffer(target, section->addr + stldr_info->loader.offset, + section->size, section->content); + if (retval != ERROR_OK) { + target_free_working_area(target, stldr_info->loader.work_area); + return retval; + } + } + } + + return ERROR_OK; +} + +static int stldr_exec_function(struct flash_bank *bank, enum stldr_func_id func_id, + int timeout_ms, struct stldr_func_args *args) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + + struct target *target = bank->target; + struct armv7m_algorithm armv7m_info = { + .common_magic = ARMV7M_COMMON_MAGIC, + .core_mode = ARM_MODE_THREAD + }; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (stldr_info->loader.func_addr[func_id] == STLDR_FUNC_ADDR_UNKNOWN) { + LOG_ERROR("Loader function %s not found", stldr_functions[func_id].name); + return ERROR_FAIL; + } + + const uint32_t func_addr = stldr_info->loader.func_addr[func_id] + + (stldr_info->loader.relocatable ? stldr_info->loader.offset : 0); + + LOG_INFO("Running loader function %s(0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32 ")", + stldr_functions[func_id].name, args->arg_0, args->arg_1, args->arg_2, args->arg_3); + + struct reg_param reg_params[6]; + + /* write function parameters */ + init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); + init_reg_param(®_params[2], "r2", 32, PARAM_OUT); + init_reg_param(®_params[3], "r3", 32, PARAM_OUT); + + buf_set_u32(reg_params[0].value, 0, 32, args->arg_0); + buf_set_u32(reg_params[1].value, 0, 32, args->arg_1); + buf_set_u32(reg_params[2].value, 0, 32, args->arg_2); + buf_set_u32(reg_params[3].value, 0, 32, args->arg_3); + + /* write algo stack pointer */ + uint32_t estack; + if (stldr_info->loader.relocatable) + estack = stldr_info->loader.work_area->size + stldr_info->loader.work_area->address; + else + estack = stldr_info->loader.size + stldr_info->loader.offset + STACK_OFFSET; + init_reg_param(®_params[4], "sp", 32, PARAM_OUT); + buf_set_u32(reg_params[4].value, 0, 32, estack); + init_reg_param(®_params[5], "lr", 32, PARAM_OUT); + buf_set_u32(reg_params[5].value, 0, 32, stldr_info->loader.return_addr | 0x1); + + /* execute function */ + breakpoint_add(target, stldr_info->loader.return_addr, 2, BKPT_SOFT); + int retval = target_run_algorithm(target, + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + func_addr, + stldr_info->loader.return_addr, timeout_ms, &armv7m_info); + + breakpoint_remove(target, stldr_info->loader.return_addr); + + if (retval != ERROR_OK) { + LOG_ERROR("Failed to run loader function %s(...) %s", + stldr_functions[func_id].name, + retval == ERROR_TARGET_TIMEOUT ? " : timeout reached" : "" + ); + goto exit; + } + + args->ret = buf_get_u32(reg_params[0].value, 0, 32); + LOG_DEBUG("Loader function %s(...) returned %d", stldr_functions[func_id].name, args->ret); +exit: + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + destroy_reg_param(®_params[3]); + destroy_reg_param(®_params[4]); + destroy_reg_param(®_params[5]); + return retval; +} + +static int stldr_exec_function_deinit(struct flash_bank *bank) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + return target_free_working_area(bank->target, stldr_info->loader.work_area); +} + +static int stldr_exec_function_init(struct flash_bank *bank) +{ + struct stldr_func_args args; + stldr_func_args_init(&args); + + int retval = stldr_write_loader(bank); + if (retval != ERROR_OK) + return retval; + + retval = stldr_exec_function(bank, FUNC_ID_INIT, STLDR_INIT_TIMEOUT, &args); + if (retval != ERROR_OK || args.ret != STLDR_FUNC_SUCCESS) + return ERROR_FLASH_OPERATION_FAILED; + + return ERROR_OK; +} + +static int stldr_exec_function_mass_erase(struct flash_bank *bank) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + + const int timeout = (stldr_info->dev_info.type == STLDR_TYPE_MCU_FLASH) ? + STLDR_MASS_ERASE_INT_TIMEOUT : STLDR_MASS_ERASE_EXT_TIMEOUT; + + struct stldr_func_args args; + stldr_func_args_init(&args); + + int retval = stldr_exec_function_init(bank); + if (retval != ERROR_OK) { + stldr_exec_function_deinit(bank); + return retval; + } + + retval = stldr_exec_function(bank, FUNC_ID_MASS_ERASE, timeout, &args); + if (retval != ERROR_OK || args.ret != STLDR_FUNC_SUCCESS) { + stldr_exec_function_deinit(bank); + return ERROR_FLASH_OPERATION_FAILED; + } + + return stldr_exec_function_deinit(bank); +} + +static int stldr_exec_function_sector_erase(struct flash_bank *bank, + uint32_t start_addr, uint32_t end_addr) +{ + struct stldr_func_args args; + stldr_func_args_set(&args, start_addr, end_addr, 0, 0); + int retval = stldr_exec_function(bank, FUNC_ID_SECTOR_ERASE, STLDR_SECTOR_ERASE_TIMEOUT, &args); + if (retval != ERROR_OK || args.ret != STLDR_FUNC_SUCCESS) + return ERROR_FLASH_OPERATION_FAILED; + + return ERROR_OK; +} + +static int stldr_exec_function_read(struct flash_bank *bank, + uint32_t addr, uint32_t size, uint32_t buffer_addr) +{ + struct stldr_func_args args; + stldr_func_args_set(&args, addr, size, buffer_addr, 0); + int retval = stldr_exec_function(bank, FUNC_ID_READ, STLDR_READ_TIMEOUT, &args); + if (retval != ERROR_OK || args.ret != STLDR_FUNC_SUCCESS) + return ERROR_FLASH_OPERATION_FAILED; + + return ERROR_OK; +} + +static int stldr_exec_function_write(struct flash_bank *bank, + uint32_t addr, uint32_t size, uint32_t buffer_addr) +{ + struct stldr_func_args args; + stldr_func_args_set(&args, addr, size, buffer_addr, 0); + int retval = stldr_exec_function(bank, FUNC_ID_WRITE, STLDR_WRITE_TIMEOUT, &args); + if (retval != ERROR_OK || args.ret != STLDR_FUNC_SUCCESS) + return ERROR_FLASH_OPERATION_FAILED; + + return ERROR_OK; +} + +static int stldr_erase(struct flash_bank *bank, unsigned int first, + unsigned int last) +{ + int retval = stldr_exec_function_init(bank); + if (retval != ERROR_OK) { + stldr_exec_function_deinit(bank); + return retval; + } + + retval = stldr_exec_function_sector_erase(bank, + bank->base + bank->sectors[first].offset, + bank->base + bank->sectors[last].offset); + stldr_exec_function_deinit(bank); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int stldr_protect(struct flash_bank *bank, int set, unsigned int first, + unsigned int last) +{ + LOG_ERROR("The stldr driver does not support option bytes modification"); + + return ERROR_FLASH_OPER_UNSUPPORTED; +} + +static int stldr_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + + int retval = stldr_exec_function_init(bank); + if (retval != ERROR_OK) { + stldr_exec_function_deinit(bank); + return retval; + } + + uint32_t buffer_size = target_get_working_area_avail(bank->target); + /* buffer_size should be multiple of stldr_info->data_width */ + buffer_size &= ~(stldr_info->data_width - 1); + + if (buffer_size < 256) { + LOG_WARNING("large enough working area not available, can't do block memory writes"); + stldr_exec_function_deinit(bank); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + LOG_INFO("workarea size: %x", buffer_size); + LOG_INFO("workarea address: " TARGET_ADDR_FMT, bank->target->working_area_phys); + + /* The flash write must be aligned to the 'stldr_info->data_width' boundary. + * The flash infrastructure ensures it, do just a security check */ + assert(offset % stldr_info->data_width == 0); + assert(count % stldr_info->data_width == 0); + + struct working_area *source; + if (target_alloc_working_area_try(bank->target, buffer_size, &source) != ERROR_OK) { + LOG_WARNING("no large enough working area available"); + stldr_exec_function_deinit(bank); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + uint32_t address = bank->base + offset; + + while (count > 0) { + if (count < buffer_size) + buffer_size = count; + + retval = target_write_buffer(bank->target, source->address, buffer_size, buffer); + if (retval != ERROR_OK) + goto exit_error; + + retval = stldr_exec_function_write(bank, address, buffer_size, source->address); + if (retval != ERROR_OK) + goto exit_error; + + buffer += buffer_size; + address += buffer_size; + count -= buffer_size; + } + stldr_exec_function_deinit(bank); + target_free_working_area(bank->target, source); + return ERROR_OK; + +exit_error: + stldr_exec_function_deinit(bank); + target_free_working_area(bank->target, source); + return ERROR_FLASH_OPERATION_FAILED; +} + +static int stldr_read(struct flash_bank *bank, + uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + struct stldr_dev_info *dev_info = &stldr_info->dev_info; + + /* init is needed for default_flash_read as well */ + int retval = stldr_exec_function_init(bank); + if (retval != ERROR_OK) { + stldr_exec_function_deinit(bank); + return retval; + } + + if (stldr_info->loader.func_addr[FUNC_ID_READ] == 0xFFFFFFFF) { + int ret = default_flash_read(bank, buffer, offset, count); + stldr_exec_function_deinit(bank); + return ret; + } + + /* FIXME the code below is not tested (Read function from stldr) */ + const uint32_t block_size = dev_info->page_size; + + struct working_area *buffer_area; + if (target_alloc_working_area_try(bank->target, block_size, &buffer_area) != ERROR_OK) { + LOG_WARNING("no large enough working area available"); + stldr_exec_function_deinit(bank); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + uint32_t address = bank->base + offset; + while (count > 0) { + const uint32_t read_block_size = MIN(count, block_size); + + retval = stldr_exec_function_read(bank, address, read_block_size, buffer_area->address); + if (retval != ERROR_OK) + goto exit_error; + + retval = target_read_buffer(bank->target, buffer_area->address, read_block_size, buffer); + if (retval != ERROR_OK) + goto exit_error; + + buffer += read_block_size; + address += read_block_size; + count -= read_block_size; + } + + stldr_exec_function_deinit(bank); + target_free_working_area(bank->target, buffer_area); + return ERROR_OK; + +exit_error: + stldr_exec_function_deinit(bank); + target_free_working_area(bank->target, buffer_area); + return ERROR_FLASH_OPERATION_FAILED; +} + +static int stldr_probe(struct flash_bank *bank) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + struct stldr_dev_info *dev_info = &stldr_info->dev_info; + + if (!stldr_info->loader.parsed) { + LOG_ERROR("Please check if you have set correctly the loader file"); + return ERROR_FAIL; + } + + free(bank->sectors); + + if (!stldr_info->loader.user_flash_base_addr) + bank->base = dev_info->base_addr; + if (!stldr_info->loader.user_flash_size) + bank->size = dev_info->size; + + stldr_info->data_width = bank->write_start_alignment; + + bank->num_sectors = 0; + for (unsigned int i = 0; i < dev_info->n_sectors; i++) + bank->num_sectors += dev_info->sectors[i].count; + + bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + if (!bank->sectors) { + LOG_ERROR("failed to allocate bank sectors"); + return ERROR_FAIL; + } + + uint32_t s_i = 0, s_offset = 0; + for (unsigned int i = 0; i < dev_info->n_sectors; i++) { + for (unsigned int j = 0; j < dev_info->sectors[i].count; j++) { + bank->sectors[s_i].offset = s_offset; + bank->sectors[s_i].size = dev_info->sectors[i].size; + bank->sectors[s_i].is_erased = -1; + bank->sectors[s_i].is_protected = -1; + + s_i++; + s_offset += dev_info->sectors[i].size; + } + } + + LOG_DEBUG("Bank (%u) size is %" PRIu32 " kb, base address is " TARGET_ADDR_FMT, + bank->bank_number, bank->size >> 10, bank->base); + + stldr_info->probed = true; + return ERROR_OK; +} + +static int stldr_auto_probe(struct flash_bank *bank) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + + if (stldr_info->probed) + return ERROR_OK; + + return stldr_probe(bank); +} + +static int stldr_get_info(struct flash_bank *bank, struct command_invocation *cmd) +{ + struct stldr_flash_bank *stldr_info = bank->driver_priv; + struct stldr_dev_info *dev_info = &stldr_info->dev_info; + + if (stldr_info->loader.parsed) + command_print_sameline(cmd, "Loader file for %s ", dev_info->name); + else + command_print_sameline(cmd, "Please check if you have set correctly the loader file"); + + return ERROR_OK; +} + +static int stldr_protect_check(struct flash_bank *bank) +{ + /* probe is required before checking protection */ + /* Nothing to do since we set the protection to unknown during the probe */ + LOG_WARNING("The stldr driver does not support flash protection"); + return ERROR_OK; +} + +COMMAND_HANDLER(stldr_handle_set_loader_command) +{ + if (CMD_ARGC < 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank_probe_optional, 0, &bank, false); + if (retval != ERROR_OK) + return retval; + + struct stldr_flash_bank *stldr_info = bank->driver_priv; + stldr_info->probed = false; + + retval = stldr_parse(bank, CMD_ARGV[1]); + if (retval == ERROR_OK) + command_print(CMD, "stldr file parsing succeeded"); + else + command_print(CMD, "stldr file parsing failed"); + + return retval; +} + +COMMAND_HANDLER(stldr_handle_set_size_command) +{ + if (CMD_ARGC < 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank_probe_optional, 0, &bank, false); + if (retval != ERROR_OK) + return retval; + + struct stldr_flash_bank *stldr_info = bank->driver_priv; + stldr_info->probed = false; + + uint32_t bank_size; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], bank_size); + if (bank_size == 0) { + LOG_ERROR("The bank size could not be zero"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + bank->size = bank_size; + stldr_info->loader.user_flash_size = true; + + return retval; +} + +COMMAND_HANDLER(stldr_handle_mass_erase_command) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (retval != ERROR_OK) + return retval; + + retval = stldr_exec_function_mass_erase(bank); + if (retval == ERROR_OK) + command_print(CMD, "stldr mass erase complete"); + else + command_print(CMD, "stldr mass erase failed"); + + return retval; +} + +COMMAND_HANDLER(stldr_handle_init_command) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (retval != ERROR_OK) + return retval; + + retval = stldr_exec_function_init(bank); + if (retval == ERROR_OK) + command_print(CMD, "stldr init complete"); + else + command_print(CMD, "stldr init failed"); + + return retval; +} + +static const struct command_registration stldr_subcommand_handlers[] = { + { + .name = "set_loader", + .handler = stldr_handle_set_loader_command, + .mode = COMMAND_ANY, + .usage = "bank_id path/to/stldr", + .help = "set loader path" + }, + { + .name = "set_size", + .handler = stldr_handle_set_size_command, + .mode = COMMAND_ANY, + .usage = "bank_id size", + .help = "set bank size" + }, + { + .name = "mass_erase", + .handler = stldr_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Erase entire flash bank" + }, + { + .name = "init", + .handler = stldr_handle_init_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Initialize memory" + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration stldr_command_handlers[] = { + { + .name = "stldr", + .mode = COMMAND_ANY, + .help = "stldr flash command group", + .usage = "", + .chain = stldr_subcommand_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +const struct flash_driver stldr_flash = { + .name = "stldr", + .commands = stldr_command_handlers, + .flash_bank_command = stldr_flash_bank_command, + .erase = stldr_erase, + .protect = stldr_protect, + .write = stldr_write, + .read = stldr_read, + .probe = stldr_probe, + .auto_probe = stldr_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = stldr_protect_check, + .info = stldr_get_info, + .free_driver_priv = stldr_free_driver_priv +}; diff --git a/src/helper/replacements.h b/src/helper/replacements.h index 11e3c119e7..d2613f067a 100644 --- a/src/helper/replacements.h +++ b/src/helper/replacements.h @@ -16,6 +16,7 @@ #include <stdint.h> #include <helper/system.h> +#include <helper/bits.h> /* MIN,MAX macros */ #ifndef MIN @@ -260,6 +261,7 @@ typedef uint16_t Elf32_Half; typedef uint32_t Elf32_Off; typedef uint32_t Elf32_Word; typedef uint32_t Elf32_Size; +typedef uint16_t Elf32_Section; #define EI_NIDENT 16 @@ -291,6 +293,34 @@ typedef struct { #define ELFDATA2LSB 1 /* 2's complement, little endian */ #define ELFDATA2MSB 2 /* 2's complement, big endian */ +typedef struct { + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +#define SHT_SYMTAB 2 /* Symbol table */ + +#define SHF_WRITE BIT(0) /* Writable */ +#define SHF_ALLOC BIT(1) /* Occupies memory during execution */ +#define SHF_EXECINSTR BIT(2) /* Executable */ + +typedef struct { + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + typedef struct { Elf32_Word p_type; /* Segment type */ Elf32_Off p_offset; /* Segment file offset */ --
