[PATCH v2] cmd: mtd: OTP access support

Arseniy Krasnov avkrasnov at salutedevices.com
Mon Jan 8 19:33:43 CET 2024


Sorry, pls ping

Thanks, Arseniy

On 20.12.2023 22:36, Arseniy Krasnov wrote:
> Add access to OTP region. It supports info, dump, write and lock
> operations.
> 
> Signed-off-by: Arseniy Krasnov <avkrasnov at salutedevices.com>
> ---
>  Changelog:
>  v1 -> v2:
>   * Remove warning that OTP can't be erased after write.
> 
>  cmd/Kconfig |   1 +
>  cmd/mtd.c   | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 225 insertions(+)
> 
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 90e4ef93e0..c47523a03b 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1354,6 +1354,7 @@ config CMD_MTD
>  	bool "mtd"
>  	depends on MTD
>  	select MTD_PARTITIONS
> +	select HEXDUMP
>  	help
>  	  MTD commands support.
>  
> diff --git a/cmd/mtd.c b/cmd/mtd.c
> index eb6e2d6892..1ab69b108b 100644
> --- a/cmd/mtd.c
> +++ b/cmd/mtd.c
> @@ -11,6 +11,7 @@
>  #include <command.h>
>  #include <common.h>
>  #include <console.h>
> +#include <hexdump.h>
>  #include <malloc.h>
>  #include <mapmem.h>
>  #include <mtd.h>
> @@ -202,6 +203,219 @@ static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op)
>  	return true;
>  }
>  
> +static int do_mtd_otp_read(struct cmd_tbl *cmdtp, int flag, int argc,
> +			   char *const argv[])
> +{
> +	struct mtd_info *mtd;
> +	size_t retlen;
> +	off_t from;
> +	size_t len;
> +	bool user;
> +	int ret;
> +	u8 *buf;
> +
> +	if (argc != 5)
> +		return CMD_RET_USAGE;
> +
> +	if (!strcmp(argv[2], "u"))
> +		user = true;
> +	else if (!strcmp(argv[2], "f"))
> +		user = false;
> +	else
> +		return CMD_RET_USAGE;
> +
> +	mtd = get_mtd_by_name(argv[1]);
> +	if (IS_ERR_OR_NULL(mtd))
> +		return CMD_RET_FAILURE;
> +
> +	from = simple_strtoul(argv[3], NULL, 0);
> +	len = simple_strtoul(argv[4], NULL, 0);
> +
> +	ret = CMD_RET_FAILURE;
> +
> +	buf = malloc(len);
> +	if (!buf)
> +		goto put_mtd;
> +
> +	printf("Reading %s OTP from 0x%lx, %lu bytes\n",
> +	       user ? "user" : "factory", from, len);
> +
> +	if (user)
> +		ret = mtd_read_user_prot_reg(mtd, from, len, &retlen, buf);
> +	else
> +		ret = mtd_read_fact_prot_reg(mtd, from, len, &retlen, buf);
> +	if (ret) {
> +		free(buf);
> +		pr_err("OTP read failed: %d\n", ret);
> +		ret = CMD_RET_FAILURE;
> +		goto put_mtd;
> +	}
> +
> +	if (retlen != len)
> +		pr_err("OTP read returns %zu, but %zu expected\n",
> +		       retlen, len);
> +
> +	print_hex_dump("", 0, 16, 1, buf, retlen, true);
> +
> +	free(buf);
> +
> +	ret = CMD_RET_SUCCESS;
> +
> +put_mtd:
> +	put_mtd_device(mtd);
> +
> +	return ret;
> +}
> +
> +static int do_mtd_otp_lock(struct cmd_tbl *cmdtp, int flag, int argc,
> +			   char *const argv[])
> +{
> +	struct mtd_info *mtd;
> +	off_t from;
> +	size_t len;
> +	int ret;
> +
> +	if (argc != 4)
> +		return CMD_RET_USAGE;
> +
> +	mtd = get_mtd_by_name(argv[1]);
> +	if (IS_ERR_OR_NULL(mtd))
> +		return CMD_RET_FAILURE;
> +
> +	from = simple_strtoul(argv[2], NULL, 0);
> +	len = simple_strtoul(argv[3], NULL, 0);
> +
> +	ret = mtd_lock_user_prot_reg(mtd, from, len);
> +	if (ret) {
> +		pr_err("OTP lock failed: %d\n", ret);
> +		ret = CMD_RET_FAILURE;
> +		goto put_mtd;
> +	}
> +
> +	ret = CMD_RET_SUCCESS;
> +
> +put_mtd:
> +	put_mtd_device(mtd);
> +
> +	return ret;
> +}
> +
> +static int do_mtd_otp_write(struct cmd_tbl *cmdtp, int flag, int argc,
> +			    char *const argv[])
> +{
> +	struct mtd_info *mtd;
> +	size_t retlen;
> +	size_t binlen;
> +	u8 *binbuf;
> +	off_t from;
> +	int ret;
> +
> +	if (argc != 4)
> +		return CMD_RET_USAGE;
> +
> +	mtd = get_mtd_by_name(argv[1]);
> +	if (IS_ERR_OR_NULL(mtd))
> +		return CMD_RET_FAILURE;
> +
> +	from = simple_strtoul(argv[2], NULL, 0);
> +	binlen = strlen(argv[3]) / 2;
> +
> +	ret = CMD_RET_FAILURE;
> +	binbuf = malloc(binlen);
> +	if (!binbuf)
> +		goto put_mtd;
> +
> +	hex2bin(binbuf, argv[3], binlen);
> +
> +	printf("Will write:\n");
> +
> +	print_hex_dump("", 0, 16, 1, binbuf, binlen, true);
> +
> +	printf("to 0x%zx\n", from);
> +
> +	printf("Continue (y/n)?\n");
> +
> +	if (confirm_yesno() != 1) {
> +		pr_err("OTP write canceled\n");
> +		ret = CMD_RET_SUCCESS;
> +		goto put_mtd;
> +	}
> +
> +	ret = mtd_write_user_prot_reg(mtd, from, binlen, &retlen, binbuf);
> +	if (ret) {
> +		pr_err("OTP write failed: %d\n", ret);
> +		ret = CMD_RET_FAILURE;
> +		goto put_mtd;
> +	}
> +
> +	if (retlen != binlen)
> +		pr_err("OTP write returns %zu, but %zu expected\n",
> +		       retlen, binlen);
> +
> +	ret = CMD_RET_SUCCESS;
> +
> +put_mtd:
> +	free(binbuf);
> +	put_mtd_device(mtd);
> +
> +	return ret;
> +}
> +
> +static int do_mtd_otp_info(struct cmd_tbl *cmdtp, int flag, int argc,
> +			   char *const argv[])
> +{
> +	struct otp_info otp_info;
> +	struct mtd_info *mtd;
> +	size_t retlen;
> +	bool user;
> +	int ret;
> +
> +	if (argc != 3)
> +		return CMD_RET_USAGE;
> +
> +	if (!strcmp(argv[2], "u"))
> +		user = true;
> +	else if (!strcmp(argv[2], "f"))
> +		user = false;
> +	else
> +		return CMD_RET_USAGE;
> +
> +	mtd = get_mtd_by_name(argv[1]);
> +	if (IS_ERR_OR_NULL(mtd))
> +		return CMD_RET_FAILURE;
> +
> +	if (user)
> +		ret = mtd_get_user_prot_info(mtd, sizeof(otp_info), &retlen,
> +					     &otp_info);
> +	else
> +		ret = mtd_get_fact_prot_info(mtd, sizeof(otp_info), &retlen,
> +					     &otp_info);
> +	if (ret) {
> +		pr_err("OTP info failed: %d\n", ret);
> +		ret = CMD_RET_FAILURE;
> +		goto put_mtd;
> +	}
> +
> +	if (retlen != sizeof(otp_info)) {
> +		pr_err("OTP info returns %zu, but %zu expected\n",
> +		       retlen, sizeof(otp_info));
> +		ret = CMD_RET_FAILURE;
> +		goto put_mtd;
> +	}
> +
> +	printf("%s OTP region info:\n", user ? "User" : "Factory");
> +	printf("\tstart: %u\n", otp_info.start);
> +	printf("\tlength: %u\n", otp_info.length);
> +	printf("\tlocked: %u\n", otp_info.locked);
> +
> +	ret = CMD_RET_SUCCESS;
> +
> +put_mtd:
> +	put_mtd_device(mtd);
> +
> +	return ret;
> +}
> +
>  static int do_mtd_list(struct cmd_tbl *cmdtp, int flag, int argc,
>  		       char *const argv[])
>  {
> @@ -552,6 +766,10 @@ static char mtd_help_text[] =
>  	"\n"
>  	"Specific functions:\n"
>  	"mtd bad                               <name>\n"
> +	"mtd otpread                           <name> [u|f] <off> <size>\n"
> +	"mtd otpwrite                          <name> <off> <hex string>\n"
> +	"mtd otplock                           <name> <off> <size>\n"
> +	"mtd otpinfo                           <name> [u|f]\n"
>  	"\n"
>  	"With:\n"
>  	"\t<name>: NAND partition/chip name (or corresponding DM device name or OF path)\n"
> @@ -562,11 +780,17 @@ static char mtd_help_text[] =
>  	"\t<size>: length of the operation in bytes (default: the entire device)\n"
>  	"\t\t* must be a multiple of a block for erase\n"
>  	"\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n"
> +	"\t<hex string>: hex string without '0x' and spaces. Example: ABCD1234\n"
> +	"\t[u|f]: user or factory OTP region\n"
>  	"\n"
>  	"The .dontskipff option forces writing empty pages, don't use it if unsure.\n";
>  #endif
>  
>  U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
> +		U_BOOT_SUBCMD_MKENT(otpread, 5, 1, do_mtd_otp_read),
> +		U_BOOT_SUBCMD_MKENT(otpwrite, 4, 1, do_mtd_otp_write),
> +		U_BOOT_SUBCMD_MKENT(otplock, 4, 1, do_mtd_otp_lock),
> +		U_BOOT_SUBCMD_MKENT(otpinfo, 3, 1, do_mtd_otp_info),
>  		U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list),
>  		U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io,
>  					     mtd_name_complete),


More information about the U-Boot mailing list