Message ID | 20231220193649.3154346-1-avkrasnov@salutedevices.com |
---|---|
State | Superseded |
Delegated to: | Dario Binacchi |
Headers | show |
Series | [v2] cmd: mtd: OTP access support | expand |
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@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),
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@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),
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@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),
Hi On Wed, Mar 13, 2024 at 7:43 AM Arseniy Krasnov <avkrasnov@salutedevices.com> wrote: > > 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@salutedevices.com> > >>> --- Please extend the commit message with some example of the usage otherwise Reviewed-by: Michael Trimarchi <michael@amarulasolutions.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),
On 13.03.2024 09:48, Michael Nazzareno Trimarchi wrote: > Hi > > On Wed, Mar 13, 2024 at 7:43 AM Arseniy Krasnov > <avkrasnov@salutedevices.com> wrote: >> >> 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@salutedevices.com> >>>>> --- > > Please extend the commit message with some example of the usage otherwise Done in v3 Thanks, Arseniy > > Reviewed-by: Michael Trimarchi <michael@amarulasolutions.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), > > >
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),
Add access to OTP region. It supports info, dump, write and lock operations. Signed-off-by: Arseniy Krasnov <avkrasnov@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(+)