[PATCH v4] cmd: mtd: OTP access support
Arseniy Krasnov
avkrasnov at salutedevices.com
Fri Mar 22 09:08:54 CET 2024
On 22.03.2024 11:17, Michael Nazzareno Trimarchi wrote:
> Hi Arseniy
>
> On Fri, Mar 22, 2024 at 9:14 AM Arseniy Krasnov
> <avkrasnov at salutedevices.com> wrote:
>>
>> Hi,
>>
>> On 22.03.2024 11:12, Michael Nazzareno Trimarchi wrote:
>>> Hi Arseniy
>>>
>>> On Wed, Mar 20, 2024 at 8:14 PM Arseniy Krasnov
>>> <avkrasnov at salutedevices.com> wrote:
>>>>
>>>> Add access to OTP region. It supports info, dump, write and lock
>>>> operations. Usage example:
>>>>
>>>> 'mtd otpread nand0 u 0 1024' - dump 1024 bytes of user area starting
>>>> from offset 0 of device 'nand0'.
>>>>
>>>> 'mtd otpwrite nand0 10 11223344' - write binary data 0x11, 0x22, 0x33,
>>>> 0x44 to offset 10 to user area of device 'nand0'.
>>>>
>>>> 'mtd otplock nand0 0 1024' - lock 1024 bytes of user area starting
>>>> from offset 0 of device 'nand0'.
>>>>
>>>> 'mtd otpinfo nand0 f' - show info about factory area of device 'nand0'.
>>>>
>>>> Signed-off-by: Arseniy Krasnov <avkrasnov at salutedevices.com>
>>>> Reviewed-by: Michael Trimarchi <michael at amarulasolutions.com>
>>>> ---
>>>> Changelog:
>>>> v1 -> v2:
>>>> * Remove warning that OTP can't be erased after write.
>>>> v2 -> v3:
>>>> * Commit message updated by adding usage.
>>>> * R-b added.
>>>> v3 -> v4:
>>>> * Fix build failure due to invalid format strings for 'printf()'.
>>>> * Rebase over latest version of cmd/mtd.c.
>>>>
>>>> cmd/Kconfig | 1 +
>>>> cmd/mtd.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>> 2 files changed, 225 insertions(+)
>>>>
>>>> diff --git a/cmd/Kconfig b/cmd/Kconfig
>>>> index 7292a150f5..f218dc6cf2 100644
>>>> --- a/cmd/Kconfig
>>>> +++ b/cmd/Kconfig
>>>> @@ -1363,6 +1363,7 @@ config CMD_MTD
>>>> bool "mtd"
>>>> depends on MTD
>>>> select MTD_PARTITIONS
>>>> + select HEXDUMP
>>>
>>> Make those change forced in increase the size of uboot and break this build
>>>
>>> https://source.denx.de/u-boot/custodians/u-boot-nand-flash/-/jobs/802378
>>>
>>> I think that those command could be optional and not forced to be
>>> included by default
>>>
>>
>> Ah, Ok:) I'll add it as config option which depends on CMD_MTD in the next version
>>
>
> I am really glad how you interact with the mailing list ;) and how you
> are fast on feedback
>
Sure, thanks!
>
> Michael
>
>> Thanks
>>
>>> Michael
>>>
>>>
>>>> help
>>>> MTD commands support.
>>>>
>>>> diff --git a/cmd/mtd.c b/cmd/mtd.c
>>>> index e63c011e79..2b03a85ef0 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, %zu 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%lx\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[])
>>>> {
>>>> @@ -551,6 +765,10 @@ U_BOOT_LONGHELP(mtd,
>>>> "\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"
>>>> @@ -561,10 +779,16 @@ U_BOOT_LONGHELP(mtd,
>>>> "\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");
>>>>
>>>> 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),
>>>> --
>>>> 2.35.0
>>>>
>>>
>>>
>
>
>
> --
> Michael Nazzareno Trimarchi
> Co-Founder & Chief Executive Officer
> M. +39 347 913 2170
> michael at amarulasolutions.com
> __________________________________
>
> Amarula Solutions BV
> Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
> T. +31 (0)85 111 9172
> info at amarulasolutions.com
> www.amarulasolutions.com
More information about the U-Boot
mailing list