[PATCH 04/18] stm32mp: add the command stm32prog

Patrice CHOTARD patrice.chotard at st.com
Tue Apr 14 15:03:07 CEST 2020


Hi

On 3/18/20 9:24 AM, Patrick Delaunay wrote:
> Add a specific command stm32prog for STM32MP soc family
> witch allows to program the boot devices with the tool
> STM32CubeProgrammer (http://www.st.com/STM32CubeProg).
>
> This command uses the same UART STM32 protocol than MCU
> STM32 with or USB with DFU protocol v1.1 (ithe MCU ST
> extension are no supported).
>
> The executed actions are based on a tab separated value file
> with a stm32 header, the FlashLayout file
> (https://wiki.st.com/stm32mpu/wiki/STM32CubeProgrammer_flashlayout).
>
> This file is parsed by the U-Boot command to:
> - initialize the devices
> - create the partition table on each device
> - initialize the DFU backend to access to not volatile memory
>   (NOR/NAND/SD/eMMC) or to virtual device (OTP/PMIC)
>
> Up to STM32PROG_MAX_DEV (5) devices can be updated with a FlashLayout.
>
> The communication between U-Boot and STM32CubeProgrammer is done with
> the specific alternate configuration (see "AN5275: USB DFU/USART protocols
> used in STM32MP1 Series bootloaders" for details).
>
> The command stm32prog is executed when a boot from USB is detected
> (selected with bootpins) and we can program the boot devices with
> a simple command (on Windows or Linux):
>
> PC $>  STM32_Programmer_CLI -c port=usb1 -w flaslayout.tsv
>
> 1/ the ROM code loads TF-A in embedded RAM (DFU or uart)
> 2/ TF-A loads flashlayout file and U-Boot in DDR (DFU or uart)
> 3/ U-Boot executes the stm32prog command (DFU or uart)
>
> Signed-off-by: Patrick Delaunay <patrick.delaunay at st.com>
> ---
>
>  arch/arm/mach-stm32mp/Kconfig                 |  12 +
>  arch/arm/mach-stm32mp/Makefile                |   1 +
>  arch/arm/mach-stm32mp/cmd_stm32prog/Makefile  |   8 +
>  .../cmd_stm32prog/cmd_stm32prog.c             |  96 ++++
>  .../mach-stm32mp/cmd_stm32prog/stm32prog.c    | 480 ++++++++++++++++++
>  .../mach-stm32mp/cmd_stm32prog/stm32prog.h    | 137 +++++
>  .../cmd_stm32prog/stm32prog_usb.c             | 206 ++++++++
>  .../arm/mach-stm32mp/include/mach/stm32prog.h |  12 +
>  board/st/common/stm32mp_dfu.c                 |  20 +
>  configs/stm32mp15_basic_defconfig             |   3 +-
>  configs/stm32mp15_trusted_defconfig           |   3 +-
>  11 files changed, 974 insertions(+), 4 deletions(-)
>  create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/Makefile
>  create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c
>  create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c
>  create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h
>  create mode 100644 arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c
>  create mode 100644 arch/arm/mach-stm32mp/include/mach/stm32prog.h
>
> diff --git a/arch/arm/mach-stm32mp/Kconfig b/arch/arm/mach-stm32mp/Kconfig
> index 1a5545b98d..61466f6125 100644
> --- a/arch/arm/mach-stm32mp/Kconfig
> +++ b/arch/arm/mach-stm32mp/Kconfig
> @@ -114,6 +114,18 @@ 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
> +	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
> diff --git a/arch/arm/mach-stm32mp/Makefile b/arch/arm/mach-stm32mp/Makefile
> index 19ca3b08a5..c6ab3cecf0 100644
> --- a/arch/arm/mach-stm32mp/Makefile
> +++ b/arch/arm/mach-stm32mp/Makefile
> @@ -10,6 +10,7 @@ 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
> diff --git a/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile b/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile
> new file mode 100644
> index 0000000000..14f722759c
> --- /dev/null
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/Makefile
> @@ -0,0 +1,8 @@
> +# 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_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..3e8b426444
> --- /dev/null
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/cmd_stm32prog.c
> @@ -0,0 +1,96 @@
> +// 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 "stm32prog.h"
> +
> +struct stm32prog_data *stm32prog_data;
> +
> +static int do_stm32prog(cmd_tbl_t *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 stm32prog_data *data;
> +
> +	if (argc < 3 ||  argc > 5)
> +		return CMD_RET_USAGE;
> +
> +	if (!strcmp(argv[1], "usb"))
> +		link = LINK_USB;
> +
> +	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);
> +
> +	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_USB:
> +		reset = stm32prog_usb_loop(data, dev);
> +		break;
> +	default:
> +		goto cleanup;
> +	}
> +
> +	stm32prog_clean(data);
> +	free(stm32prog_data);
> +	stm32prog_data = NULL;
> +
> +	puts("Download done\n");
> +	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> = usb\n"
> +	   "<dev>  = device instance\n"
> +	   "<addr> = address of flashlayout\n"
> +	   "<size> = size of flashlayout\n"
> +);
> 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..e2c6c43d88
> --- /dev/null
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.c
> @@ -0,0 +1,480 @@
> +// 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 <dm/uclass.h>
> +#include <linux/list.h>
> +#include <linux/list_sort.h>
> +#include <linux/sizes.h>
> +
> +#include "stm32prog.h"
> +
> +#define OPT_SELECT	BIT(0)
> +#define OPT_EMPTY	BIT(1)
> +
> +#define IS_SELECT(part)	((part)->option & OPT_SELECT)
> +#define IS_EMPTY(part)	((part)->option & OPT_EMPTY)
> +
> +#define ALT_BUF_LEN			SZ_1K
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +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;
> +}
> +
> +static int parse_flash_layout(struct stm32prog_data *data,
> +			      ulong addr,
> +			      ulong size)
> +{
> +	return -ENODEV;
> +}
> +
> +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);
> +
> +	return parta->addr > partb->addr ? 1 : -1;
> +}
> +
> +static int init_device(struct stm32prog_data *data,
> +		       struct stm32prog_dev_t *dev)
> +{
> +	struct blk_desc *block_dev = NULL;
> +	int part_id;
> +	u64 first_addr = 0, last_addr = 0;
> +	struct stm32prog_part_t *part, *next_part;
> +
> +	switch (dev->target) {
> +	default:
> +		stm32prog_err("unknown device type = %d", dev->target);
> +		return -ENODEV;
> +	}
> +
> +	/* 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->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 %02d.%02d %08llx %08llx\n",
> +				 part->option, part->id, part->name,
> +				 part->part_type, part->target,
> +				 part->dev_id, part->addr, part->size);
> +			continue;
> +		}
> +
> +		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;
> +		}
> +
> +		pr_debug("%02d : %1d %02x %14s %02d %02d.%02d %08llx %08llx",
> +			 part->part_id, part->option, part->id, part->name,
> +			 part->part_type, part->target,
> +			 part->dev_id, part->addr, part->size);
> +	}
> +	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);
> +	}
> +
> +	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_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;
> +		}
> +		part->dev = &data->dev[j];
> +		list_add_tail(&part->list, &data->dev[j].part_list);
> +	}
> +
> +	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->part_type == RAW_IMAGE) {
> +		u64 dfu_size;
> +
> +		dfu_size = part->size;
> +		offset += snprintf(buf + offset, ALT_BUF_LEN - offset,
> +				   "raw 0x0 0x%llx", dfu_size);
> +	} else {
> +		offset += snprintf(buf + offset,
> +				   ALT_BUF_LEN - offset,
> +				   "part");
> +		offset += snprintf(buf + offset, ALT_BUF_LEN - offset,
> +				   " %d;", part->part_id);
> +	}
> +	switch (part->target) {
> +	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 = 1; /* number of virtual = CMD */
> +	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)
> +		stm32prog_err("dfu init failed: %d", ret);
> +	puts("done\n");
> +
> +#ifdef DEBUG
> +	dfu_show_entities();
> +#endif
> +	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;
> +}
> +
> +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->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 void stm32prog_devices_init(struct stm32prog_data *data)
> +{
> +	int i;
> +	int ret;
> +
> +	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;
> +	}
> +
> +	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->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->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_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;
> +		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..b44b6f89af
> --- /dev/null
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog.h
> @@ -0,0 +1,137 @@
> +/* 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_END		0xFE
> +#define PHASE_RESET		0xFF
> +#define PHASE_DO_RESET		0x1FF
> +
> +#define DEFAULT_ADDRESS		0xFFFFFFFF
> +
> +enum stm32prog_target {
> +	STM32PROG_NONE,
> +};
> +
> +enum stm32prog_link_t {
> +	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;
> +	/* list of partition for this device / ordered in offset */
> +	struct list_head	part_list;
> +};
> +
> +/* 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;
> +
> +	/* information on associated device */
> +	struct stm32prog_dev_t	*dev;		/* pointer to device */
> +	u16			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 */
> +
> +	/* command internal information */
> +	unsigned int		phase;
> +	u32			offset;
> +	char			error[255];
> +	struct stm32prog_part_t	*cur_part;
> +
> +	/* STM32 header information */
> +	struct raw_header_s	*header_data;
> +	struct image_header_s	header;
> +};
> +
> +extern struct stm32prog_data *stm32prog_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);
> +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_usb.c b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c
> new file mode 100644
> index 0000000000..ed2cdbc66f
> --- /dev/null
> +++ b/arch/arm/mach-stm32mp/cmd_stm32prog/stm32prog_usb.c
> @@ -0,0 +1,206 @@
> +// 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;
> +		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;
> +			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);
> +	}
> +	*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);
> +	}
> +	*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;
> +	}
> +
> +	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 at 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/include/mach/stm32prog.h b/arch/arm/mach-stm32mp/include/mach/stm32prog.h
> new file mode 100644
> index 0000000000..c10bff09c8
> --- /dev/null
> +++ b/arch/arm/mach-stm32mp/include/mach/stm32prog.h
> @@ -0,0 +1,12 @@
> +/* 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);
> diff --git a/board/st/common/stm32mp_dfu.c b/board/st/common/stm32mp_dfu.c
> index e129f8c8b5..3bd005bb04 100644
> --- a/board/st/common/stm32mp_dfu.c
> +++ b/board/st/common/stm32mp_dfu.c
> @@ -11,6 +11,7 @@
>  #include <misc.h>
>  #include <mtd.h>
>  #include <mtd_node.h>
> +#include <asm/arch/stm32prog.h>
>  
>  #define DFU_ALT_BUF_LEN SZ_1K
>  
> @@ -211,12 +212,31 @@ int dfu_read_medium_virt(struct dfu_entity *dfu, u64 offset,
>  	case 0x1:
>  		return dfu_pmic_read(offset, buf, len);
>  	}
> +
> +	if (CONFIG_IS_ENABLED(CMD_STM32PROG) &&
> +	    dfu->data.virt.dev_num >= STM32PROG_VIRT_FIRST_DEV_NUM)
> +		return stm32prog_read_medium_virt(dfu, offset, buf, len);
> +
>  	*len = 0;
>  	return 0;
>  }
>  
> +int dfu_write_medium_virt(struct dfu_entity *dfu, u64 offset,
> +			  void *buf, long *len)
> +{
> +	if (CONFIG_IS_ENABLED(CMD_STM32PROG) &&
> +	    dfu->data.virt.dev_num >= STM32PROG_VIRT_FIRST_DEV_NUM)
> +		return stm32prog_write_medium_virt(dfu, offset, buf, len);
> +
> +	return -EOPNOTSUPP;
> +}
> +
>  int __weak dfu_get_medium_size_virt(struct dfu_entity *dfu, u64 *size)
>  {
> +	if (CONFIG_IS_ENABLED(CMD_STM32PROG) &&
> +	    dfu->data.virt.dev_num >= STM32PROG_VIRT_FIRST_DEV_NUM)
> +		return stm32prog_get_medium_size_virt(dfu, size);
> +
>  	*size = SZ_1K;
>  
>  	return 0;
> diff --git a/configs/stm32mp15_basic_defconfig b/configs/stm32mp15_basic_defconfig
> index f9bc5b6086..baed3d92f5 100644
> --- a/configs/stm32mp15_basic_defconfig
> +++ b/configs/stm32mp15_basic_defconfig
> @@ -6,6 +6,7 @@ CONFIG_ENV_OFFSET=0x280000
>  CONFIG_SPL_MMC_SUPPORT=y
>  CONFIG_SPL=y
>  CONFIG_TARGET_ST_STM32MP15x=y
> +CONFIG_CMD_STM32PROG=y
>  CONFIG_ENV_OFFSET_REDUND=0x2C0000
>  CONFIG_SPL_SPI_FLASH_SUPPORT=y
>  CONFIG_SPL_SPI_SUPPORT=y
> @@ -68,9 +69,7 @@ CONFIG_ENV_UBI_VOLUME_REDUND="uboot_config_r"
>  CONFIG_SYS_RELOC_GD_ENV_ADDR=y
>  CONFIG_STM32_ADC=y
>  CONFIG_DFU_MMC=y
> -CONFIG_DFU_RAM=y
>  CONFIG_DFU_MTD=y
> -CONFIG_DFU_VIRT=y
>  CONFIG_SET_DFU_ALT_INFO=y
>  CONFIG_USB_FUNCTION_FASTBOOT=y
>  CONFIG_FASTBOOT_BUF_ADDR=0xC0000000
> diff --git a/configs/stm32mp15_trusted_defconfig b/configs/stm32mp15_trusted_defconfig
> index f0d524d344..adbe304902 100644
> --- a/configs/stm32mp15_trusted_defconfig
> +++ b/configs/stm32mp15_trusted_defconfig
> @@ -4,6 +4,7 @@ CONFIG_SYS_MALLOC_F_LEN=0x3000
>  CONFIG_ENV_SECT_SIZE=0x40000
>  CONFIG_ENV_OFFSET=0x280000
>  CONFIG_TARGET_ST_STM32MP15x=y
> +CONFIG_CMD_STM32PROG=y
>  CONFIG_ENV_OFFSET_REDUND=0x2C0000
>  CONFIG_DISTRO_DEFAULTS=y
>  CONFIG_FIT=y
> @@ -54,9 +55,7 @@ CONFIG_ENV_UBI_VOLUME_REDUND="uboot_config_r"
>  CONFIG_SYS_RELOC_GD_ENV_ADDR=y
>  CONFIG_STM32_ADC=y
>  CONFIG_DFU_MMC=y
> -CONFIG_DFU_RAM=y
>  CONFIG_DFU_MTD=y
> -CONFIG_DFU_VIRT=y
>  CONFIG_SET_DFU_ALT_INFO=y
>  CONFIG_USB_FUNCTION_FASTBOOT=y
>  CONFIG_FASTBOOT_BUF_ADDR=0xC0000000

Reviewed-by: Patrice Chotard <patrice.chotard at st.com>

Thanks

Patrice


More information about the U-Boot mailing list