uboot/cmd/bcb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2019 Eugeniu Rosca <rosca.eugeniu@gmail.com>
   4 *
   5 * Command to read/modify/write Android BCB fields
   6 */
   7
   8#include <android_bootloader_message.h>
   9#include <bcb.h>
  10#include <command.h>
  11#include <common.h>
  12#include <log.h>
  13#include <part.h>
  14#include <malloc.h>
  15
  16enum bcb_cmd {
  17        BCB_CMD_LOAD,
  18        BCB_CMD_FIELD_SET,
  19        BCB_CMD_FIELD_CLEAR,
  20        BCB_CMD_FIELD_TEST,
  21        BCB_CMD_FIELD_DUMP,
  22        BCB_CMD_STORE,
  23};
  24
  25static int bcb_dev = -1;
  26static int bcb_part = -1;
  27static struct bootloader_message bcb = { { 0 } };
  28
  29static int bcb_cmd_get(char *cmd)
  30{
  31        if (!strcmp(cmd, "load"))
  32                return BCB_CMD_LOAD;
  33        if (!strcmp(cmd, "set"))
  34                return BCB_CMD_FIELD_SET;
  35        if (!strcmp(cmd, "clear"))
  36                return BCB_CMD_FIELD_CLEAR;
  37        if (!strcmp(cmd, "test"))
  38                return BCB_CMD_FIELD_TEST;
  39        if (!strcmp(cmd, "store"))
  40                return BCB_CMD_STORE;
  41        if (!strcmp(cmd, "dump"))
  42                return BCB_CMD_FIELD_DUMP;
  43        else
  44                return -1;
  45}
  46
  47static int bcb_is_misused(int argc, char *const argv[])
  48{
  49        int cmd = bcb_cmd_get(argv[0]);
  50
  51        switch (cmd) {
  52        case BCB_CMD_LOAD:
  53        case BCB_CMD_FIELD_SET:
  54                if (argc != 3)
  55                        goto err;
  56                break;
  57        case BCB_CMD_FIELD_TEST:
  58                if (argc != 4)
  59                        goto err;
  60                break;
  61        case BCB_CMD_FIELD_CLEAR:
  62                if (argc != 1 && argc != 2)
  63                        goto err;
  64                break;
  65        case BCB_CMD_STORE:
  66                if (argc != 1)
  67                        goto err;
  68                break;
  69        case BCB_CMD_FIELD_DUMP:
  70                if (argc != 2)
  71                        goto err;
  72                break;
  73        default:
  74                printf("Error: 'bcb %s' not supported\n", argv[0]);
  75                return -1;
  76        }
  77
  78        if (cmd != BCB_CMD_LOAD && (bcb_dev < 0 || bcb_part < 0)) {
  79                printf("Error: Please, load BCB first!\n");
  80                return -1;
  81        }
  82
  83        return 0;
  84err:
  85        printf("Error: Bad usage of 'bcb %s'\n", argv[0]);
  86
  87        return -1;
  88}
  89
  90static int bcb_field_get(char *name, char **fieldp, int *sizep)
  91{
  92        if (!strcmp(name, "command")) {
  93                *fieldp = bcb.command;
  94                *sizep = sizeof(bcb.command);
  95        } else if (!strcmp(name, "status")) {
  96                *fieldp = bcb.status;
  97                *sizep = sizeof(bcb.status);
  98        } else if (!strcmp(name, "recovery")) {
  99                *fieldp = bcb.recovery;
 100                *sizep = sizeof(bcb.recovery);
 101        } else if (!strcmp(name, "stage")) {
 102                *fieldp = bcb.stage;
 103                *sizep = sizeof(bcb.stage);
 104        } else if (!strcmp(name, "reserved")) {
 105                *fieldp = bcb.reserved;
 106                *sizep = sizeof(bcb.reserved);
 107        } else {
 108                printf("Error: Unknown bcb field '%s'\n", name);
 109                return -1;
 110        }
 111
 112        return 0;
 113}
 114
 115static int __bcb_load(int devnum, const char *partp)
 116{
 117        struct blk_desc *desc;
 118        struct disk_partition info;
 119        u64 cnt;
 120        char *endp;
 121        int part, ret;
 122
 123        desc = blk_get_devnum_by_type(IF_TYPE_MMC, devnum);
 124        if (!desc) {
 125                ret = -ENODEV;
 126                goto err_read_fail;
 127        }
 128
 129        part = simple_strtoul(partp, &endp, 0);
 130        if (*endp == '\0') {
 131                ret = part_get_info(desc, part, &info);
 132                if (ret)
 133                        goto err_read_fail;
 134        } else {
 135                part = part_get_info_by_name(desc, partp, &info);
 136                if (part < 0) {
 137                        ret = part;
 138                        goto err_read_fail;
 139                }
 140        }
 141
 142        cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz);
 143        if (cnt > info.size)
 144                goto err_too_small;
 145
 146        if (blk_dread(desc, info.start, cnt, &bcb) != cnt) {
 147                ret = -EIO;
 148                goto err_read_fail;
 149        }
 150
 151        bcb_dev = desc->devnum;
 152        bcb_part = part;
 153        debug("%s: Loaded from mmc %d:%d\n", __func__, bcb_dev, bcb_part);
 154
 155        return CMD_RET_SUCCESS;
 156err_read_fail:
 157        printf("Error: mmc %d:%s read failed (%d)\n", devnum, partp, ret);
 158        goto err;
 159err_too_small:
 160        printf("Error: mmc %d:%s too small!", devnum, partp);
 161        goto err;
 162err:
 163        bcb_dev = -1;
 164        bcb_part = -1;
 165
 166        return CMD_RET_FAILURE;
 167}
 168
 169static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc,
 170                       char * const argv[])
 171{
 172        char *endp;
 173        int devnum = simple_strtoul(argv[1], &endp, 0);
 174
 175        if (*endp != '\0') {
 176                printf("Error: Device id '%s' not a number\n", argv[1]);
 177                return CMD_RET_FAILURE;
 178        }
 179
 180        return __bcb_load(devnum, argv[2]);
 181}
 182
 183static int __bcb_set(char *fieldp, const char *valp)
 184{
 185        int size, len;
 186        char *field, *str, *found, *tmp;
 187
 188        if (bcb_field_get(fieldp, &field, &size))
 189                return CMD_RET_FAILURE;
 190
 191        len = strlen(valp);
 192        if (len >= size) {
 193                printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n",
 194                       valp, len, size, fieldp);
 195                return CMD_RET_FAILURE;
 196        }
 197        str = strdup(valp);
 198        if (!str) {
 199                printf("Error: Out of memory while strdup\n");
 200                return CMD_RET_FAILURE;
 201        }
 202
 203        tmp = str;
 204        field[0] = '\0';
 205        while ((found = strsep(&tmp, ":"))) {
 206                if (field[0] != '\0')
 207                        strcat(field, "\n");
 208                strcat(field, found);
 209        }
 210        free(str);
 211
 212        return CMD_RET_SUCCESS;
 213}
 214
 215static int do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc,
 216                      char * const argv[])
 217{
 218        return __bcb_set(argv[1], argv[2]);
 219}
 220
 221static int do_bcb_clear(struct cmd_tbl *cmdtp, int flag, int argc,
 222                        char *const argv[])
 223{
 224        int size;
 225        char *field;
 226
 227        if (argc == 1) {
 228                memset(&bcb, 0, sizeof(bcb));
 229                return CMD_RET_SUCCESS;
 230        }
 231
 232        if (bcb_field_get(argv[1], &field, &size))
 233                return CMD_RET_FAILURE;
 234
 235        memset(field, 0, size);
 236
 237        return CMD_RET_SUCCESS;
 238}
 239
 240static int do_bcb_test(struct cmd_tbl *cmdtp, int flag, int argc,
 241                       char *const argv[])
 242{
 243        int size;
 244        char *field;
 245        char *op = argv[2];
 246
 247        if (bcb_field_get(argv[1], &field, &size))
 248                return CMD_RET_FAILURE;
 249
 250        if (*op == '=' && *(op + 1) == '\0') {
 251                if (!strncmp(argv[3], field, size))
 252                        return CMD_RET_SUCCESS;
 253                else
 254                        return CMD_RET_FAILURE;
 255        } else if (*op == '~' && *(op + 1) == '\0') {
 256                if (!strstr(field, argv[3]))
 257                        return CMD_RET_FAILURE;
 258                else
 259                        return CMD_RET_SUCCESS;
 260        } else {
 261                printf("Error: Unknown operator '%s'\n", op);
 262        }
 263
 264        return CMD_RET_FAILURE;
 265}
 266
 267static int do_bcb_dump(struct cmd_tbl *cmdtp, int flag, int argc,
 268                       char *const argv[])
 269{
 270        int size;
 271        char *field;
 272
 273        if (bcb_field_get(argv[1], &field, &size))
 274                return CMD_RET_FAILURE;
 275
 276        print_buffer((ulong)field - (ulong)&bcb, (void *)field, 1, size, 16);
 277
 278        return CMD_RET_SUCCESS;
 279}
 280
 281static int __bcb_store(void)
 282{
 283        struct blk_desc *desc;
 284        struct disk_partition info;
 285        u64 cnt;
 286        int ret;
 287
 288        desc = blk_get_devnum_by_type(IF_TYPE_MMC, bcb_dev);
 289        if (!desc) {
 290                ret = -ENODEV;
 291                goto err;
 292        }
 293
 294        ret = part_get_info(desc, bcb_part, &info);
 295        if (ret)
 296                goto err;
 297
 298        cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz);
 299
 300        if (blk_dwrite(desc, info.start, cnt, &bcb) != cnt) {
 301                ret = -EIO;
 302                goto err;
 303        }
 304
 305        return CMD_RET_SUCCESS;
 306err:
 307        printf("Error: mmc %d:%d write failed (%d)\n", bcb_dev, bcb_part, ret);
 308
 309        return CMD_RET_FAILURE;
 310}
 311
 312static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc,
 313                        char * const argv[])
 314{
 315        return __bcb_store();
 316}
 317
 318int bcb_write_reboot_reason(int devnum, char *partp, const char *reasonp)
 319{
 320        int ret;
 321
 322        ret = __bcb_load(devnum, partp);
 323        if (ret != CMD_RET_SUCCESS)
 324                return ret;
 325
 326        ret = __bcb_set("command", reasonp);
 327        if (ret != CMD_RET_SUCCESS)
 328                return ret;
 329
 330        ret = __bcb_store();
 331        if (ret != CMD_RET_SUCCESS)
 332                return ret;
 333
 334        return 0;
 335}
 336
 337static struct cmd_tbl cmd_bcb_sub[] = {
 338        U_BOOT_CMD_MKENT(load, CONFIG_SYS_MAXARGS, 1, do_bcb_load, "", ""),
 339        U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 1, do_bcb_set, "", ""),
 340        U_BOOT_CMD_MKENT(clear, CONFIG_SYS_MAXARGS, 1, do_bcb_clear, "", ""),
 341        U_BOOT_CMD_MKENT(test, CONFIG_SYS_MAXARGS, 1, do_bcb_test, "", ""),
 342        U_BOOT_CMD_MKENT(dump, CONFIG_SYS_MAXARGS, 1, do_bcb_dump, "", ""),
 343        U_BOOT_CMD_MKENT(store, CONFIG_SYS_MAXARGS, 1, do_bcb_store, "", ""),
 344};
 345
 346static int do_bcb(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 347{
 348        struct cmd_tbl *c;
 349
 350        if (argc < 2)
 351                return CMD_RET_USAGE;
 352
 353        argc--;
 354        argv++;
 355
 356        c = find_cmd_tbl(argv[0], cmd_bcb_sub, ARRAY_SIZE(cmd_bcb_sub));
 357        if (!c)
 358                return CMD_RET_USAGE;
 359
 360        if (bcb_is_misused(argc, argv)) {
 361                /*
 362                 * We try to improve the user experience by reporting the
 363                 * root-cause of misusage, so don't return CMD_RET_USAGE,
 364                 * since the latter prints out the full-blown help text
 365                 */
 366                return CMD_RET_FAILURE;
 367        }
 368
 369        return c->cmd(cmdtp, flag, argc, argv);
 370}
 371
 372U_BOOT_CMD(
 373        bcb, CONFIG_SYS_MAXARGS, 1, do_bcb,
 374        "Load/set/clear/test/dump/store Android BCB fields",
 375        "load  <dev> <part>       - load  BCB from mmc <dev>:<part>\n"
 376        "bcb set   <field> <val>      - set   BCB <field> to <val>\n"
 377        "bcb clear [<field>]          - clear BCB <field> or all fields\n"
 378        "bcb test  <field> <op> <val> - test  BCB <field> against <val>\n"
 379        "bcb dump  <field>            - dump  BCB <field>\n"
 380        "bcb store                    - store BCB back to mmc\n"
 381        "\n"
 382        "Legend:\n"
 383        "<dev>   - MMC device index containing the BCB partition\n"
 384        "<part>  - MMC partition index or name containing the BCB\n"
 385        "<field> - one of {command,status,recovery,stage,reserved}\n"
 386        "<op>    - the binary operator used in 'bcb test':\n"
 387        "          '=' returns true if <val> matches the string stored in <field>\n"
 388        "          '~' returns true if <val> matches a subset of <field>'s string\n"
 389        "<val>   - string/text provided as input to bcb {set,test}\n"
 390        "          NOTE: any ':' character in <val> will be replaced by line feed\n"
 391        "          during 'bcb set' and used as separator by upper layers\n"
 392);
 393