Today "mmc enh_area /dev/mmcX" can only size the enhanced user data area to the device-reported maximum (MAX_ENH_SIZE_MULT). For systems that want a high-reliability prefix covering bootloader and system images but keep the trailing user-data partition in default MLC/TLC mode, an explicit size is required.
Extend the command with an "set" subcommand matching the Linux mmc-utils ergonomics: mmc enh_area set [-c] <start_KiB> <length_KiB> /dev/mmcX The length is rounded up to the device's enhanced-area unit (HC_WP_GRP_SIZE * HC_ERASE_GRP_SIZE * 512 KiB) so a target region is always fully covered, and validated against MAX_ENH_SIZE_MULT before any EXT_CSD write. The actual provisioned size is printed. The legacy "mmc enh_area [-c] /dev/mmcX" form keeps its existing "fill the whole user area" behaviour. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Johannes Schneider <[email protected]> --- commands/mmc.c | 139 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 129 insertions(+), 10 deletions(-) diff --git a/commands/mmc.c b/commands/mmc.c index dafc3a755f..d379e6fea2 100644 --- a/commands/mmc.c +++ b/commands/mmc.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only #include <command.h> +#include <errno.h> #include <mci.h> #include <stdio.h> #include <string.h> #include <getopt.h> #include <linux/kernel.h> +#include <linux/kstrtox.h> #include <dma.h> static int mmc_enh_area_setmax(struct mci *mci, u8 *ext_csd) @@ -56,6 +58,91 @@ static int mmc_enh_area_setmax(struct mci *mci, u8 *ext_csd) return 0; } +/* + * Provision a sized enhanced user data area at @start_kib of @length_kib + * bytes; length is rounded up to the device's WP-group unit. The one-shot + * PARTITION_SETTING_COMPLETED is not written here. + */ +static int mmc_enh_area_set(struct mci *mci, u8 *ext_csd, + u64 start_kib, u64 length_kib) +{ + u32 hc_wp_grp = ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + u32 hc_erase_grp = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + u32 max_mult = (u32)ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT] | + (u32)ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT + 1] << 8 | + (u32)ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT + 2] << 16; + u64 unit_kib, start_sectors; + u32 size_mult; + int i, ret; + + if (!hc_wp_grp || !hc_erase_grp) { + printf("Device reports zero HC_WP_GRP_SIZE / HC_ERASE_GRP_SIZE\n"); + return -EINVAL; + } + + /* JEDEC unit for ENH_START_ADDR / ENH_SIZE_MULT */ + unit_kib = (u64)hc_wp_grp * hc_erase_grp * 512; + + if (start_kib % unit_kib) { + printf("Start %llu KiB not a multiple of unit %llu KiB\n", + (unsigned long long)start_kib, + (unsigned long long)unit_kib); + return -EINVAL; + } + + /* Round length up so the requested region is fully covered */ + size_mult = (length_kib + unit_kib - 1) / unit_kib; + + if (size_mult > max_mult) { + printf("Requested %llu KiB (mult=%u) exceeds MAX_ENH_SIZE_MULT=%u\n", + (unsigned long long)length_kib, size_mult, max_mult); + return -EINVAL; + } + + /* ENH_START_ADDR is in 512-byte sectors */ + start_sectors = start_kib * 2; + + ret = mci_switch(mci, EXT_CSD_ERASE_GROUP_DEF, 1); + if (ret) { + printf("Failure to write EXT_CSD_ERASE_GROUP_DEF\n"); + return ret; + } + + for (i = 0; i < 4; i++) { + ret = mci_switch(mci, EXT_CSD_ENH_START_ADDR + i, + (start_sectors >> (8 * i)) & 0xff); + if (ret) { + printf("Failure to write EXT_CSD_ENH_START_ADDR[%d]\n", i); + return ret; + } + } + + for (i = 0; i < 3; i++) { + ret = mci_switch(mci, EXT_CSD_ENH_SIZE_MULT + i, + (size_mult >> (8 * i)) & 0xff); + if (ret) { + printf("Failure to write EXT_CSD_ENH_SIZE_MULT[%d]\n", i); + return ret; + } + } + + ret = mci_switch(mci, EXT_CSD_PARTITIONS_ATTRIBUTE, + ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | + EXT_CSD_ENH_USR_MASK); + if (ret) { + printf("Failure to write EXT_CSD_PARTITIONS_ATTRIBUTE\n"); + return ret; + } + + printf("Enhanced user data area: start=%llu KiB, size=%llu KiB " + "(mult=%u, unit=%llu KiB, max_mult=%u)\n", + (unsigned long long)start_kib, + (unsigned long long)size_mult * unit_kib, + size_mult, (unsigned long long)unit_kib, max_mult); + + return 0; +} + static int mmc_partitioning_complete(struct mci *mci) { int ret; @@ -67,16 +154,32 @@ static int mmc_partitioning_complete(struct mci *mci) return ret; } -/* enh_area [-c] /dev/mmcX */ +/* + * enh_area [-c] /dev/mmcX + * -> fill the entire user area (legacy) + * enh_area set [-c] <start_KiB> <length_KiB> /dev/mmcX + * -> sized, mmc-utils compatible + */ static int do_mmc_enh_area(int argc, char *argv[]) { const char *devpath; struct mci *mci; u8 *ext_csd; + u64 start_kib = 0, length_kib = 0; + bool sized = false; int set_completed = 0; int opt; int ret; + if (argc >= 2 && !strcmp(argv[1], "set")) { + sized = true; + /* Shift past "set" so getopt() and the trailing positional + * arguments line up the same way as for the legacy form. */ + argv++; + argc--; + } + + optind = 1; while ((opt = getopt(argc, argv, "c")) > 0) { switch (opt) { case 'c': @@ -86,13 +189,25 @@ static int do_mmc_enh_area(int argc, char *argv[]) } } - if (argc - optind != 1) { - printf("Usage: mmc enh_area [-c] /dev/mmcX\n"); - return COMMAND_ERROR_USAGE; + if (sized) { + if (argc - optind != 3) { + printf("Usage: mmc enh_area set [-c] <start_KiB> <length_KiB> /dev/mmcX\n"); + return COMMAND_ERROR_USAGE; + } + if (kstrtou64(argv[optind], 0, &start_kib) || + kstrtou64(argv[optind + 1], 0, &length_kib)) { + printf("Invalid start/length value\n"); + return COMMAND_ERROR_USAGE; + } + devpath = argv[optind + 2]; + } else { + if (argc - optind != 1) { + printf("Usage: mmc enh_area [-c] /dev/mmcX\n"); + return COMMAND_ERROR_USAGE; + } + devpath = argv[optind]; } - devpath = argv[optind]; - mci = mci_get_device_by_devpath(devpath); if (!mci) { printf("Failure to open %s as mci device\n", devpath); @@ -113,7 +228,10 @@ static int do_mmc_enh_area(int argc, char *argv[]) goto error; } - ret = mmc_enh_area_setmax(mci, ext_csd); + if (sized) + ret = mmc_enh_area_set(mci, ext_csd, start_kib, length_kib); + else + ret = mmc_enh_area_setmax(mci, ext_csd); if (ret) goto error; @@ -273,8 +391,9 @@ static int do_mmc(int argc, char *argv[]) BAREBOX_CMD_HELP_START(mmc) BAREBOX_CMD_HELP_TEXT("Modifies mmc properties.") BAREBOX_CMD_HELP_TEXT("") -BAREBOX_CMD_HELP_TEXT("The subcommand enh_area creates an enhanced area of") -BAREBOX_CMD_HELP_TEXT("maximal size.") +BAREBOX_CMD_HELP_TEXT("Subcommand enh_area without arguments creates an enhanced") +BAREBOX_CMD_HELP_TEXT("area of maximal size; enh_area set provisions a region of the") +BAREBOX_CMD_HELP_TEXT("requested length (rounded up to the device's enhanced-area unit).") BAREBOX_CMD_HELP_TEXT("Note, with -c this is an irreversible action.") BAREBOX_CMD_HELP_OPT("-c", "complete partitioning (deprecated)") BAREBOX_CMD_HELP_TEXT("") @@ -285,7 +404,7 @@ BAREBOX_CMD_HELP_END BAREBOX_CMD_START(mmc) .cmd = do_mmc, - BAREBOX_CMD_OPTS("partition_complete|write_reliability|enh_area [-c] /dev/mmcX") + BAREBOX_CMD_OPTS("partition_complete|write_reliability|enh_area [set [-c] <start_KiB> <length_KiB>|[-c]] /dev/mmcX") BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP) BAREBOX_CMD_HELP(cmd_mmc_help) BAREBOX_CMD_END base-commit: c92603e91031b09f28bff7b69f29fea89ee544b9 -- 2.43.0
