[PATCH v5] cmd: mtd: OTP access support

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@salutedevices.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. v4 -> v5: * Implement commands from this patch as config option due to too big final size of the uboot image. * R-b removed because of patch update.
cmd/Kconfig | 7 ++ cmd/mtd.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 7292a150f5..832098e66e 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1366,6 +1366,13 @@ config CMD_MTD help MTD commands support.
+config CMD_MTD_OTP + bool "mtd otp" + depends on CMD_MTD + select HEXDUMP + help + MTD commands for OTP access. + config CMD_MUX bool "mux" depends on MULTIPLEXER diff --git a/cmd/mtd.c b/cmd/mtd.c index e63c011e79..c66105e373 100644 --- a/cmd/mtd.c +++ b/cmd/mtd.c @@ -11,6 +11,9 @@ #include <command.h> #include <common.h> #include <console.h> +#if CONFIG_IS_ENABLED(CMD_MTD_OTP) +#include <hexdump.h> +#endif #include <malloc.h> #include <mapmem.h> #include <mtd.h> @@ -202,6 +205,221 @@ static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op) return true; }
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP) +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; +} +#endif + static int do_mtd_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -551,6 +769,12 @@ U_BOOT_LONGHELP(mtd, "\n" "Specific functions:\n" "mtd bad <name>\n" +#if CONFIG_IS_ENABLED(CMD_MTD_OTP) + "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" +#endif "\n" "With:\n" "\t<name>: NAND partition/chip name (or corresponding DM device name or OF path)\n" @@ -561,10 +785,20 @@ 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" +#if CONFIG_IS_ENABLED(CMD_MTD_OTP) + "\t<hex string>: hex string without '0x' and spaces. Example: ABCD1234\n" + "\t[u|f]: user or factory OTP region\n" +#endif "\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, +#if CONFIG_IS_ENABLED(CMD_MTD_OTP) + 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), +#endif U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list), U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io, mtd_name_complete),

Hi Arseniy,
On Tue, Mar 26, 2024 at 11:49 PM Arseniy Krasnov avkrasnov@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@salutedevices.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.
v4 -> v5:
- Implement commands from this patch as config option due to too big final size of the uboot image.
- R-b removed because of patch update.
cmd/Kconfig | 7 ++ cmd/mtd.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+)
diff --git a/cmd/Kconfig b/cmd/Kconfig index 7292a150f5..832098e66e 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1366,6 +1366,13 @@ config CMD_MTD help MTD commands support.
+config CMD_MTD_OTP
bool "mtd otp"
depends on CMD_MTD
select HEXDUMP
help
MTD commands for OTP access.
config CMD_MUX bool "mux" depends on MULTIPLEXER diff --git a/cmd/mtd.c b/cmd/mtd.c index e63c011e79..c66105e373 100644 --- a/cmd/mtd.c +++ b/cmd/mtd.c @@ -11,6 +11,9 @@ #include <command.h> #include <common.h> #include <console.h> +#if CONFIG_IS_ENABLED(CMD_MTD_OTP) +#include <hexdump.h> +#endif #include <malloc.h> #include <mapmem.h> #include <mtd.h> @@ -202,6 +205,221 @@ static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op) return true; }
+#if CONFIG_IS_ENABLED(CMD_MTD_OTP) +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;
+} +#endif
static int do_mtd_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -551,6 +769,12 @@ U_BOOT_LONGHELP(mtd, "\n" "Specific functions:\n" "mtd bad <name>\n" +#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
"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"
+#endif "\n" "With:\n" "\t<name>: NAND partition/chip name (or corresponding DM device name or OF path)\n" @@ -561,10 +785,20 @@ 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" +#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
"\t<hex string>: hex string without '0x' and spaces. Example: ABCD1234\n"
"\t[u|f]: user or factory OTP region\n"
+#endif "\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, +#if CONFIG_IS_ENABLED(CMD_MTD_OTP)
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),
+#endif 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
Applied to nand-next
Thanks and regards Dario
participants (2)
-
Arseniy Krasnov
-
Dario Binacchi