[PATCH v2] cmd: mtd: OTP access support
Arseniy Krasnov
avkrasnov at salutedevices.com
Wed Mar 13 07:33:45 CET 2024
Sorry, please ping
Thanks, Arseniy
On 11.02.2024 02:16, Arseniy Krasnov wrote:
> Sorry, pls ping
>
> Thanks, Arseniy
>
> On 08.01.2024 21:33, Arseniy Krasnov wrote:
>> 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