diff options
Diffstat (limited to 'arch/arm/mach-stm32mp')
20 files changed, 3596 insertions, 24 deletions
diff --git a/arch/arm/mach-stm32mp/Kconfig b/arch/arm/mach-stm32mp/Kconfig index ba965e7b3b..6c995ed8d8 100644 --- a/arch/arm/mach-stm32mp/Kconfig +++ b/arch/arm/mach-stm32mp/Kconfig @@ -86,16 +86,6 @@ config TARGET_DH_STM32MP1_PDK2 endchoice -config STM32MP1_OPTEE - bool "Support trusted boot with TF-A and OP-TEE" - depends on TFABOOT - default n - help - Say Y here to enable boot with TF-A and OP-TEE - Trusted boot chain is : - BootRom => TF-A.stm32 (clock & DDR) => OP-TEE => U-Boot.stm32 - OP-TEE monitor provides ST SMC to access to secure resources - config SYS_TEXT_BASE default 0xC0100000 @@ -117,15 +107,30 @@ config STM32_ETZPC help Say y to enable STM32 Extended TrustZone Protection +config CMD_STM32PROG + bool "command stm32prog for STM32CudeProgrammer" + select DFU + select DFU_RAM + select DFU_VIRT + select PARTITION_TYPE_GUID + imply CMD_GPT if MMC + imply CMD_MTD if MTD + imply DFU_MMC if MMC + imply DFU_MTD if MTD + help + activate a specific command stm32prog for STM32MP soc family + witch update the device with the tools STM32CubeProgrammer, + using UART with STM32 protocol or USB with DFU protocol + NB: access to not volatile memory (NOR/NAND/SD/eMMC) is based + on U-Boot DFU framework + config CMD_STM32KEY bool "command stm32key to fuse public key hash" default y - depends on CMD_FUSE help fuse public key hash in corresponding fuse used to authenticate binary. - config PRE_CON_BUF_ADDR default 0xC02FF000 diff --git a/arch/arm/mach-stm32mp/Makefile b/arch/arm/mach-stm32mp/Makefile index eee39c27c3..66bb8cf92f 100644 --- a/arch/arm/mach-stm32mp/Makefile +++ b/arch/arm/mach-stm32mp/Makefile @@ -10,9 +10,11 @@ obj-y += syscon.o ifdef CONFIG_SPL_BUILD obj-y += spl.o else +obj-$(CONFIG_CMD_STM32PROG) += cmd_stm32prog/ obj-y += bsec.o obj-$(CONFIG_CMD_STM32KEY) += cmd_stm32key.o obj-$(CONFIG_ARMV7_PSCI) += psci.o +obj-$(CONFIG_TFABOOT) += boot_params.o endif obj-$(CONFIG_$(SPL_)DM_REGULATOR) += pwr_regulator.o diff --git a/arch/arm/mach-stm32mp/boot_params.c b/arch/arm/mach-stm32mp/boot_params.c new file mode 100644 index 0000000000..37ee9e1612 --- /dev/null +++ b/arch/arm/mach-stm32mp/boot_params.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + */ + +#include <common.h> +#include <log.h> +#include <asm/sections.h> +#include <asm/system.h> + +/* + * Force data-section, as .bss will not be valid + * when save_boot_params is invoked. + */ +static unsigned long nt_fw_dtb __section(".data"); + +/* + * Save the FDT address provided by TF-A in r2 at boot time + * This function is called from start.S + */ +void save_boot_params(unsigned long r0, unsigned long r1, unsigned long r2, + unsigned long r3) +{ + nt_fw_dtb = r2; + + save_boot_params_ret(); +} + +/* + * Use the saved FDT address provided by TF-A at boot time (NT_FW_CONFIG = + * Non Trusted Firmware configuration file) when the pointer is valid + */ +void *board_fdt_blob_setup(void) +{ + debug("%s: nt_fw_dtb=%lx\n", __func__, nt_fw_dtb); + + /* use external device tree only if address is valid */ + if (nt_fw_dtb >= STM32_DDR_BASE) { + if (fdt_magic(nt_fw_dtb) == FDT_MAGIC) + return (void *)nt_fw_dtb; + debug("%s: DTB not found.\n", __func__); + } + debug("%s: fall back to builtin DTB, %p\n", __func__, &_end); + + return (void *)&_end; +} diff --git a/arch/arm/mach-stm32mp/bsec.c b/arch/arm/mach-stm32mp/bsec.c index 0d5850b4a9..fc39230113 100644 --- a/arch/arm/mach-stm32mp/bsec.c +++ b/arch/arm/mach-stm32mp/bsec.c @@ -5,6 +5,7 @@ #include <common.h> #include <dm.h> +#include <log.h> #include <misc.h> #include <asm/io.h> #include <asm/arch/stm32mp1_smc.h> diff --git a/arch/arm/mach-stm32mp/cmd_stm32key.c b/arch/arm/mach-stm32mp/cmd_stm32key.c index f1f26e7c94..f191085a12 100644 --- a/arch/arm/mach-stm32mp/cmd_stm32key.c +++ b/arch/arm/mach-stm32mp/cmd_stm32key.c @@ -66,8 +66,8 @@ static int confirm_prog(void) return 0; } -static int do_stm32key(cmd_tbl_t *cmdtp, int flag, int argc, - char * const argv[]) +static int do_stm32key(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) { u32 addr; const char *op = argc >= 2 ? argv[1] : NULL; diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile b/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile new file mode 100644 index 0000000000..548a378921 --- /dev/null +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2020, STMicroelectronics - All Rights Reserved +# + +obj-y += cmd_stm32prog.o +obj-y += stm32prog.o +obj-y += stm32prog_serial.o +obj-y += stm32prog_usb.o diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c b/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c new file mode 100644 index 0000000000..0722e4a891 --- /dev/null +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2020, STMicroelectronics - All Rights Reserved + */ + +#include <common.h> +#include <command.h> +#include <dfu.h> +#include <image.h> +#include <asm/arch/stm32prog.h> +#include "stm32prog.h" + +struct stm32prog_data *stm32prog_data; + +static void enable_vidconsole(void) +{ +#ifdef CONFIG_DM_VIDEO + char *stdname; + char buf[64]; + + stdname = env_get("stdout"); + if (!stdname || !strstr(stdname, "vidconsole")) { + if (!stdname) + snprintf(buf, sizeof(buf), "serial,vidconsole"); + else + snprintf(buf, sizeof(buf), "%s,vidconsole", stdname); + env_set("stdout", buf); + } + + stdname = env_get("stderr"); + if (!stdname || !strstr(stdname, "vidconsole")) { + if (!stdname) + snprintf(buf, sizeof(buf), "serial,vidconsole"); + else + snprintf(buf, sizeof(buf), "%s,vidconsole", stdname); + env_set("stderr", buf); + } +#endif +} + +static int do_stm32prog(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + ulong addr, size; + int dev, ret; + enum stm32prog_link_t link = LINK_UNDEFINED; + bool reset = false; + struct image_header_s header; + struct stm32prog_data *data; + u32 uimage, dtb; + + if (argc < 3 || argc > 5) + return CMD_RET_USAGE; + + if (!strcmp(argv[1], "usb")) + link = LINK_USB; + else if (!strcmp(argv[1], "serial")) + link = LINK_SERIAL; + + if (link == LINK_UNDEFINED) { + pr_err("not supported link=%s\n", argv[1]); + return CMD_RET_USAGE; + } + + dev = (int)simple_strtoul(argv[2], NULL, 10); + + addr = STM32_DDR_BASE; + size = 0; + if (argc > 3) { + addr = simple_strtoul(argv[3], NULL, 16); + if (!addr) + return CMD_RET_FAILURE; + } + if (argc > 4) + size = simple_strtoul(argv[4], NULL, 16); + + /* check STM32IMAGE presence */ + if (size == 0 && + !stm32prog_header_check((struct raw_header_s *)addr, &header)) { + size = header.image_length + BL_HEADER_SIZE; + + /* uImage detected in STM32IMAGE, execute the script */ + if (IMAGE_FORMAT_LEGACY == + genimg_get_format((void *)(addr + BL_HEADER_SIZE))) + return image_source_script(addr + BL_HEADER_SIZE, + "script@1"); + } + + enable_vidconsole(); + + data = (struct stm32prog_data *)malloc(sizeof(*data)); + + if (!data) { + pr_err("Alloc failed."); + return CMD_RET_FAILURE; + } + stm32prog_data = data; + + ret = stm32prog_init(data, addr, size); + if (ret) + printf("Invalid or missing layout file."); + + /* prepare DFU for device read/write */ + ret = stm32prog_dfu_init(data); + if (ret) + goto cleanup; + + switch (link) { + case LINK_SERIAL: + ret = stm32prog_serial_init(data, dev); + if (ret) + goto cleanup; + reset = stm32prog_serial_loop(data); + break; + case LINK_USB: + reset = stm32prog_usb_loop(data, dev); + break; + default: + goto cleanup; + } + + uimage = data->uimage; + dtb = data->dtb; + + stm32prog_clean(data); + free(stm32prog_data); + stm32prog_data = NULL; + + puts("Download done\n"); + + if (uimage) { + char boot_addr_start[20]; + char dtb_addr[20]; + char *bootm_argv[5] = { + "bootm", boot_addr_start, "-", dtb_addr, NULL + }; + if (!dtb) + bootm_argv[3] = env_get("fdtcontroladdr"); + else + snprintf(dtb_addr, sizeof(dtb_addr) - 1, + "0x%x", dtb); + + snprintf(boot_addr_start, sizeof(boot_addr_start) - 1, + "0x%x", uimage); + printf("Booting kernel at %s - %s...\n\n\n", + boot_addr_start, bootm_argv[3]); + /* Try bootm for legacy and FIT format image */ + if (genimg_get_format((void *)uimage) != IMAGE_FORMAT_INVALID) + do_bootm(cmdtp, 0, 4, bootm_argv); + else if CONFIG_IS_ENABLED(CMD_BOOTZ) + do_bootz(cmdtp, 0, 4, bootm_argv); + } + + if (reset) { + puts("Reset...\n"); + run_command("reset", 0); + } + + return CMD_RET_SUCCESS; + +cleanup: + stm32prog_clean(data); + free(stm32prog_data); + stm32prog_data = NULL; + + return CMD_RET_FAILURE; +} + +U_BOOT_CMD(stm32prog, 5, 0, do_stm32prog, + "<link> <dev> [<addr>] [<size>]\n" + "start communication with tools STM32Cubeprogrammer on <link> with Flashlayout at <addr>", + "<link> = serial|usb\n" + "<dev> = device instance\n" + "<addr> = address of flashlayout\n" + "<size> = size of flashlayout\n" +); + +bool stm32prog_get_tee_partitions(void) +{ + if (stm32prog_data) + return stm32prog_data->tee_detected; + + return false; +} + +bool stm32prog_get_fsbl_nor(void) +{ + if (stm32prog_data) + return stm32prog_data->fsbl_nor_detected; + + return false; +} diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c new file mode 100644 index 0000000000..c5b8841b23 --- /dev/null +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c @@ -0,0 +1,1745 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2020, STMicroelectronics - All Rights Reserved + */ + +#include <command.h> +#include <console.h> +#include <dfu.h> +#include <malloc.h> +#include <misc.h> +#include <mmc.h> +#include <part.h> +#include <asm/arch/stm32mp1_smc.h> +#include <dm/uclass.h> +#include <jffs2/load_kernel.h> +#include <linux/list.h> +#include <linux/list_sort.h> +#include <linux/mtd/mtd.h> +#include <linux/sizes.h> + +#include "stm32prog.h" + +/* Primary GPT header size for 128 entries : 17kB = 34 LBA of 512B */ +#define GPT_HEADER_SZ 34 + +#define OPT_SELECT BIT(0) +#define OPT_EMPTY BIT(1) +#define OPT_DELETE BIT(2) + +#define IS_SELECT(part) ((part)->option & OPT_SELECT) +#define IS_EMPTY(part) ((part)->option & OPT_EMPTY) +#define IS_DELETE(part) ((part)->option & OPT_DELETE) + +#define ALT_BUF_LEN SZ_1K + +#define ROOTFS_MMC0_UUID \ + EFI_GUID(0xE91C4E10, 0x16E6, 0x4C0E, \ + 0xBD, 0x0E, 0x77, 0xBE, 0xCF, 0x4A, 0x35, 0x82) + +#define ROOTFS_MMC1_UUID \ + EFI_GUID(0x491F6117, 0x415D, 0x4F53, \ + 0x88, 0xC9, 0x6E, 0x0D, 0xE5, 0x4D, 0xEA, 0xC6) + +#define ROOTFS_MMC2_UUID \ + EFI_GUID(0xFD58F1C7, 0xBE0D, 0x4338, \ + 0x88, 0xE9, 0xAD, 0x8F, 0x05, 0x0A, 0xEB, 0x18) + +/* RAW parttion (binary / bootloader) used Linux - reserved UUID */ +#define LINUX_RESERVED_UUID "8DA63339-0007-60C0-C436-083AC8230908" + +/* + * unique partition guid (uuid) for partition named "rootfs" + * on each MMC instance = SD Card or eMMC + * allow fixed kernel bootcmd: "rootf=PARTUID=e91c4e10-..." + */ +static const efi_guid_t uuid_mmc[3] = { + ROOTFS_MMC0_UUID, + ROOTFS_MMC1_UUID, + ROOTFS_MMC2_UUID +}; + +DECLARE_GLOBAL_DATA_PTR; + +/* order of column in flash layout file */ +enum stm32prog_col_t { + COL_OPTION, + COL_ID, + COL_NAME, + COL_TYPE, + COL_IP, + COL_OFFSET, + COL_NB_STM32 +}; + +/* partition handling routines : CONFIG_CMD_MTDPARTS */ +int mtdparts_init(void); +int find_dev_and_part(const char *id, struct mtd_device **dev, + u8 *part_num, struct part_info **part); + +char *stm32prog_get_error(struct stm32prog_data *data) +{ + static const char error_msg[] = "Unspecified"; + + if (strlen(data->error) == 0) + strcpy(data->error, error_msg); + + return data->error; +} + +u8 stm32prog_header_check(struct raw_header_s *raw_header, + struct image_header_s *header) +{ + unsigned int i; + + header->present = 0; + header->image_checksum = 0x0; + header->image_length = 0x0; + + if (!raw_header || !header) { + pr_debug("%s:no header data\n", __func__); + return -1; + } + if (raw_header->magic_number != + (('S' << 0) | ('T' << 8) | ('M' << 16) | (0x32 << 24))) { + pr_debug("%s:invalid magic number : 0x%x\n", + __func__, raw_header->magic_number); + return -2; + } + /* only header v1.0 supported */ + if (raw_header->header_version != 0x00010000) { + pr_debug("%s:invalid header version : 0x%x\n", + __func__, raw_header->header_version); + return -3; + } + if (raw_header->reserved1 != 0x0 || raw_header->reserved2) { + pr_debug("%s:invalid reserved field\n", __func__); + return -4; + } + for (i = 0; i < (sizeof(raw_header->padding) / 4); i++) { + if (raw_header->padding[i] != 0) { + pr_debug("%s:invalid padding field\n", __func__); + return -5; + } + } + header->present = 1; + header->image_checksum = le32_to_cpu(raw_header->image_checksum); + header->image_length = le32_to_cpu(raw_header->image_length); + + return 0; +} + +static u32 stm32prog_header_checksum(u32 addr, struct image_header_s *header) +{ + u32 i, checksum; + u8 *payload; + + /* compute checksum on payload */ + payload = (u8 *)addr; + checksum = 0; + for (i = header->image_length; i > 0; i--) + checksum += *(payload++); + + return checksum; +} + +/* FLASHLAYOUT PARSING *****************************************/ +static int parse_option(struct stm32prog_data *data, + int i, char *p, struct stm32prog_part_t *part) +{ + int result = 0; + char *c = p; + + part->option = 0; + if (!strcmp(p, "-")) + return 0; + + while (*c) { + switch (*c) { + case 'P': + part->option |= OPT_SELECT; + break; + case 'E': + part->option |= OPT_EMPTY; + break; + case 'D': + part->option |= OPT_DELETE; + break; + default: + result = -EINVAL; + stm32prog_err("Layout line %d: invalid option '%c' in %s)", + i, *c, p); + return -EINVAL; + } + c++; + } + if (!(part->option & OPT_SELECT)) { + stm32prog_err("Layout line %d: missing 'P' in option %s", i, p); + return -EINVAL; + } + + return result; +} + +static int parse_id(struct stm32prog_data *data, + int i, char *p, struct stm32prog_part_t *part) +{ + int result = 0; + unsigned long value; + + result = strict_strtoul(p, 0, &value); + part->id = value; + if (result || value > PHASE_LAST_USER) { + stm32prog_err("Layout line %d: invalid phase value = %s", i, p); + result = -EINVAL; + } + + return result; +} + +static int parse_name(struct stm32prog_data *data, + int i, char *p, struct stm32prog_part_t *part) +{ + int result = 0; + + if (strlen(p) < sizeof(part->name)) { + strcpy(part->name, p); + } else { + stm32prog_err("Layout line %d: partition name too long [%d]: %s", + i, strlen(p), p); + result = -EINVAL; + } + + return result; +} + +static int parse_type(struct stm32prog_data *data, + int i, char *p, struct stm32prog_part_t *part) +{ + int result = 0; + int len = 0; + + part->bin_nb = 0; + if (!strncmp(p, "Binary", 6)) { + part->part_type = PART_BINARY; + + /* search for Binary(X) case */ + len = strlen(p); + part->bin_nb = 1; + if (len > 6) { + if (len < 8 || + (p[6] != '(') || + (p[len - 1] != ')')) + result = -EINVAL; + else + part->bin_nb = + simple_strtoul(&p[7], NULL, 10); + } + } else if (!strcmp(p, "System")) { + part->part_type = PART_SYSTEM; + } else if (!strcmp(p, "FileSystem")) { + part->part_type = PART_FILESYSTEM; + } else if (!strcmp(p, "RawImage")) { + part->part_type = RAW_IMAGE; + } else { + result = -EINVAL; + } + if (result) + stm32prog_err("Layout line %d: type parsing error : '%s'", + i, p); + + return result; +} + +static int parse_ip(struct stm32prog_data *data, + int i, char *p, struct stm32prog_part_t *part) +{ + int result = 0; + unsigned int len = 0; + + part->dev_id = 0; + if (!strcmp(p, "none")) { + part->target = STM32PROG_NONE; + } else if (!strncmp(p, "mmc", 3)) { + part->target = STM32PROG_MMC; + len = 3; + } else if (!strncmp(p, "nor", 3)) { + part->target = STM32PROG_NOR; + len = 3; + } else if (!strncmp(p, "nand", 4)) { + part->target = STM32PROG_NAND; + len = 4; + } else if (!strncmp(p, "spi-nand", 8)) { + part->target = STM32PROG_SPI_NAND; + len = 8; + } else if (!strncmp(p, "ram", 3)) { + part->target = STM32PROG_RAM; + len = 0; + } else { + result = -EINVAL; + } + if (len) { + /* only one digit allowed for device id */ + if (strlen(p) != len + 1) { + result = -EINVAL; + } else { + part->dev_id = p[len] - '0'; + if (part->dev_id > 9) + result = -EINVAL; + } + } + if (result) + stm32prog_err("Layout line %d: ip parsing error: '%s'", i, p); + + return result; +} + +static int parse_offset(struct stm32prog_data *data, + int i, char *p, struct stm32prog_part_t *part) +{ + int result = 0; + char *tail; + + part->part_id = 0; + part->addr = 0; + part->size = 0; + /* eMMC boot parttion */ + if (!strncmp(p, "boot", 4)) { + if (strlen(p) != 5) { + result = -EINVAL; + } else { + if (p[4] == '1') + part->part_id = -1; + else if (p[4] == '2') + part->part_id = -2; + else + result = -EINVAL; + } + if (result) + stm32prog_err("Layout line %d: invalid part '%s'", + i, p); + } else { + part->addr = simple_strtoull(p, &tail, 0); + if (tail == p || *tail != '\0') { + stm32prog_err("Layout line %d: invalid offset '%s'", + i, p); + result = -EINVAL; + } + } + + return result; +} + +static +int (* const parse[COL_NB_STM32])(struct stm32prog_data *data, int i, char *p, + struct stm32prog_part_t *part) = { + [COL_OPTION] = parse_option, + [COL_ID] = parse_id, + [COL_NAME] = parse_name, + [COL_TYPE] = parse_type, + [COL_IP] = parse_ip, + [COL_OFFSET] = parse_offset, +}; + +static int parse_flash_layout(struct stm32prog_data *data, + ulong addr, + ulong size) +{ + int column = 0, part_nb = 0, ret; + bool end_of_line, eof; + char *p, *start, *last, *col; + struct stm32prog_part_t *part; + int part_list_size; + int i; + + data->part_nb = 0; + + /* check if STM32image is detected */ + if (!stm32prog_header_check((struct raw_header_s *)addr, + &data->header)) { + u32 checksum; + + addr = addr + BL_HEADER_SIZE; + size = data->header.image_length; + + checksum = stm32prog_header_checksum(addr, &data->header); + if (checksum != data->header.image_checksum) { + stm32prog_err("Layout: invalid checksum : 0x%x expected 0x%x", + checksum, data->header.image_checksum); + return -EIO; + } + } + if (!size) + return -EINVAL; + + start = (char *)addr; + last = start + size; + + *last = 0x0; /* force null terminated string */ + pr_debug("flash layout =\n%s\n", start); + + /* calculate expected number of partitions */ + part_list_size = 1; + p = start; + while (*p && (p < last)) { + if (*p++ == '\n') { + part_list_size++; + if (p < last && *p == '#') + part_list_size--; + } + } + if (part_list_size > PHASE_LAST_USER) { + stm32prog_err("Layout: too many partition (%d)", + part_list_size); + return -1; + } + part = calloc(sizeof(struct stm32prog_part_t), part_list_size); + if (!part) { + stm32prog_err("Layout: alloc failed"); + return -ENOMEM; + } + data->part_array = part; + + /* main parsing loop */ + i = 1; + eof = false; + p = start; + col = start; /* 1st column */ + end_of_line = false; + while (!eof) { + switch (*p) { + /* CR is ignored and replaced by NULL character */ + case '\r': + *p = '\0'; + p++; + continue; + case '\0': + end_of_line = true; + eof = true; + break; + case '\n': + end_of_line = true; + break; + case '\t': + break; + case '#': + /* comment line is skipped */ + if (column == 0 && p == col) { + while ((p < last) && *p) + if (*p++ == '\n') + break; + col = p; + i++; + if (p >= last || !*p) { + eof = true; + end_of_line = true; + } + continue; + } + /* fall through */ + /* by default continue with the next character */ + default: + p++; + continue; + } + + /* replace by \0: allow string parsing for each column */ + *p = '\0'; + p++; + if (p >= last) { + eof = true; + end_of_line = true; + } + + /* skip empty line and multiple TAB in tsv file */ + if (strlen(col) == 0) { + col = p; + /* skip empty line */ + if (column == 0 && end_of_line) { + end_of_line = false; + i++; + } + continue; + } + + if (column < COL_NB_STM32) { + ret = parse[column](data, i, col, part); + if (ret) + return ret; + } + + /* save the beginning of the next column */ + column++; + col = p; + + if (!end_of_line) + continue; + + /* end of the line detected */ + end_of_line = false; + + if (column < COL_NB_STM32) { + stm32prog_err("Layout line %d: no enought column", i); + return -EINVAL; + } + column = 0; + part_nb++; + part++; + i++; + if (part_nb >= part_list_size) { + part = NULL; + if (!eof) { + stm32prog_err("Layout: no enought memory for %d part", + part_nb); + return -EINVAL; + } + } + } + data->part_nb = part_nb; + if (data->part_nb == 0) { + stm32prog_err("Layout: no partition found"); + return -ENODEV; + } + + return 0; +} + +static int __init part_cmp(void *priv, struct list_head *a, struct list_head *b) +{ + struct stm32prog_part_t *parta, *partb; + + parta = container_of(a, struct stm32prog_part_t, list); + partb = container_of(b, struct stm32prog_part_t, list); + + if (parta->part_id != partb->part_id) + return parta->part_id - partb->part_id; + else + return parta->addr > partb->addr ? 1 : -1; +} + +static void get_mtd_by_target(char *string, enum stm32prog_target target, + int dev_id) +{ + const char *dev_str; + + switch (target) { + case STM32PROG_NOR: + dev_str = "nor"; + break; + case STM32PROG_NAND: + dev_str = "nand"; + break; + case STM32PROG_SPI_NAND: + dev_str = "spi-nand"; + break; + default: + dev_str = "invalid"; + break; + } + sprintf(string, "%s%d", dev_str, dev_id); +} + +static int init_device(struct stm32prog_data *data, + struct stm32prog_dev_t *dev) +{ + struct mmc *mmc = NULL; + struct blk_desc *block_dev = NULL; +#ifdef CONFIG_MTD + struct mtd_info *mtd = NULL; + char mtd_id[16]; +#endif + int part_id; + int ret; + u64 first_addr = 0, last_addr = 0; + struct stm32prog_part_t *part, *next_part; + u64 part_addr, part_size; + bool part_found; + const char *part_name; + + switch (dev->target) { +#ifdef CONFIG_MMC + case STM32PROG_MMC: + mmc = find_mmc_device(dev->dev_id); + if (mmc_init(mmc)) { + stm32prog_err("mmc device %d not found", dev->dev_id); + return -ENODEV; + } + block_dev = mmc_get_blk_desc(mmc); + if (!block_dev) { + stm32prog_err("mmc device %d not probed", dev->dev_id); + return -ENODEV; + } + dev->erase_size = mmc->erase_grp_size * block_dev->blksz; + dev->mmc = mmc; + + /* reserve a full erase group for each GTP headers */ + if (mmc->erase_grp_size > GPT_HEADER_SZ) { + first_addr = dev->erase_size; + last_addr = (u64)(block_dev->lba - + mmc->erase_grp_size) * + block_dev->blksz; + } else { + first_addr = (u64)GPT_HEADER_SZ * block_dev->blksz; + last_addr = (u64)(block_dev->lba - GPT_HEADER_SZ - 1) * + block_dev->blksz; + } + pr_debug("MMC %d: lba=%ld blksz=%ld\n", dev->dev_id, + block_dev->lba, block_dev->blksz); + pr_debug(" available address = 0x%llx..0x%llx\n", + first_addr, last_addr); + pr_debug(" full_update = %d\n", dev->full_update); + break; +#endif +#ifdef CONFIG_MTD + case STM32PROG_NOR: + case STM32PROG_NAND: + case STM32PROG_SPI_NAND: + get_mtd_by_target(mtd_id, dev->target, dev->dev_id); + pr_debug("%s\n", mtd_id); + + mtdparts_init(); + mtd = get_mtd_device_nm(mtd_id); + if (IS_ERR(mtd)) { + stm32prog_err("MTD device %s not found", mtd_id); + return -ENODEV; + } + first_addr = 0; + last_addr = mtd->size; + dev->erase_size = mtd->erasesize; + pr_debug("MTD device %s: size=%lld erasesize=%d\n", + mtd_id, mtd->size, mtd->erasesize); + pr_debug(" available address = 0x%llx..0x%llx\n", + first_addr, last_addr); + dev->mtd = mtd; + break; +#endif + case STM32PROG_RAM: + first_addr = gd->bd->bi_dram[0].start; + last_addr = first_addr + gd->bd->bi_dram[0].size; + dev->erase_size = 1; + break; + default: + stm32prog_err("unknown device type = %d", dev->target); + return -ENODEV; + } + pr_debug(" erase size = 0x%x\n", dev->erase_size); + pr_debug(" full_update = %d\n", dev->full_update); + + /* order partition list in offset order */ + list_sort(NULL, &dev->part_list, &part_cmp); + part_id = 1; + pr_debug("id : Opt Phase Name target.n dev.n addr size part_off part_size\n"); + list_for_each_entry(part, &dev->part_list, list) { + if (part->bin_nb > 1) { + if ((dev->target != STM32PROG_NAND && + dev->target != STM32PROG_SPI_NAND) || + part->id >= PHASE_FIRST_USER || + strncmp(part->name, "fsbl", 4)) { + stm32prog_err("%s (0x%x): multiple binary %d not supported", + part->name, part->id, + part->bin_nb); + return -EINVAL; + } + } + if (part->part_type == RAW_IMAGE) { + part->part_id = 0x0; + part->addr = 0x0; + if (block_dev) + part->size = block_dev->lba * block_dev->blksz; + else + part->size = last_addr; + pr_debug("-- : %1d %02x %14s %02d.%d %02d.%02d %08llx %08llx\n", + part->option, part->id, part->name, + part->part_type, part->bin_nb, part->target, + part->dev_id, part->addr, part->size); + continue; + } + if (part->part_id < 0) { /* boot hw partition for eMMC */ + if (mmc) { + part->size = mmc->capacity_boot; + } else { + stm32prog_err("%s (0x%x): hw partition not expected : %d", + part->name, part->id, + part->part_id); + return -ENODEV; + } + } else { + part->part_id = part_id++; + + /* last partition : size to the end of the device */ + if (part->list.next != &dev->part_list) { + next_part = + container_of(part->list.next, + struct stm32prog_part_t, + list); + if (part->addr < next_part->addr) { + part->size = next_part->addr - + part->addr; + } else { + stm32prog_err("%s (0x%x): same address : 0x%llx == %s (0x%x): 0x%llx", + part->name, part->id, + part->addr, + next_part->name, + next_part->id, + next_part->addr); + return -EINVAL; + } + } else { + if (part->addr <= last_addr) { + part->size = last_addr - part->addr; + } else { + stm32prog_err("%s (0x%x): invalid address 0x%llx (max=0x%llx)", + part->name, part->id, + part->addr, last_addr); + return -EINVAL; + } + } + if (part->addr < first_addr) { + stm32prog_err("%s (0x%x): invalid address 0x%llx (min=0x%llx)", + part->name, part->id, + part->addr, first_addr); + return -EINVAL; + } + } + if ((part->addr & ((u64)part->dev->erase_size - 1)) != 0) { + stm32prog_err("%s (0x%x): not aligned address : 0x%llx on erase size 0x%x", + part->name, part->id, part->addr, + part->dev->erase_size); + return -EINVAL; + } + pr_debug("%02d : %1d %02x %14s %02d.%d %02d.%02d %08llx %08llx", + part->part_id, part->option, part->id, part->name, + part->part_type, part->bin_nb, part->target, + part->dev_id, part->addr, part->size); + + part_addr = 0; + part_size = 0; + part_found = false; + + /* check coherency with existing partition */ + if (block_dev) { + /* + * block devices with GPT: check user partition size + * only for partial update, the GPT partions are be + * created for full update + */ + if (dev->full_update || part->part_id < 0) { + pr_debug("\n"); + continue; + } + struct disk_partition partinfo; + + ret = part_get_info(block_dev, part->part_id, + &partinfo); + + if (ret) { + stm32prog_err("%s (0x%x):Couldn't find part %d on device mmc %d", + part->name, part->id, + part_id, part->dev_id); + return -ENODEV; + } + part_addr = (u64)partinfo.start * partinfo.blksz; + part_size = (u64)partinfo.size * partinfo.blksz; + part_name = (char *)partinfo.name; + part_found = true; + } + +#ifdef CONFIG_MTD + if (mtd) { + char mtd_part_id[32]; + struct part_info *mtd_part; + struct mtd_device *mtd_dev; + u8 part_num; + + sprintf(mtd_part_id, "%s,%d", mtd_id, + part->part_id - 1); + ret = find_dev_and_part(mtd_part_id, &mtd_dev, + &part_num, &mtd_part); + if (ret != 0) { + stm32prog_err("%s (0x%x): Invalid MTD partition %s", + part->name, part->id, + mtd_part_id); + return -ENODEV; + } + part_addr = mtd_part->offset; + part_size = mtd_part->size; + part_name = mtd_part->name; + part_found = true; + } +#endif + if (!part_found) { + stm32prog_err("%s (0x%x): Invalid partition", + part->name, part->id); + pr_debug("\n"); + continue; + } + + pr_debug(" %08llx %08llx\n", part_addr, part_size); + + if (part->addr != part_addr) { + stm32prog_err("%s (0x%x): Bad address for partition %d (%s) = 0x%llx <> 0x%llx expected", + part->name, part->id, part->part_id, + part_name, part->addr, part_addr); + return -ENODEV; + } + if (part->size != part_size) { + stm32prog_err("%s (0x%x): Bad size for partition %d (%s) at 0x%llx = 0x%llx <> 0x%llx expected", + part->name, part->id, part->part_id, + part_name, part->addr, part->size, + part_size); + return -ENODEV; + } + } + return 0; +} + +static int treat_partition_list(struct stm32prog_data *data) +{ + int i, j; + struct stm32prog_part_t *part; + + for (j = 0; j < STM32PROG_MAX_DEV; j++) { + data->dev[j].target = STM32PROG_NONE; + INIT_LIST_HEAD(&data->dev[j].part_list); + } + + data->tee_detected = false; + data->fsbl_nor_detected = false; + for (i = 0; i < data->part_nb; i++) { + part = &data->part_array[i]; + part->alt_id = -1; + + /* skip partition with IP="none" */ + if (part->target == STM32PROG_NONE) { + if (IS_SELECT(part)) { + stm32prog_err("Layout: selected none phase = 0x%x", + part->id); + return -EINVAL; + } + continue; + } + + if (part->id == PHASE_FLASHLAYOUT || + part->id > PHASE_LAST_USER) { + stm32prog_err("Layout: invalid phase = 0x%x", + part->id); + return -EINVAL; + } + for (j = i + 1; j < data->part_nb; j++) { + if (part->id == data->part_array[j].id) { + stm32prog_err("Layout: duplicated phase 0x%x at line %d and %d", + part->id, i, j); + return -EINVAL; + } + } + for (j = 0; j < STM32PROG_MAX_DEV; j++) { + if (data->dev[j].target == STM32PROG_NONE) { + /* new device found */ + data->dev[j].target = part->target; + data->dev[j].dev_id = part->dev_id; + data->dev[j].full_update = true; + data->dev_nb++; + break; + } else if ((part->target == data->dev[j].target) && + (part->dev_id == data->dev[j].dev_id)) { + break; + } + } + if (j == STM32PROG_MAX_DEV) { + stm32prog_err("Layout: too many device"); + return -EINVAL; + } + switch (part->target) { + case STM32PROG_NOR: + if (!data->fsbl_nor_detected && + !strncmp(part->name, "fsbl", 4)) + data->fsbl_nor_detected = true; + /* fallthrough */ + case STM32PROG_NAND: + case STM32PROG_SPI_NAND: + if (!data->tee_detected && + !strncmp(part->name, "tee", 3)) + data->tee_detected = true; + break; + default: + break; + } + part->dev = &data->dev[j]; + if (!IS_SELECT(part)) + part->dev->full_update = false; + list_add_tail(&part->list, &data->dev[j].part_list); + } + + return 0; +} + +static int create_partitions(struct stm32prog_data *data) +{ +#ifdef CONFIG_MMC + int offset = 0; + const int buflen = SZ_8K; + char *buf; + char uuid[UUID_STR_LEN + 1]; + unsigned char *uuid_bin; + unsigned int mmc_id; + int i; + bool rootfs_found; + struct stm32prog_part_t *part; + + buf = malloc(buflen); + if (!buf) + return -ENOMEM; + + puts("partitions : "); + /* initialize the selected device */ + for (i = 0; i < data->dev_nb; i++) { + /* create gpt partition support only for full update on MMC */ + if (data->dev[i].target != STM32PROG_MMC || + !data->dev[i].full_update) + continue; + + offset = 0; + rootfs_found = false; + memset(buf, 0, buflen); + + list_for_each_entry(part, &data->dev[i].part_list, list) { + /* skip eMMC boot partitions */ + if (part->part_id < 0) + continue; + /* skip Raw Image */ + if (part->part_type == RAW_IMAGE) + continue; + + if (offset + 100 > buflen) { + pr_debug("\n%s: buffer too small, %s skippped", + __func__, part->name); + continue; + } + + if (!offset) + offset += sprintf(buf, "gpt write mmc %d \"", + data->dev[i].dev_id); + + offset += snprintf(buf + offset, buflen - offset, + "name=%s,start=0x%llx,size=0x%llx", + part->name, + part->addr, + part->size); + + if (part->part_type == PART_BINARY) + offset += snprintf(buf + offset, + buflen - offset, + ",type=" + LINUX_RESERVED_UUID); + else + offset += snprintf(buf + offset, + buflen - offset, + ",type=linux"); + + if (part->part_type == PART_SYSTEM) + offset += snprintf(buf + offset, + buflen - offset, + ",bootable"); + + if (!rootfs_found && !strcmp(part->name, "rootfs")) { + mmc_id = part->dev_id; + rootfs_found = true; + if (mmc_id < ARRAY_SIZE(uuid_mmc)) { + uuid_bin = + (unsigned char *)uuid_mmc[mmc_id].b; + uuid_bin_to_str(uuid_bin, uuid, + UUID_STR_FORMAT_GUID); + offset += snprintf(buf + offset, + buflen - offset, + ",uuid=%s", uuid); + } + } + + offset += snprintf(buf + offset, buflen - offset, ";"); + } + + if (offset) { + offset += snprintf(buf + offset, buflen - offset, "\""); + pr_debug("\ncmd: %s\n", buf); + if (run_command(buf, 0)) { + stm32prog_err("GPT partitionning fail: %s", + buf); + free(buf); + + return -1; + } + } + + if (data->dev[i].mmc) + part_init(mmc_get_blk_desc(data->dev[i].mmc)); + +#ifdef DEBUG + sprintf(buf, "gpt verify mmc %d", data->dev[i].dev_id); + pr_debug("\ncmd: %s", buf); + if (run_command(buf, 0)) + printf("fail !\n"); + else + printf("OK\n"); + + sprintf(buf, "part list mmc %d", data->dev[i].dev_id); + run_command(buf, 0); +#endif + } + puts("done\n"); + +#ifdef DEBUG + run_command("mtd list", 0); +#endif + free(buf); +#endif + + return 0; +} + +static int stm32prog_alt_add(struct stm32prog_data *data, + struct dfu_entity *dfu, + struct stm32prog_part_t *part) +{ + int ret = 0; + int offset = 0; + char devstr[10]; + char dfustr[10]; + char buf[ALT_BUF_LEN]; + u32 size; + char multiplier, type; + + /* max 3 digit for sector size */ + if (part->size > SZ_1M) { + size = (u32)(part->size / SZ_1M); + multiplier = 'M'; + } else if (part->size > SZ_1K) { + size = (u32)(part->size / SZ_1K); + multiplier = 'K'; + } else { + size = (u32)part->size; + multiplier = 'B'; + } + if (IS_SELECT(part) && !IS_EMPTY(part)) + type = 'e'; /*Readable and Writeable*/ + else + type = 'a';/*Readable*/ + + memset(buf, 0, sizeof(buf)); + offset = snprintf(buf, ALT_BUF_LEN - offset, + "@%s/0x%02x/1*%d%c%c ", + part->name, part->id, + size, multiplier, type); + + if (part->target == STM32PROG_RAM) { + offset += snprintf(buf + offset, ALT_BUF_LEN - offset, + "ram 0x%llx 0x%llx", + part->addr, part->size); + } else if (part->part_type == RAW_IMAGE) { + u64 dfu_size; + + if (part->dev->target == STM32PROG_MMC) + dfu_size = part->size / part->dev->mmc->read_bl_len; + else + dfu_size = part->size; + offset += snprintf(buf + offset, ALT_BUF_LEN - offset, + "raw 0x0 0x%llx", dfu_size); + } else if (part->part_id < 0) { + u64 nb_blk = part->size / part->dev->mmc->read_bl_len; + + offset += snprintf(buf + offset, ALT_BUF_LEN - offset, + "raw 0x%llx 0x%llx", + part->addr, nb_blk); + offset += snprintf(buf + offset, ALT_BUF_LEN - offset, + " mmcpart %d;", -(part->part_id)); + } else { + if (part->part_type == PART_SYSTEM && + (part->target == STM32PROG_NAND || + part->target == STM32PROG_NOR || + part->target == STM32PROG_SPI_NAND)) + offset += snprintf(buf + offset, + ALT_BUF_LEN - offset, + "partubi"); + else + offset += snprintf(buf + offset, + ALT_BUF_LEN - offset, + "part"); + /* dev_id requested by DFU MMC */ + if (part->target == STM32PROG_MMC) + offset += snprintf(buf + offset, ALT_BUF_LEN - offset, + " %d", part->dev_id); + offset += snprintf(buf + offset, ALT_BUF_LEN - offset, + " %d;", part->part_id); + } + switch (part->target) { +#ifdef CONFIG_MMC + case STM32PROG_MMC: + sprintf(dfustr, "mmc"); + sprintf(devstr, "%d", part->dev_id); + break; +#endif +#ifdef CONFIG_MTD + case STM32PROG_NAND: + case STM32PROG_NOR: + case STM32PROG_SPI_NAND: + sprintf(dfustr, "mtd"); + get_mtd_by_target(devstr, part->target, part->dev_id); + break; +#endif + case STM32PROG_RAM: + sprintf(dfustr, "ram"); + sprintf(devstr, "0"); + break; + default: + stm32prog_err("invalid target: %d", part->target); + return -ENODEV; + } + pr_debug("dfu_alt_add(%s,%s,%s)\n", dfustr, devstr, buf); + ret = dfu_alt_add(dfu, dfustr, devstr, buf); + pr_debug("dfu_alt_add(%s,%s,%s) result %d\n", + dfustr, devstr, buf, ret); + + return ret; +} + +static int stm32prog_alt_add_virt(struct dfu_entity *dfu, + char *name, int phase, int size) +{ + int ret = 0; + char devstr[4]; + char buf[ALT_BUF_LEN]; + + sprintf(devstr, "%d", phase); + sprintf(buf, "@%s/0x%02x/1*%dBe", name, phase, size); + ret = dfu_alt_add(dfu, "virt", devstr, buf); + pr_debug("dfu_alt_add(virt,%s,%s) result %d\n", devstr, buf, ret); + + return ret; +} + +static int dfu_init_entities(struct stm32prog_data *data) +{ + int ret = 0; + int phase, i, alt_id; + struct stm32prog_part_t *part; + struct dfu_entity *dfu; + int alt_nb; + + alt_nb = 3; /* number of virtual = CMD, OTP, PMIC*/ + if (data->part_nb == 0) + alt_nb++; /* +1 for FlashLayout */ + else + for (i = 0; i < data->part_nb; i++) { + if (data->part_array[i].target != STM32PROG_NONE) + alt_nb++; + } + + if (dfu_alt_init(alt_nb, &dfu)) + return -ENODEV; + + puts("DFU alt info setting: "); + if (data->part_nb) { + alt_id = 0; + for (phase = 1; + (phase <= PHASE_LAST_USER) && + (alt_id < alt_nb) && !ret; + phase++) { + /* ordering alt setting by phase id */ + part = NULL; + for (i = 0; i < data->part_nb; i++) { + if (phase == data->part_array[i].id) { + part = &data->part_array[i]; + break; + } + } + if (!part) + continue; + if (part->target == STM32PROG_NONE) + continue; + part->alt_id = alt_id; + alt_id++; + + ret = stm32prog_alt_add(data, dfu, part); + } + } else { + char buf[ALT_BUF_LEN]; + + sprintf(buf, "@FlashLayout/0x%02x/1*256Ke ram %x 40000", + PHASE_FLASHLAYOUT, STM32_DDR_BASE); + ret = dfu_alt_add(dfu, "ram", NULL, buf); + pr_debug("dfu_alt_add(ram, NULL,%s) result %d\n", buf, ret); + } + + if (!ret) + ret = stm32prog_alt_add_virt(dfu, "virtual", PHASE_CMD, 512); + + if (!ret) + ret = stm32prog_alt_add_virt(dfu, "OTP", PHASE_OTP, 512); + + if (!ret && CONFIG_IS_ENABLED(DM_PMIC)) + ret = stm32prog_alt_add_virt(dfu, "PMIC", PHASE_PMIC, 8); + + if (ret) + stm32prog_err("dfu init failed: %d", ret); + puts("done\n"); + +#ifdef DEBUG + dfu_show_entities(); +#endif + return ret; +} + +int stm32prog_otp_write(struct stm32prog_data *data, u32 offset, u8 *buffer, + long *size) +{ + pr_debug("%s: %x %lx\n", __func__, offset, *size); + + if (!data->otp_part) { + data->otp_part = memalign(CONFIG_SYS_CACHELINE_SIZE, OTP_SIZE); + if (!data->otp_part) + return -ENOMEM; + } + + if (!offset) + memset(data->otp_part, 0, OTP_SIZE); + + if (offset + *size > OTP_SIZE) + *size = OTP_SIZE - offset; + + memcpy((void *)((u32)data->otp_part + offset), buffer, *size); + + return 0; +} + +int stm32prog_otp_read(struct stm32prog_data *data, u32 offset, u8 *buffer, + long *size) +{ +#ifndef CONFIG_ARM_SMCCC + stm32prog_err("OTP update not supported"); + + return -1; +#else + int result = 0; + + pr_debug("%s: %x %lx\n", __func__, offset, *size); + /* alway read for first packet */ + if (!offset) { + if (!data->otp_part) + data->otp_part = + memalign(CONFIG_SYS_CACHELINE_SIZE, OTP_SIZE); + + if (!data->otp_part) { + result = -ENOMEM; + goto end_otp_read; + } + + /* init struct with 0 */ + memset(data->otp_part, 0, OTP_SIZE); + + /* call the service */ + result = stm32_smc_exec(STM32_SMC_BSEC, STM32_SMC_READ_ALL, + (u32)data->otp_part, 0); + if (result) + goto end_otp_read; + } + + if (!data->otp_part) { + result = -ENOMEM; + goto end_otp_read; + } + + if (offset + *size > OTP_SIZE) + *size = OTP_SIZE - offset; + memcpy(buffer, (void *)((u32)data->otp_part + offset), *size); + +end_otp_read: + pr_debug("%s: result %i\n", __func__, result); + + return result; +#endif +} + +int stm32prog_otp_start(struct stm32prog_data *data) +{ +#ifndef CONFIG_ARM_SMCCC + stm32prog_err("OTP update not supported"); + + return -1; +#else + int result = 0; + struct arm_smccc_res res; + + if (!data->otp_part) { + stm32prog_err("start OTP without data"); + return -1; + } + + arm_smccc_smc(STM32_SMC_BSEC, STM32_SMC_WRITE_ALL, + (u32)data->otp_part, 0, 0, 0, 0, 0, &res); + + if (!res.a0) { + switch (res.a1) { + case 0: + result = 0; + break; + case 1: + stm32prog_err("Provisioning"); + result = 0; + break; + default: + pr_err("%s: OTP incorrect value (err = %ld)\n", + __func__, res.a1); + result = -EINVAL; + break; + } + } else { + pr_err("%s: Failed to exec svc=%x op=%x in secure mode (err = %ld)\n", + __func__, STM32_SMC_BSEC, STM32_SMC_WRITE_ALL, res.a0); + result = -EINVAL; + } + + free(data->otp_part); + data->otp_part = NULL; + pr_debug("%s: result %i\n", __func__, result); + + return result; +#endif +} + +int stm32prog_pmic_write(struct stm32prog_data *data, u32 offset, u8 *buffer, + long *size) +{ + pr_debug("%s: %x %lx\n", __func__, offset, *size); + + if (!offset) + memset(data->pmic_part, 0, PMIC_SIZE); + + if (offset + *size > PMIC_SIZE) + *size = PMIC_SIZE - offset; + + memcpy(&data->pmic_part[offset], buffer, *size); + + return 0; +} + +int stm32prog_pmic_read(struct stm32prog_data *data, u32 offset, u8 *buffer, + long *size) +{ + int result = 0, ret; + struct udevice *dev; + + if (!CONFIG_IS_ENABLED(PMIC_STPMIC1)) { + stm32prog_err("PMIC update not supported"); + + return -EOPNOTSUPP; + } + + pr_debug("%s: %x %lx\n", __func__, offset, *size); + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_GET_DRIVER(stpmic1_nvm), + &dev); + if (ret) + return ret; + + /* alway request PMIC for first packet */ + if (!offset) { + /* init struct with 0 */ + memset(data->pmic_part, 0, PMIC_SIZE); + + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_GET_DRIVER(stpmic1_nvm), + &dev); + if (ret) + return ret; + + ret = misc_read(dev, 0xF8, data->pmic_part, PMIC_SIZE); + if (ret < 0) { + result = ret; + goto end_pmic_read; + } + if (ret != PMIC_SIZE) { + result = -EACCES; + goto end_pmic_read; + } + } + + if (offset + *size > PMIC_SIZE) + *size = PMIC_SIZE - offset; + + memcpy(buffer, &data->pmic_part[offset], *size); + +end_pmic_read: + pr_debug("%s: result %i\n", __func__, result); + return result; +} + +int stm32prog_pmic_start(struct stm32prog_data *data) +{ + int ret; + struct udevice *dev; + + if (!CONFIG_IS_ENABLED(PMIC_STPMIC1)) { + stm32prog_err("PMIC update not supported"); + + return -EOPNOTSUPP; + } + + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_GET_DRIVER(stpmic1_nvm), + &dev); + if (ret) + return ret; + + return misc_write(dev, 0xF8, data->pmic_part, PMIC_SIZE); +} + +/* copy FSBL on NAND to improve reliability on NAND */ +static int stm32prog_copy_fsbl(struct stm32prog_part_t *part) +{ + int ret, i; + void *fsbl; + struct image_header_s header; + struct raw_header_s raw_header; + struct dfu_entity *dfu; + long size, offset; + + if (part->target != STM32PROG_NAND && + part->target != STM32PROG_SPI_NAND) + return -1; + + dfu = dfu_get_entity(part->alt_id); + + /* read header */ + dfu_transaction_cleanup(dfu); + size = BL_HEADER_SIZE; + ret = dfu->read_medium(dfu, 0, (void *)&raw_header, &size); + if (ret) + return ret; + if (stm32prog_header_check(&raw_header, &header)) + return -1; + + /* read header + payload */ + size = header.image_length + BL_HEADER_SIZE; + size = round_up(size, part->dev->mtd->erasesize); + fsbl = calloc(1, size); + if (!fsbl) + return -ENOMEM; + ret = dfu->read_medium(dfu, 0, fsbl, &size); + pr_debug("%s read size=%lx ret=%d\n", __func__, size, ret); + if (ret) + goto error; + + dfu_transaction_cleanup(dfu); + offset = 0; + for (i = part->bin_nb - 1; i > 0; i--) { + offset += size; + /* write to the next erase block */ + ret = dfu->write_medium(dfu, offset, fsbl, &size); + pr_debug("%s copy at ofset=%lx size=%lx ret=%d", + __func__, offset, size, ret); + if (ret) + goto error; + } + +error: + free(fsbl); + return ret; +} + +static void stm32prog_end_phase(struct stm32prog_data *data) +{ + if (data->phase == PHASE_FLASHLAYOUT) { + if (parse_flash_layout(data, STM32_DDR_BASE, 0)) + stm32prog_err("Layout: invalid FlashLayout"); + return; + } + + if (!data->cur_part) + return; + + if (data->cur_part->target == STM32PROG_RAM) { + if (data->cur_part->part_type == PART_SYSTEM) + data->uimage = data->cur_part->addr; + if (data->cur_part->part_type == PART_FILESYSTEM) + data->dtb = data->cur_part->addr; + } + + if (CONFIG_IS_ENABLED(MMC) && + data->cur_part->part_id < 0) { + char cmdbuf[60]; + + sprintf(cmdbuf, "mmc bootbus %d 0 0 0; mmc partconf %d 1 %d 0", + data->cur_part->dev_id, data->cur_part->dev_id, + -(data->cur_part->part_id)); + if (run_command(cmdbuf, 0)) { + stm32prog_err("commands '%s' failed", cmdbuf); + return; + } + } + + if (CONFIG_IS_ENABLED(MTD) && + data->cur_part->bin_nb > 1) { + if (stm32prog_copy_fsbl(data->cur_part)) { + stm32prog_err("%s (0x%x): copy of fsbl failed", + data->cur_part->name, data->cur_part->id); + return; + } + } +} + +void stm32prog_do_reset(struct stm32prog_data *data) +{ + if (data->phase == PHASE_RESET) { + data->phase = PHASE_DO_RESET; + puts("Reset requested\n"); + } +} + +void stm32prog_next_phase(struct stm32prog_data *data) +{ + int phase, i; + struct stm32prog_part_t *part; + bool found; + + phase = data->phase; + switch (phase) { + case PHASE_RESET: + case PHASE_END: + case PHASE_DO_RESET: + return; + } + + /* found next selected partition */ + data->dfu_seq = 0; + data->cur_part = NULL; + data->phase = PHASE_END; + found = false; + do { + phase++; + if (phase > PHASE_LAST_USER) + break; + for (i = 0; i < data->part_nb; i++) { + part = &data->part_array[i]; + if (part->id == phase) { + if (IS_SELECT(part) && !IS_EMPTY(part)) { + data->cur_part = part; + data->phase = phase; + found = true; + } + break; + } + } + } while (!found); + + if (data->phase == PHASE_END) + puts("Phase=END\n"); +} + +static int part_delete(struct stm32prog_data *data, + struct stm32prog_part_t *part) +{ + int ret = 0; +#ifdef CONFIG_MMC + unsigned long blks, blks_offset, blks_size; + struct blk_desc *block_dev = NULL; + #endif +#ifdef CONFIG_MTD + char cmdbuf[40]; + char devstr[10]; +#endif + + printf("Erasing %s ", part->name); + switch (part->target) { +#ifdef CONFIG_MMC + case STM32PROG_MMC: + printf("on mmc %d: ", part->dev->dev_id); + block_dev = mmc_get_blk_desc(part->dev->mmc); + blks_offset = lldiv(part->addr, part->dev->mmc->read_bl_len); + blks_size = lldiv(part->size, part->dev->mmc->read_bl_len); + /* -1 or -2 : delete boot partition of MMC + * need to switch to associated hwpart 1 or 2 + */ + if (part->part_id < 0) + if (blk_select_hwpart_devnum(IF_TYPE_MMC, + part->dev->dev_id, + -part->part_id)) + return -1; + + blks = blk_derase(block_dev, blks_offset, blks_size); + + /* return to user partition */ + if (part->part_id < 0) + blk_select_hwpart_devnum(IF_TYPE_MMC, + part->dev->dev_id, 0); + if (blks != blks_size) { + ret = -1; + stm32prog_err("%s (0x%x): MMC erase failed", + part->name, part->id); + } + break; +#endif +#ifdef CONFIG_MTD + case STM32PROG_NOR: + case STM32PROG_NAND: + case STM32PROG_SPI_NAND: + get_mtd_by_target(devstr, part->target, part->dev->dev_id); + printf("on %s: ", devstr); + sprintf(cmdbuf, "mtd erase %s 0x%llx 0x%llx", + devstr, part->addr, part->size); + if (run_command(cmdbuf, 0)) { + ret = -1; + stm32prog_err("%s (0x%x): MTD erase commands failed (%s)", + part->name, part->id, cmdbuf); + } + break; +#endif + case STM32PROG_RAM: + printf("on ram: "); + memset((void *)(uintptr_t)part->addr, 0, (size_t)part->size); + break; + default: + ret = -1; + stm32prog_err("%s (0x%x): erase invalid", part->name, part->id); + break; + } + if (!ret) + printf("done\n"); + + return ret; +} + +static void stm32prog_devices_init(struct stm32prog_data *data) +{ + int i; + int ret; + struct stm32prog_part_t *part; + + ret = treat_partition_list(data); + if (ret) + goto error; + + /* initialize the selected device */ + for (i = 0; i < data->dev_nb; i++) { + ret = init_device(data, &data->dev[i]); + if (ret) + goto error; + } + + /* delete RAW partition before create partition */ + for (i = 0; i < data->part_nb; i++) { + part = &data->part_array[i]; + + if (part->part_type != RAW_IMAGE) + continue; + + if (!IS_SELECT(part) || !IS_DELETE(part)) + continue; + + ret = part_delete(data, part); + if (ret) + goto error; + } + + ret = create_partitions(data); + if (ret) + goto error; + + /* delete partition GPT or MTD */ + for (i = 0; i < data->part_nb; i++) { + part = &data->part_array[i]; + + if (part->part_type == RAW_IMAGE) + continue; + + if (!IS_SELECT(part) || !IS_DELETE(part)) + continue; + + ret = part_delete(data, part); + if (ret) + goto error; + } + + return; + +error: + data->part_nb = 0; +} + +int stm32prog_dfu_init(struct stm32prog_data *data) +{ + /* init device if no error */ + if (data->part_nb) + stm32prog_devices_init(data); + + if (data->part_nb) + stm32prog_next_phase(data); + + /* prepare DFU for device read/write */ + dfu_free_entities(); + return dfu_init_entities(data); +} + +int stm32prog_init(struct stm32prog_data *data, ulong addr, ulong size) +{ + memset(data, 0x0, sizeof(*data)); + data->read_phase = PHASE_RESET; + data->phase = PHASE_FLASHLAYOUT; + + return parse_flash_layout(data, addr, size); +} + +void stm32prog_clean(struct stm32prog_data *data) +{ + /* clean */ + dfu_free_entities(); + free(data->part_array); + free(data->otp_part); + free(data->buffer); + free(data->header_data); +} + +/* DFU callback: used after serial and direct DFU USB access */ +void dfu_flush_callback(struct dfu_entity *dfu) +{ + if (!stm32prog_data) + return; + + if (dfu->dev_type == DFU_DEV_VIRT) { + if (dfu->data.virt.dev_num == PHASE_OTP) + stm32prog_otp_start(stm32prog_data); + else if (dfu->data.virt.dev_num == PHASE_PMIC) + stm32prog_pmic_start(stm32prog_data); + return; + } + + if (dfu->dev_type == DFU_DEV_RAM) { + if (dfu->alt == 0 && + stm32prog_data->phase == PHASE_FLASHLAYOUT) { + stm32prog_end_phase(stm32prog_data); + /* waiting DFU DETACH for reenumeration */ + } + } + + if (!stm32prog_data->cur_part) + return; + + if (dfu->alt == stm32prog_data->cur_part->alt_id) { + stm32prog_end_phase(stm32prog_data); + stm32prog_next_phase(stm32prog_data); + } +} + +void dfu_initiated_callback(struct dfu_entity *dfu) +{ + if (!stm32prog_data) + return; + + if (!stm32prog_data->cur_part) + return; + + /* force the saved offset for the current partition */ + if (dfu->alt == stm32prog_data->cur_part->alt_id) { + dfu->offset = stm32prog_data->offset; + stm32prog_data->dfu_seq = 0; + pr_debug("dfu offset = 0x%llx\n", dfu->offset); + } +} diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h new file mode 100644 index 0000000000..bae4e91c01 --- /dev/null +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/* + * Copyright (C) 2020, STMicroelectronics - All Rights Reserved + */ + +#ifndef _STM32PROG_H_ +#define _STM32PROG_H_ + +/* - phase defines ------------------------------------------------*/ +#define PHASE_FLASHLAYOUT 0x00 +#define PHASE_FIRST_USER 0x10 +#define PHASE_LAST_USER 0xF0 +#define PHASE_CMD 0xF1 +#define PHASE_OTP 0xF2 +#define PHASE_PMIC 0xF4 +#define PHASE_END 0xFE +#define PHASE_RESET 0xFF +#define PHASE_DO_RESET 0x1FF + +#define DEFAULT_ADDRESS 0xFFFFFFFF + +#define OTP_SIZE 1024 +#define PMIC_SIZE 8 + +enum stm32prog_target { + STM32PROG_NONE, + STM32PROG_MMC, + STM32PROG_NAND, + STM32PROG_NOR, + STM32PROG_SPI_NAND, + STM32PROG_RAM +}; + +enum stm32prog_link_t { + LINK_SERIAL, + LINK_USB, + LINK_UNDEFINED, +}; + +struct image_header_s { + bool present; + u32 image_checksum; + u32 image_length; +}; + +struct raw_header_s { + u32 magic_number; + u32 image_signature[64 / 4]; + u32 image_checksum; + u32 header_version; + u32 image_length; + u32 image_entry_point; + u32 reserved1; + u32 load_address; + u32 reserved2; + u32 version_number; + u32 option_flags; + u32 ecdsa_algorithm; + u32 ecdsa_public_key[64 / 4]; + u32 padding[83 / 4]; + u32 binary_type; +}; + +#define BL_HEADER_SIZE sizeof(struct raw_header_s) + +/* partition type in flashlayout file */ +enum stm32prog_part_type { + PART_BINARY, + PART_SYSTEM, + PART_FILESYSTEM, + RAW_IMAGE +}; + +/* device information */ +struct stm32prog_dev_t { + enum stm32prog_target target; + char dev_id; + u32 erase_size; + struct mmc *mmc; + struct mtd_info *mtd; + /* list of partition for this device / ordered in offset */ + struct list_head part_list; + bool full_update; +}; + +/* partition information build from FlashLayout and device */ +struct stm32prog_part_t { + /* FlashLayout information */ + int option; + int id; + enum stm32prog_part_type part_type; + enum stm32prog_target target; + char dev_id; + + /* partition name + * (16 char in gpt, + 1 for null terminated string + */ + char name[16 + 1]; + u64 addr; + u64 size; + enum stm32prog_part_type bin_nb; /* SSBL repeatition */ + + /* information on associated device */ + struct stm32prog_dev_t *dev; /* pointer to device */ + s16 part_id; /* partition id in device */ + int alt_id; /* alt id in usb/dfu */ + + struct list_head list; +}; + +#define STM32PROG_MAX_DEV 5 +struct stm32prog_data { + /* Layout information */ + int dev_nb; /* device number*/ + struct stm32prog_dev_t dev[STM32PROG_MAX_DEV]; /* array of device */ + int part_nb; /* nb of partition */ + struct stm32prog_part_t *part_array; /* array of partition */ + bool tee_detected; + bool fsbl_nor_detected; + + /* command internal information */ + unsigned int phase; + u32 offset; + char error[255]; + struct stm32prog_part_t *cur_part; + u32 *otp_part; + u8 pmic_part[PMIC_SIZE]; + + /* STM32 header information */ + struct raw_header_s *header_data; + struct image_header_s header; + + /* SERIAL information */ + u32 cursor; + u32 packet_number; + u32 checksum; + u8 *buffer; /* size = USART_RAM_BUFFER_SIZE*/ + int dfu_seq; + u8 read_phase; + + /* bootm information */ + u32 uimage; + u32 dtb; +}; + +extern struct stm32prog_data *stm32prog_data; + +/* OTP access */ +int stm32prog_otp_write(struct stm32prog_data *data, u32 offset, + u8 *buffer, long *size); +int stm32prog_otp_read(struct stm32prog_data *data, u32 offset, + u8 *buffer, long *size); +int stm32prog_otp_start(struct stm32prog_data *data); + +/* PMIC access */ +int stm32prog_pmic_write(struct stm32prog_data *data, u32 offset, + u8 *buffer, long *size); +int stm32prog_pmic_read(struct stm32prog_data *data, u32 offset, + u8 *buffer, long *size); +int stm32prog_pmic_start(struct stm32prog_data *data); + +/* generic part*/ +u8 stm32prog_header_check(struct raw_header_s *raw_header, + struct image_header_s *header); +int stm32prog_dfu_init(struct stm32prog_data *data); +void stm32prog_next_phase(struct stm32prog_data *data); +void stm32prog_do_reset(struct stm32prog_data *data); + +char *stm32prog_get_error(struct stm32prog_data *data); + +#define stm32prog_err(args...) {\ + if (data->phase != PHASE_RESET) { \ + sprintf(data->error, args); \ + data->phase = PHASE_RESET; \ + pr_err("Error: %s\n", data->error); } \ + } + +/* Main function */ +int stm32prog_init(struct stm32prog_data *data, ulong addr, ulong size); +int stm32prog_serial_init(struct stm32prog_data *data, int link_dev); +bool stm32prog_serial_loop(struct stm32prog_data *data); +bool stm32prog_usb_loop(struct stm32prog_data *data, int dev); +void stm32prog_clean(struct stm32prog_data *data); + +#endif diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c new file mode 100644 index 0000000000..70940f01f3 --- /dev/null +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_serial.c @@ -0,0 +1,994 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2020, STMicroelectronics - All Rights Reserved + */ + +#include <common.h> +#include <console.h> +#include <dfu.h> +#include <malloc.h> +#include <serial.h> +#include <watchdog.h> +#include <dm/lists.h> +#include <dm/device-internal.h> +#include <linux/delay.h> +#include "stm32prog.h" + +/* - configuration part -----------------------------*/ +#define USART_BL_VERSION 0x40 /* USART bootloader version V4.0*/ +#define UBOOT_BL_VERSION 0x03 /* bootloader version V0.3*/ +#define DEVICE_ID_BYTE1 0x05 /* MSB byte of device ID*/ +#define DEVICE_ID_BYTE2 0x00 /* LSB byte of device ID*/ +#define USART_RAM_BUFFER_SIZE 256 /* Size of USART_RAM_Buf buffer*/ + +/* - Commands -----------------------------*/ +#define GET_CMD_COMMAND 0x00 /* Get CMD command*/ +#define GET_VER_COMMAND 0x01 /* Get Version command*/ +#define GET_ID_COMMAND 0x02 /* Get ID command*/ +#define GET_PHASE_COMMAND 0x03 /* Get Phase command*/ +#define RM_COMMAND 0x11 /* Read Memory command*/ +#define READ_PART_COMMAND 0x12 /* Read Partition command*/ +#define START_COMMAND 0x21 /* START command (Go)*/ +#define DOWNLOAD_COMMAND 0x31 /* Download command*/ +/* existing command for other STM32 but not used */ +/* ERASE 0x43 */ +/* EXTENDED_ERASE 0x44 */ +/* WRITE_UNPROTECTED 0x73 */ +/* READOUT_PROTECT 0x82 */ +/* READOUT_UNPROTECT 0x92 */ + +/* - miscellaneous defines ----------------------------------------*/ +#define INIT_BYTE 0x7F /*Init Byte ID*/ +#define ACK_BYTE 0x79 /*Acknowlede Byte ID*/ +#define NACK_BYTE 0x1F /*No Acknowlede Byte ID*/ +#define ABORT_BYTE 0x5F /*ABORT*/ + +struct udevice *down_serial_dev; + +const u8 cmd_id[] = { + GET_CMD_COMMAND, + GET_VER_COMMAND, + GET_ID_COMMAND, + GET_PHASE_COMMAND, + RM_COMMAND, + READ_PART_COMMAND, + START_COMMAND, + DOWNLOAD_COMMAND +}; + +#define NB_CMD sizeof(cmd_id) + +/* DFU support for serial *********************************************/ +static struct dfu_entity *stm32prog_get_entity(struct stm32prog_data *data) +{ + int alt_id; + + if (!data->cur_part) + if (data->phase == PHASE_FLASHLAYOUT) + alt_id = 0; + else + return NULL; + else + alt_id = data->cur_part->alt_id; + + return dfu_get_entity(alt_id); +} + +static int stm32prog_write(struct stm32prog_data *data, u8 *buffer, + u32 buffer_size) +{ + struct dfu_entity *dfu_entity; + u8 ret = 0; + + dfu_entity = stm32prog_get_entity(data); + if (!dfu_entity) + return -ENODEV; + + ret = dfu_write(dfu_entity, + buffer, + buffer_size, + data->dfu_seq); + + if (ret) { + stm32prog_err("DFU write failed [%d] cnt: %d", + ret, data->dfu_seq); + } + data->dfu_seq++; + /* handle rollover as in driver/dfu/dfu.c */ + data->dfu_seq &= 0xffff; + if (buffer_size == 0) + data->dfu_seq = 0; /* flush done */ + + return ret; +} + +static int stm32prog_read(struct stm32prog_data *data, u8 phase, u32 offset, + u8 *buffer, u32 buffer_size) +{ + struct dfu_entity *dfu_entity; + struct stm32prog_part_t *part; + u32 size; + int ret, i; + + if (data->dfu_seq) { + stm32prog_err("DFU write pending for phase %d, seq %d", + data->phase, data->dfu_seq); + return -EINVAL; + } + if (phase == PHASE_FLASHLAYOUT || phase > PHASE_LAST_USER) { + stm32prog_err("read failed : phase %d is invalid", phase); + return -EINVAL; + } + if (data->read_phase <= PHASE_LAST_USER && + phase != data->read_phase) { + /* clear previous read session */ + dfu_entity = dfu_get_entity(data->read_phase - 1); + if (dfu_entity) + dfu_transaction_cleanup(dfu_entity); + } + + dfu_entity = NULL; + /* found partition for the expected phase */ + for (i = 0; i < data->part_nb; i++) { + part = &data->part_array[i]; + if (part->id == phase) + dfu_entity = dfu_get_entity(part->alt_id); + } + if (!dfu_entity) { + stm32prog_err("read failed : phase %d is unknown", phase); + return -ENODEV; + } + + /* clear pending read before to force offset */ + if (dfu_entity->inited && + (data->read_phase != phase || data->offset != offset)) + dfu_transaction_cleanup(dfu_entity); + + /* initiate before to force offset */ + if (!dfu_entity->inited) { + ret = dfu_transaction_initiate(dfu_entity, true); + if (ret < 0) { + stm32prog_err("DFU read init failed [%d] phase = %d offset = 0x%08x", + ret, phase, offset); + return ret; + } + } + /* force new offset */ + if (dfu_entity->offset != offset) + dfu_entity->offset = offset; + data->offset = offset; + data->read_phase = phase; + pr_debug("\nSTM32 download read %s offset=0x%x\n", + dfu_entity->name, offset); + ret = dfu_read(dfu_entity, buffer, buffer_size, + dfu_entity->i_blk_seq_num); + if (ret < 0) { + stm32prog_err("DFU read failed [%d] phase = %d offset = 0x%08x", + ret, phase, offset); + return ret; + } + + size = ret; + + if (size < buffer_size) { + data->offset = 0; + data->read_phase = PHASE_END; + memset(buffer + size, 0, buffer_size - size); + } else { + data->offset += size; + } + + return ret; +} + +/* UART access ***************************************************/ +int stm32prog_serial_init(struct stm32prog_data *data, int link_dev) +{ + struct udevice *dev = NULL; + int node; + char alias[10]; + const char *path; + struct dm_serial_ops *ops; + /* no parity, 8 bits, 1 stop */ + u32 serial_config = SERIAL_DEFAULT_CONFIG; + + down_serial_dev = NULL; + + sprintf(alias, "serial%d", link_dev); + path = fdt_get_alias(gd->fdt_blob, alias); + if (!path) { + pr_err("%s alias not found", alias); + return -ENODEV; + } + node = fdt_path_offset(gd->fdt_blob, path); + if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node, + &dev)) { + down_serial_dev = dev; + } else if (node > 0 && + !lists_bind_fdt(gd->dm_root, offset_to_ofnode(node), + &dev, false)) { + if (!device_probe(dev)) + down_serial_dev = dev; + } + if (!down_serial_dev) { + pr_err("%s = %s device not found", alias, path); + return -ENODEV; + } + + /* force silent console on uart only when used */ + if (gd->cur_serial_dev == down_serial_dev) + gd->flags |= GD_FLG_DISABLE_CONSOLE | GD_FLG_SILENT; + else + gd->flags &= ~(GD_FLG_DISABLE_CONSOLE | GD_FLG_SILENT); + + ops = serial_get_ops(down_serial_dev); + + if (!ops) { + pr_err("%s = %s missing ops", alias, path); + return -ENODEV; + } + if (!ops->setconfig) { + pr_err("%s = %s missing setconfig", alias, path); + return -ENODEV; + } + + clrsetbits_le32(&serial_config, SERIAL_PAR_MASK, SERIAL_PAR_EVEN); + + data->buffer = memalign(CONFIG_SYS_CACHELINE_SIZE, + USART_RAM_BUFFER_SIZE); + + return ops->setconfig(down_serial_dev, serial_config); +} + +static void stm32prog_serial_flush(void) +{ + struct dm_serial_ops *ops = serial_get_ops(down_serial_dev); + int err; + + do { + err = ops->getc(down_serial_dev); + } while (err != -EAGAIN); +} + +static int stm32prog_serial_getc_err(void) +{ + struct dm_serial_ops *ops = serial_get_ops(down_serial_dev); + int err; + + do { + err = ops->getc(down_serial_dev); + if (err == -EAGAIN) { + ctrlc(); + WATCHDOG_RESET(); + } + } while ((err == -EAGAIN) && (!had_ctrlc())); + + return err; +} + +static u8 stm32prog_serial_getc(void) +{ + int err; + + err = stm32prog_serial_getc_err(); + + return err >= 0 ? err : 0; +} + +static bool stm32prog_serial_get_buffer(u8 *buffer, u32 *count) +{ + struct dm_serial_ops *ops = serial_get_ops(down_serial_dev); + int err; + + do { + err = ops->getc(down_serial_dev); + if (err >= 0) { + *buffer++ = err; + *count -= 1; + } else if (err == -EAGAIN) { + ctrlc(); + WATCHDOG_RESET(); + } else { + break; + } + } while (*count && !had_ctrlc()); + + return !!(err < 0); +} + +static void stm32prog_serial_putc(u8 w_byte) +{ + struct dm_serial_ops *ops = serial_get_ops(down_serial_dev); + int err; + + do { + err = ops->putc(down_serial_dev, w_byte); + } while (err == -EAGAIN); +} + +/* Helper function ************************************************/ + +static u8 stm32prog_header(struct stm32prog_data *data) +{ + u8 ret; + u8 boot = 0; + struct dfu_entity *dfu_entity; + u64 size = 0; + + dfu_entity = stm32prog_get_entity(data); + if (!dfu_entity) + return -ENODEV; + + printf("\nSTM32 download write %s\n", dfu_entity->name); + + /* force cleanup to avoid issue with previous read */ + dfu_transaction_cleanup(dfu_entity); + + ret = stm32prog_header_check(data->header_data, + &data->header); + + /* no header : max size is partition size */ + if (ret) { + dfu_entity->get_medium_size(dfu_entity, &size); + data->header.image_length = size; + } + + /**** Flash the header if necessary for boot partition */ + if (data->phase < PHASE_FIRST_USER) + boot = 1; + + /* write header if boot partition */ + if (boot) { + if (ret) { + stm32prog_err("invalid header (error %d)", ret); + } else { + ret = stm32prog_write(data, + (u8 *)data->header_data, + BL_HEADER_SIZE); + } + } else { + if (ret) + printf(" partition without checksum\n"); + ret = 0; + } + + free(data->header_data); + data->header_data = NULL; + + return ret; +} + +static u8 stm32prog_start(struct stm32prog_data *data, u32 address) +{ + u8 ret = 0; + struct dfu_entity *dfu_entity; + + if (address < 0x100) { + if (address == PHASE_OTP) + return stm32prog_otp_start(data); + + if (address == PHASE_PMIC) + return stm32prog_pmic_start(data); + + if (address == PHASE_RESET || address == PHASE_END) { + data->cur_part = NULL; + data->dfu_seq = 0; + data->phase = address; + return 0; + } + if (address != data->phase) { + stm32prog_err("invalid received phase id %d, current phase is %d", + (u8)address, (u8)data->phase); + return -EINVAL; + } + } + /* check the last loaded partition */ + if (address == DEFAULT_ADDRESS || address == data->phase) { + switch (data->phase) { + case PHASE_END: + case PHASE_RESET: + case PHASE_DO_RESET: + data->cur_part = NULL; + data->phase = PHASE_DO_RESET; + return 0; + } + dfu_entity = stm32prog_get_entity(data); + if (!dfu_entity) + return -ENODEV; + + if (data->dfu_seq) { + ret = dfu_flush(dfu_entity, NULL, 0, data->dfu_seq); + data->dfu_seq = 0; + if (ret) { + stm32prog_err("DFU flush failed [%d]", ret); + return ret; + } + } + printf("\n received length = 0x%x\n", data->cursor); + if (data->header.present) { + if (data->cursor != + (data->header.image_length + BL_HEADER_SIZE)) { + stm32prog_err("transmission interrupted (length=0x%x expected=0x%x)", + data->cursor, + data->header.image_length + + BL_HEADER_SIZE); + return -EIO; + } + if (data->header.image_checksum != data->checksum) { + stm32prog_err("invalid checksum received (0x%x expected 0x%x)", + data->checksum, + data->header.image_checksum); + return -EIO; + } + printf("\n checksum OK (0x%x)\n", data->checksum); + } + + /* update DFU with received flashlayout */ + if (data->phase == PHASE_FLASHLAYOUT) + stm32prog_dfu_init(data); + } else { + void (*entry)(void) = (void *)address; + + printf("## Starting application at 0x%x ...\n", address); + (*entry)(); + printf("## Application terminated\n"); + ret = -ENOEXEC; + } + + return ret; +} + +/** + * get_address() - Get address if it is valid + * + * @tmp_xor: Current xor value to update + * @return The address area + */ +static u32 get_address(u8 *tmp_xor) +{ + u32 address = 0x0; + u8 data; + + data = stm32prog_serial_getc(); + *tmp_xor ^= data; + address |= ((u32)data) << 24; + + data = stm32prog_serial_getc(); + address |= ((u32)data) << 16; + *tmp_xor ^= data; + + data = stm32prog_serial_getc(); + address |= ((u32)data) << 8; + *tmp_xor ^= data; + + data = stm32prog_serial_getc(); + address |= ((u32)data); + *tmp_xor ^= data; + + return address; +} + +static void stm32prog_serial_result(u8 result) +{ + /* always flush fifo before to send result */ + stm32prog_serial_flush(); + stm32prog_serial_putc(result); +} + +/* Command -----------------------------------------------*/ +/** + * get_cmd_command() - Respond to Get command + * + * @data: Current command context + */ +static void get_cmd_command(struct stm32prog_data *data) +{ + u32 counter = 0x0; + + stm32prog_serial_putc(NB_CMD); + stm32prog_serial_putc(USART_BL_VERSION); + + for (counter = 0; counter < NB_CMD; counter++) + stm32prog_serial_putc(cmd_id[counter]); + + stm32prog_serial_result(ACK_BYTE); +} + +/** + * get_version_command() - Respond to Get Version command + * + * @data: Current command context + */ +static void get_version_command(struct stm32prog_data *data) +{ + stm32prog_serial_putc(UBOOT_BL_VERSION); + stm32prog_serial_result(ACK_BYTE); +} + +/** + * get_id_command() - Respond to Get ID command + * + * @data: Current command context + */ +static void get_id_command(struct stm32prog_data *data) +{ + /* Send Device IDCode */ + stm32prog_serial_putc(0x1); + stm32prog_serial_putc(DEVICE_ID_BYTE1); + stm32prog_serial_putc(DEVICE_ID_BYTE2); + stm32prog_serial_result(ACK_BYTE); +} + +/** + * get_phase_command() - Respond to Get phase + * + * @data: Current command context + */ +static void get_phase_command(struct stm32prog_data *data) +{ + char *err_msg = NULL; + u8 i, length = 0; + u32 destination = DEFAULT_ADDRESS; /* destination address */ + int phase = data->phase; + + if (phase == PHASE_RESET || phase == PHASE_DO_RESET) { + err_msg = stm32prog_get_error(data); + length = strlen(err_msg); + } + if (phase == PHASE_FLASHLAYOUT) + destination = STM32_DDR_BASE; + + stm32prog_serial_putc(length + 5); /* Total length */ + stm32prog_serial_putc(phase & 0xFF); /* partition ID */ + stm32prog_serial_putc(destination); /* byte 1 of address */ + stm32prog_serial_putc(destination >> 8); /* byte 2 of address */ + stm32prog_serial_putc(destination >> 16); /* byte 3 of address */ + stm32prog_serial_putc(destination >> 24); /* byte 4 of address */ + + stm32prog_serial_putc(length); /* Information length */ + for (i = 0; i < length; i++) + stm32prog_serial_putc(err_msg[i]); + stm32prog_serial_result(ACK_BYTE); + + if (phase == PHASE_RESET) + stm32prog_do_reset(data); +} + +/** + * read_memory_command() - Read data from memory + * + * @data: Current command context + */ +static void read_memory_command(struct stm32prog_data *data) +{ + u32 address = 0x0; + u8 rcv_data = 0x0, tmp_xor = 0x0; + u32 counter = 0x0; + + /* Read memory address */ + address = get_address(&tmp_xor); + + /* If address memory is not received correctly */ + rcv_data = stm32prog_serial_getc(); + if (rcv_data != tmp_xor) { + stm32prog_serial_result(NACK_BYTE); + return; + } + + stm32prog_serial_result(ACK_BYTE); + + /* Read the number of bytes to be received: + * Max NbrOfData = Data + 1 = 256 + */ + rcv_data = stm32prog_serial_getc(); + tmp_xor = ~rcv_data; + if (stm32prog_serial_getc() != tmp_xor) { + stm32prog_serial_result(NACK_BYTE); + return; + } + + /* If checksum is correct send ACK */ + stm32prog_serial_result(ACK_BYTE); + + /* Send data to the host: + * Number of data to read = data + 1 + */ + for (counter = (rcv_data + 1); counter != 0; counter--) + stm32prog_serial_putc(*(u8 *)(address++)); +} + +/** + * start_command() - Respond to start command + * + * Jump to user application in RAM or partition check + * + * @data: Current command context + */ +static void start_command(struct stm32prog_data *data) +{ + u32 address = 0; + u8 tmp_xor = 0x0; + u8 ret, rcv_data; + + /* Read memory address */ + address = get_address(&tmp_xor); + + /* If address memory is not received correctly */ + rcv_data = stm32prog_serial_getc(); + if (rcv_data != tmp_xor) { + stm32prog_serial_result(NACK_BYTE); + return; + } + /* validate partition */ + ret = stm32prog_start(data, + address); + + if (ret) + stm32prog_serial_result(ABORT_BYTE); + else + stm32prog_serial_result(ACK_BYTE); +} + +/** + * download_command() - Respond to download command + * + * Write data to not volatile memory, Flash + * + * @data: Current command context + */ +static void download_command(struct stm32prog_data *data) +{ + u32 address = 0x0; + u8 my_xor = 0x0; + u8 rcv_xor; + u32 counter = 0x0, codesize = 0x0; + u8 *ramaddress = 0; + u8 rcv_data = 0x0; + struct image_header_s *image_header = &data->header; + u32 cursor = data->cursor; + long size = 0; + u8 operation; + u32 packet_number; + u32 result = ACK_BYTE; + u8 ret; + unsigned int i; + bool error; + int rcv; + + address = get_address(&my_xor); + + /* If address memory is not received correctly */ + rcv_xor = stm32prog_serial_getc(); + if (rcv_xor != my_xor) { + result = NACK_BYTE; + goto end; + } + + /* If address valid send ACK */ + stm32prog_serial_result(ACK_BYTE); + + /* get packet number and operation type */ + operation = (u8)((u32)address >> 24); + packet_number = ((u32)(((u32)address << 8))) >> 8; + + switch (operation) { + /* supported operation */ + case PHASE_FLASHLAYOUT: + case PHASE_OTP: + case PHASE_PMIC: + break; + default: + result = NACK_BYTE; + goto end; + } + /* check the packet number */ + if (packet_number == 0) { + /* erase: re-initialize the image_header struct */ + data->packet_number = 0; + if (data->header_data) + memset(data->header_data, 0, BL_HEADER_SIZE); + else + data->header_data = calloc(1, BL_HEADER_SIZE); + cursor = 0; + data->cursor = 0; + data->checksum = 0; + /*idx = cursor;*/ + } else { + data->packet_number++; + } + + /* Check with the number of current packet if the device receive + * the true packet + */ + if (packet_number != data->packet_number) { + data->packet_number--; + result = NACK_BYTE; + goto end; + } + + /*-- Read number of bytes to be written and data -----------*/ + + /* Read the number of bytes to be written: + * Max NbrOfData = data + 1 <= 256 + */ + rcv_data = stm32prog_serial_getc(); + + /* NbrOfData to write = data + 1 */ + codesize = rcv_data + 0x01; + + if (codesize > USART_RAM_BUFFER_SIZE) { + result = NACK_BYTE; + goto end; + } + + /* Checksum Initialization */ + my_xor = rcv_data; + + /* UART receive data and send to Buffer */ + counter = codesize; + error = stm32prog_serial_get_buffer(data->buffer, &counter); + + /* read checksum */ + if (!error) { + rcv = stm32prog_serial_getc_err(); + error = !!(rcv < 0); + rcv_xor = rcv; + } + + if (error) { + printf("transmission error on packet %d, byte %d\n", + packet_number, codesize - counter); + /* waiting end of packet before flush & NACK */ + mdelay(30); + data->packet_number--; + result = NACK_BYTE; + goto end; + } + + /* Compute Checksum */ + ramaddress = data->buffer; + for (counter = codesize; counter != 0; counter--) + my_xor ^= *(ramaddress++); + + /* If Checksum is incorrect */ + if (rcv_xor != my_xor) { + printf("checksum error on packet %d\n", + packet_number); + /* wait to be sure that all data are received + * in the FIFO before flush + */ + mdelay(30); + data->packet_number--; + result = NACK_BYTE; + goto end; + } + + /* Update current position in buffer */ + data->cursor += codesize; + + if (operation == PHASE_OTP) { + size = data->cursor - cursor; + /* no header for OTP */ + if (stm32prog_otp_write(data, cursor, + data->buffer, &size)) + result = ABORT_BYTE; + goto end; + } + + if (operation == PHASE_PMIC) { + size = data->cursor - cursor; + /* no header for PMIC */ + if (stm32prog_pmic_write(data, cursor, + data->buffer, &size)) + result = ABORT_BYTE; + goto end; + } + + if (cursor < BL_HEADER_SIZE) { + /* size = portion of header in this chunck */ + if (data->cursor >= BL_HEADER_SIZE) + size = BL_HEADER_SIZE - cursor; + else + size = data->cursor - cursor; + memcpy((void *)((u32)(data->header_data) + cursor), + data->buffer, size); + cursor += size; + + if (cursor == BL_HEADER_SIZE) { + /* Check and Write the header */ + if (stm32prog_header(data)) { + result = ABORT_BYTE; + goto end; + } + } else { + goto end; + } + } + + if (image_header->present) { + if (data->cursor <= BL_HEADER_SIZE) + goto end; + /* compute checksum on payload */ + for (i = (unsigned long)size; i < codesize; i++) + data->checksum += data->buffer[i]; + + if (data->cursor > + image_header->image_length + BL_HEADER_SIZE) { + pr_err("expected size exceeded\n"); + result = ABORT_BYTE; + goto end; + } + + /* write data (payload) */ + ret = stm32prog_write(data, + &data->buffer[size], + codesize - size); + } else { + /* write all */ + ret = stm32prog_write(data, + data->buffer, + codesize); + } + if (ret) + result = ABORT_BYTE; + +end: + stm32prog_serial_result(result); +} + +/** + * read_partition() - Respond to read command + * + * Read data from not volatile memory, Flash + * + * @data: Current command context + */ +static void read_partition_command(struct stm32prog_data *data) +{ + u32 i, part_id, codesize, offset = 0, rcv_data; + long size; + u8 tmp_xor; + int res; + u8 buffer[256]; + + part_id = stm32prog_serial_getc(); + tmp_xor = part_id; + + offset = get_address(&tmp_xor); + + rcv_data = stm32prog_serial_getc(); + if (rcv_data != tmp_xor) { + pr_debug("1st checksum received = %x, computed %x\n", + rcv_data, tmp_xor); + goto error; + } + stm32prog_serial_putc(ACK_BYTE); + + /* NbrOfData to read = data + 1 */ + rcv_data = stm32prog_serial_getc(); + codesize = rcv_data + 0x01; + tmp_xor = rcv_data; + + rcv_data = stm32prog_serial_getc(); + if ((rcv_data ^ tmp_xor) != 0xFF) { + pr_debug("2nd checksum received = %x, computed %x\n", + rcv_data, tmp_xor); + goto error; + } + + pr_debug("%s : %x\n", __func__, part_id); + rcv_data = 0; + switch (part_id) { + case PHASE_OTP: + size = codesize; + if (!stm32prog_otp_read(data, offset, buffer, &size)) + rcv_data = size; + break; + case PHASE_PMIC: + size = codesize; + if (!stm32prog_pmic_read(data, offset, buffer, &size)) + rcv_data = size; + break; + default: + res = stm32prog_read(data, part_id, offset, + buffer, codesize); + if (res > 0) + rcv_data = res; + break; + } + if (rcv_data > 0) { + stm32prog_serial_putc(ACK_BYTE); + /*----------- Send data to the host -----------*/ + for (i = 0; i < rcv_data; i++) + stm32prog_serial_putc(buffer[i]); + /*----------- Send filler to the host -----------*/ + for (; i < codesize; i++) + stm32prog_serial_putc(0x0); + return; + } + stm32prog_serial_result(ABORT_BYTE); + return; + +error: + stm32prog_serial_result(NACK_BYTE); +} + +/* MAIN function = SERIAL LOOP ***********************************************/ + +/** + * stm32prog_serial_loop() - USART bootloader Loop routine + * + * @data: Current command context + * @return true if reset is needed after loop + */ +bool stm32prog_serial_loop(struct stm32prog_data *data) +{ + u32 counter = 0x0; + u8 command = 0x0; + u8 found; + int phase = data->phase; + + /* element of cmd_func need to aligned with cmd_id[]*/ + void (*cmd_func[NB_CMD])(struct stm32prog_data *) = { + /* GET_CMD_COMMAND */ get_cmd_command, + /* GET_VER_COMMAND */ get_version_command, + /* GET_ID_COMMAND */ get_id_command, + /* GET_PHASE_COMMAND */ get_phase_command, + /* RM_COMMAND */ read_memory_command, + /* READ_PART_COMMAND */ read_partition_command, + /* START_COMMAND */ start_command, + /* DOWNLOAD_COMMAND */ download_command + }; + + /* flush and NACK pending command received during u-boot init + * request command reemit + */ + stm32prog_serial_result(NACK_BYTE); + + clear_ctrlc(); /* forget any previous Control C */ + while (!had_ctrlc()) { + phase = data->phase; + + if (phase == PHASE_DO_RESET) + return true; + + /* Get the user command: read first byte */ + command = stm32prog_serial_getc(); + + if (command == INIT_BYTE) { + puts("\nConnected\n"); + stm32prog_serial_result(ACK_BYTE); + continue; + } + + found = 0; + for (counter = 0; counter < NB_CMD; counter++) + if (cmd_id[counter] == command) { + found = 1; + break; + } + if (found) + if ((command ^ stm32prog_serial_getc()) != 0xFF) + found = 0; + if (!found) { + /* wait to be sure that all data are received + * in the FIFO before flush (CMD and XOR) + */ + mdelay(3); + stm32prog_serial_result(NACK_BYTE); + } else { + stm32prog_serial_result(ACK_BYTE); + cmd_func[counter](data); + } + WATCHDOG_RESET(); + } + + /* clean device */ + if (gd->cur_serial_dev == down_serial_dev) { + /* restore console on uart */ + gd->flags &= ~(GD_FLG_DISABLE_CONSOLE | GD_FLG_SILENT); + } + down_serial_dev = NULL; + + return false; /* no reset after ctrlc */ +} diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c new file mode 100644 index 0000000000..969245e199 --- /dev/null +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2020, STMicroelectronics - All Rights Reserved + */ + +#include <common.h> +#include <dfu.h> +#include <g_dnl.h> +#include <usb.h> +#include <asm/arch/stm32prog.h> +#include <asm/arch/sys_proto.h> +#include "stm32prog.h" + +static int stm32prog_set_phase(struct stm32prog_data *data, u8 phase, + u32 offset) +{ + struct stm32prog_part_t *part; + int i; + + if (phase == data->phase) { + data->offset = offset; + data->dfu_seq = 0; + return 0; + } + + /* found partition for phase */ + for (i = 0; i < data->part_nb; i++) { + part = &data->part_array[i]; + if (part->id == phase) { + data->cur_part = part; + data->phase = phase; + data->offset = offset; + data->dfu_seq = 0; + return 0; + } + } + + return -EINVAL; +} + +static int stm32prog_cmd_write(u64 offset, void *buf, long *len) +{ + u8 phase; + u32 address; + u8 *pt = buf; + void (*entry)(void); + int ret; + + if (*len < 5) { + pr_err("size not allowed\n"); + return -EINVAL; + } + if (offset) { + pr_err("invalid offset\n"); + return -EINVAL; + } + phase = pt[0]; + address = (pt[1] << 24) | (pt[2] << 16) | (pt[3] << 8) | pt[4]; + if (phase == PHASE_RESET) { + entry = (void *)address; + printf("## Starting application at 0x%x ...\n", address); + (*entry)(); + printf("## Application terminated\n"); + return 0; + } + /* set phase and offset */ + ret = stm32prog_set_phase(stm32prog_data, phase, address); + if (ret) + pr_err("failed: %d\n", ret); + return ret; +} + +#define PHASE_MIN_SIZE 9 +static int stm32prog_cmd_read(u64 offset, void *buf, long *len) +{ + u32 destination = DEFAULT_ADDRESS; /* destination address */ + u32 dfu_offset; + u8 *pt_buf = buf; + int phase; + char *err_msg; + int length; + + if (*len < PHASE_MIN_SIZE) { + pr_err("request exceeds allowed area\n"); + return -EINVAL; + } + if (offset) { + *len = 0; /* EOF for second request */ + return 0; + } + phase = stm32prog_data->phase; + if (phase == PHASE_FLASHLAYOUT) + destination = STM32_DDR_BASE; + dfu_offset = stm32prog_data->offset; + + /* mandatory header, size = PHASE_MIN_SIZE */ + *pt_buf++ = (u8)(phase & 0xFF); + *pt_buf++ = (u8)(destination); + *pt_buf++ = (u8)(destination >> 8); + *pt_buf++ = (u8)(destination >> 16); + *pt_buf++ = (u8)(destination >> 24); + *pt_buf++ = (u8)(dfu_offset); + *pt_buf++ = (u8)(dfu_offset >> 8); + *pt_buf++ = (u8)(dfu_offset >> 16); + *pt_buf++ = (u8)(dfu_offset >> 24); + + if (phase == PHASE_RESET || phase == PHASE_DO_RESET) { + err_msg = stm32prog_get_error(stm32prog_data); + length = strlen(err_msg); + if (length + PHASE_MIN_SIZE > *len) + length = *len - PHASE_MIN_SIZE; + + memcpy(pt_buf, err_msg, length); + *len = PHASE_MIN_SIZE + length; + stm32prog_do_reset(stm32prog_data); + } else if (phase == PHASE_FLASHLAYOUT) { + *pt_buf++ = stm32prog_data->part_nb ? 1 : 0; + *len = PHASE_MIN_SIZE + 1; + } else { + *len = PHASE_MIN_SIZE; + } + + return 0; +} + +int stm32prog_write_medium_virt(struct dfu_entity *dfu, u64 offset, + void *buf, long *len) +{ + if (dfu->dev_type != DFU_DEV_VIRT) + return -EINVAL; + + switch (dfu->data.virt.dev_num) { + case PHASE_CMD: + return stm32prog_cmd_write(offset, buf, len); + + case PHASE_OTP: + return stm32prog_otp_write(stm32prog_data, (u32)offset, + buf, len); + + case PHASE_PMIC: + return stm32prog_pmic_write(stm32prog_data, (u32)offset, + buf, len); + } + *len = 0; + return 0; +} + +int stm32prog_read_medium_virt(struct dfu_entity *dfu, u64 offset, + void *buf, long *len) +{ + if (dfu->dev_type != DFU_DEV_VIRT) + return -EINVAL; + + switch (dfu->data.virt.dev_num) { + case PHASE_CMD: + return stm32prog_cmd_read(offset, buf, len); + + case PHASE_OTP: + return stm32prog_otp_read(stm32prog_data, (u32)offset, + buf, len); + + case PHASE_PMIC: + return stm32prog_pmic_read(stm32prog_data, (u32)offset, + buf, len); + } + *len = 0; + return 0; +} + +int stm32prog_get_medium_size_virt(struct dfu_entity *dfu, u64 *size) +{ + if (dfu->dev_type != DFU_DEV_VIRT) { + *size = 0; + pr_debug("%s, invalid dev_type = %d\n", + __func__, dfu->dev_type); + return -EINVAL; + } + + switch (dfu->data.virt.dev_num) { + case PHASE_CMD: + *size = 512; + break; + case PHASE_OTP: + *size = OTP_SIZE; + break; + case PHASE_PMIC: + *size = PMIC_SIZE; + break; + } + + return 0; +} + +bool stm32prog_usb_loop(struct stm32prog_data *data, int dev) +{ + int ret; + bool result; + /* USB download gadget for STM32 Programmer */ + char product[128]; + + snprintf(product, sizeof(product), + "USB download gadget@Device ID /0x%03X, @Revision ID /0x%04X", + get_cpu_dev(), get_cpu_rev()); + g_dnl_set_product(product); + + if (stm32prog_data->phase == PHASE_FLASHLAYOUT) { + ret = run_usb_dnl_gadget(dev, "usb_dnl_dfu"); + if (ret || stm32prog_data->phase == PHASE_DO_RESET) + return ret; + /* prepare the second enumeration with the FlashLayout */ + if (stm32prog_data->phase == PHASE_FLASHLAYOUT) + stm32prog_dfu_init(data); + /* found next selected partition */ + stm32prog_next_phase(data); + } + + ret = run_usb_dnl_gadget(dev, "usb_dnl_dfu"); + + result = !!(ret) || (stm32prog_data->phase == PHASE_DO_RESET); + + g_dnl_set_product(NULL); + + return result; +} + +int g_dnl_get_board_bcd_device_number(int gcnum) +{ + pr_debug("%s\n", __func__); + return 0x200; +} diff --git a/arch/arm/mach-stm32mp/cpu.c b/arch/arm/mach-stm32mp/cpu.c index 74d03fa7dd..472b140321 100644 --- a/arch/arm/mach-stm32mp/cpu.c +++ b/arch/arm/mach-stm32mp/cpu.c @@ -7,12 +7,16 @@ #include <cpu_func.h> #include <debug_uart.h> #include <env.h> +#include <init.h> +#include <log.h> #include <misc.h> +#include <net.h> #include <asm/io.h> #include <asm/arch/stm32.h> #include <asm/arch/sys_proto.h> #include <dm/device.h> #include <dm/uclass.h> +#include <linux/bitops.h> /* RCC register */ #define RCC_TZCR (STM32_RCC_BASE + 0x00) @@ -75,6 +79,12 @@ #define PKG_SHIFT 27 #define PKG_MASK GENMASK(2, 0) +/* + * early TLB into the .data section so that it not get cleared + * with 16kB allignment (see TTBR0_BASE_ADDR_MASK) + */ +u8 early_tlb[PGTABLE_SIZE] __section(".data") __aligned(0x4000); + #if !defined(CONFIG_SPL) || defined(CONFIG_SPL_BUILD) #ifndef CONFIG_TFABOOT static void security_init(void) @@ -187,12 +197,40 @@ u32 get_bootmode(void) } /* + * initialize the MMU and activate cache in SPL or in U-Boot pre-reloc stage + * MMU/TLB is updated in enable_caches() for U-Boot after relocation + * or is deactivated in U-Boot entry function start.S::cpu_init_cp15 + */ +static void early_enable_caches(void) +{ + /* I-cache is already enabled in start.S: cpu_init_cp15 */ + + if (CONFIG_IS_ENABLED(SYS_DCACHE_OFF)) + return; + + gd->arch.tlb_size = PGTABLE_SIZE; + gd->arch.tlb_addr = (unsigned long)&early_tlb; + + dcache_enable(); + + if (IS_ENABLED(CONFIG_SPL_BUILD)) + mmu_set_region_dcache_behaviour(STM32_SYSRAM_BASE, + STM32_SYSRAM_SIZE, + DCACHE_DEFAULT_OPTION); + else + mmu_set_region_dcache_behaviour(STM32_DDR_BASE, STM32_DDR_SIZE, + DCACHE_DEFAULT_OPTION); +} + +/* * Early system init */ int arch_cpu_init(void) { u32 boot_mode; + early_enable_caches(); + /* early armv7 timer init: needed for polling */ timer_init(); @@ -225,7 +263,14 @@ int arch_cpu_init(void) void enable_caches(void) { - /* Enable D-cache. I-cache is already enabled in start.S */ + /* I-cache is already enabled in start.S: icache_enable() not needed */ + + /* deactivate the data cache, early enabled in arch_cpu_init() */ + dcache_disable(); + /* + * update MMU after relocation and enable the data cache + * warning: the TLB location udpated in board_f.c::reserve_mmu + */ dcache_enable(); } @@ -236,6 +281,11 @@ static u32 read_idc(void) return readl(DBGMCU_IDC); } +u32 get_cpu_dev(void) +{ + return (read_idc() & DBGMCU_IDC_DEV_ID_MASK) >> DBGMCU_IDC_DEV_ID_SHIFT; +} + u32 get_cpu_rev(void) { return (read_idc() & DBGMCU_IDC_REV_ID_MASK) >> DBGMCU_IDC_REV_ID_SHIFT; @@ -266,11 +316,7 @@ static u32 get_cpu_rpn(void) u32 get_cpu_type(void) { - u32 id; - - id = (read_idc() & DBGMCU_IDC_DEV_ID_MASK) >> DBGMCU_IDC_DEV_ID_SHIFT; - - return (id << 16) | get_cpu_rpn(); + return (get_cpu_dev() << 16) | get_cpu_rpn(); } /* Get Package options from OTP */ @@ -435,6 +481,10 @@ static void setup_boot_mode(void) env_set("boot_device", "nand"); env_set("boot_instance", "0"); break; + case BOOT_FLASH_SPINAND: + env_set("boot_device", "spi-nand"); + env_set("boot_instance", "0"); + break; case BOOT_FLASH_NOR: env_set("boot_device", "nor"); env_set("boot_instance", "0"); diff --git a/arch/arm/mach-stm32mp/dram_init.c b/arch/arm/mach-stm32mp/dram_init.c index 7688b3e315..0e8ce63f4a 100644 --- a/arch/arm/mach-stm32mp/dram_init.c +++ b/arch/arm/mach-stm32mp/dram_init.c @@ -5,6 +5,10 @@ #include <common.h> #include <dm.h> +#include <image.h> +#include <init.h> +#include <lmb.h> +#include <log.h> #include <ram.h> DECLARE_GLOBAL_DATA_PTR; @@ -31,3 +35,20 @@ int dram_init(void) return 0; } + +ulong board_get_usable_ram_top(ulong total_size) +{ + phys_addr_t reg; + struct lmb lmb; + + /* found enough not-reserved memory to relocated U-Boot */ + lmb_init(&lmb); + lmb_add(&lmb, gd->ram_base, gd->ram_size); + boot_fdt_add_mem_rsv_regions(&lmb, (void *)gd->fdt_blob); + reg = lmb_alloc(&lmb, CONFIG_SYS_MALLOC_LEN + total_size, SZ_4K); + + if (reg) + return ALIGN(reg + CONFIG_SYS_MALLOC_LEN + total_size, SZ_4K); + + return gd->ram_top; +} diff --git a/arch/arm/mach-stm32mp/fdt.c b/arch/arm/mach-stm32mp/fdt.c index 3ee7d6a833..c723b223e0 100644 --- a/arch/arm/mach-stm32mp/fdt.c +++ b/arch/arm/mach-stm32mp/fdt.c @@ -5,6 +5,8 @@ #include <common.h> #include <fdt_support.h> +#include <log.h> +#include <tee.h> #include <asm/arch/sys_proto.h> #include <dt-bindings/pinctrl/stm32-pinfunc.h> #include <linux/io.h> @@ -218,6 +220,26 @@ static void stm32_fdt_disable(void *fdt, int offset, u32 addr, string, addr, name); } +static void stm32_fdt_disable_optee(void *blob) +{ + int off, node; + + off = fdt_node_offset_by_compatible(blob, -1, "linaro,optee-tz"); + if (off >= 0 && fdtdec_get_is_enabled(blob, off)) + fdt_status_disabled(blob, off); + + /* Disabled "optee@..." reserved-memory node */ + off = fdt_path_offset(blob, "/reserved-memory/"); + if (off < 0) + return; + for (node = fdt_first_subnode(blob, off); + node >= 0; + node = fdt_next_subnode(blob, node)) { + if (!strncmp(fdt_get_name(blob, node, NULL), "optee@", 6)) + fdt_status_disabled(blob, node); + } +} + /* * This function is called right before the kernel is booted. "blob" is the * device tree that will be passed to the kernel. @@ -302,5 +324,9 @@ int ft_system_setup(void *blob, bd_t *bd) "st,package", pkg, false); } + if (!CONFIG_IS_ENABLED(OPTEE) || + !tee_find_device(NULL, NULL, NULL, NULL)) + stm32_fdt_disable_optee(blob); + return ret; } diff --git a/arch/arm/mach-stm32mp/include/mach/stm32.h b/arch/arm/mach-stm32mp/include/mach/stm32.h index 76d593d785..5fdb893b0e 100644 --- a/arch/arm/mach-stm32mp/include/mach/stm32.h +++ b/arch/arm/mach-stm32mp/include/mach/stm32.h @@ -6,6 +6,10 @@ #ifndef _MACH_STM32_H_ #define _MACH_STM32_H_ +#ifndef __ASSEMBLY__ +#include <linux/bitops.h> +#endif + /* * Peripheral memory map * only address used before device tree parsing @@ -80,6 +84,9 @@ enum boot_device { BOOT_SERIAL_USB = 0x60, BOOT_SERIAL_USB_OTG = 0x62, + + BOOT_FLASH_SPINAND = 0x70, + BOOT_FLASH_SPINAND_1 = 0x71, }; /* TAMP registers */ @@ -130,5 +137,5 @@ enum forced_boot_mode { #define BSEC_OTP_MAC 57 #define BSEC_OTP_BOARD 59 -#endif /* __ASSEMBLY__*/ +#endif /* __ASSEMBLY__ */ #endif /* _MACH_STM32_H_ */ diff --git a/arch/arm/mach-stm32mp/include/mach/stm32prog.h b/arch/arm/mach-stm32mp/include/mach/stm32prog.h new file mode 100644 index 0000000000..c080b9cc42 --- /dev/null +++ b/arch/arm/mach-stm32mp/include/mach/stm32prog.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/* + * Copyright (C) 2020, STMicroelectronics - All Rights Reserved + */ + +#define STM32PROG_VIRT_FIRST_DEV_NUM 0xF1 + +int stm32prog_write_medium_virt(struct dfu_entity *dfu, u64 offset, + void *buf, long *len); +int stm32prog_read_medium_virt(struct dfu_entity *dfu, u64 offset, + void *buf, long *len); +int stm32prog_get_medium_size_virt(struct dfu_entity *dfu, u64 *size); + +bool stm32prog_get_tee_partitions(void); + +bool stm32prog_get_fsbl_nor(void); diff --git a/arch/arm/mach-stm32mp/include/mach/sys_proto.h b/arch/arm/mach-stm32mp/include/mach/sys_proto.h index 1617126bea..4b6c7b8bdd 100644 --- a/arch/arm/mach-stm32mp/include/mach/sys_proto.h +++ b/arch/arm/mach-stm32mp/include/mach/sys_proto.h @@ -20,6 +20,11 @@ /* return CPU_STMP32MP...Xxx constants */ u32 get_cpu_type(void); +#define CPU_DEV_STM32MP15 0x500 + +/* return CPU_DEV constants */ +u32 get_cpu_dev(void); + #define CPU_REVA 0x1000 #define CPU_REVB 0x2000 #define CPU_REVZ 0x2001 diff --git a/arch/arm/mach-stm32mp/psci.c b/arch/arm/mach-stm32mp/psci.c index 3fb038d3e7..155aa79cd5 100644 --- a/arch/arm/mach-stm32mp/psci.c +++ b/arch/arm/mach-stm32mp/psci.c @@ -6,10 +6,12 @@ #include <config.h> #include <common.h> #include <asm/armv7.h> +#include <asm/cache.h> #include <asm/gic.h> #include <asm/io.h> #include <asm/psci.h> #include <asm/secure.h> +#include <linux/bitops.h> #define BOOT_API_A7_CORE0_MAGIC_NUMBER 0xCA7FACE0 #define BOOT_API_A7_CORE1_MAGIC_NUMBER 0xCA7FACE1 diff --git a/arch/arm/mach-stm32mp/pwr_regulator.c b/arch/arm/mach-stm32mp/pwr_regulator.c index b52e1e85d5..900dee4c38 100644 --- a/arch/arm/mach-stm32mp/pwr_regulator.c +++ b/arch/arm/mach-stm32mp/pwr_regulator.c @@ -9,6 +9,7 @@ #include <syscon.h> #include <asm/io.h> #include <dm/device_compat.h> +#include <linux/bitops.h> #include <linux/err.h> #include <power/pmic.h> #include <power/regulator.h> diff --git a/arch/arm/mach-stm32mp/spl.c b/arch/arm/mach-stm32mp/spl.c index f85391c6af..39413e9a0e 100644 --- a/arch/arm/mach-stm32mp/spl.c +++ b/arch/arm/mach-stm32mp/spl.c @@ -4,9 +4,13 @@ */ #include <common.h> +#include <cpu_func.h> #include <dm.h> #include <hang.h> +#include <init.h> +#include <log.h> #include <spl.h> +#include <asm/cache.h> #include <asm/io.h> #include <asm/arch/sys_proto.h> #include <linux/libfdt.h> @@ -39,6 +43,8 @@ u32 spl_boot_device(void) return BOOT_DEVICE_NAND; case BOOT_FLASH_NOR_QSPI: return BOOT_DEVICE_SPI; + case BOOT_FLASH_SPINAND_1: + return BOOT_DEVICE_NONE; /* SPINAND not supported in SPL */ } return BOOT_DEVICE_MMC1; @@ -76,6 +82,11 @@ void spl_display_print(void) } #endif +__weak int board_early_init_f(void) +{ + return 0; +} + void board_init_f(ulong dummy) { struct udevice *dev; @@ -92,27 +103,51 @@ void board_init_f(ulong dummy) ret = uclass_get_device(UCLASS_CLK, 0, &dev); if (ret) { debug("Clock init failed: %d\n", ret); - return; + hang(); } ret = uclass_get_device(UCLASS_RESET, 0, &dev); if (ret) { debug("Reset init failed: %d\n", ret); - return; + hang(); } ret = uclass_get_device(UCLASS_PINCTRL, 0, &dev); if (ret) { debug("%s: Cannot find pinctrl device\n", __func__); - return; + hang(); } /* enable console uart printing */ preloader_console_init(); + ret = board_early_init_f(); + if (ret) { + debug("board_early_init_f() failed: %d\n", ret); + hang(); + } + ret = uclass_get_device(UCLASS_RAM, 0, &dev); if (ret) { printf("DRAM init failed: %d\n", ret); hang(); } + + /* + * activate cache on DDR only when DDR is fully initialized + * to avoid speculative access and issue in get_ram_size() + */ + if (!CONFIG_IS_ENABLED(SYS_DCACHE_OFF)) + mmu_set_region_dcache_behaviour(STM32_DDR_BASE, STM32_DDR_SIZE, + DCACHE_DEFAULT_OPTION); +} + +void spl_board_prepare_for_boot(void) +{ + dcache_disable(); +} + +void spl_board_prepare_for_boot_linux(void) +{ + dcache_disable(); } |