"Das U-Boot" Source Tree
at master 503 lines 12 kB view raw
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 <android_ab.h> 12#include <display_options.h> 13#include <log.h> 14#include <part.h> 15#include <malloc.h> 16#include <memalign.h> 17#include <vsprintf.h> 18#include <linux/err.h> 19 20static const char * const fields[] = { 21 "command", 22 "status", 23 "recovery", 24 "stage" 25}; 26 27static struct bootloader_message bcb __aligned(ARCH_DMA_MINALIGN) = { { 0 } }; 28static struct disk_partition partition_data; 29 30static struct blk_desc *block; 31static struct disk_partition *partition = &partition_data; 32 33static int bcb_not_loaded(void) 34{ 35 printf("Error: Please, load BCB first!\n"); 36 return -1; 37} 38 39static int bcb_field_get(const char *name, char **fieldp, int *sizep) 40{ 41 if (!strcmp(name, "command")) { 42 *fieldp = bcb.command; 43 *sizep = sizeof(bcb.command); 44 } else if (!strcmp(name, "status")) { 45 *fieldp = bcb.status; 46 *sizep = sizeof(bcb.status); 47 } else if (!strcmp(name, "recovery")) { 48 *fieldp = bcb.recovery; 49 *sizep = sizeof(bcb.recovery); 50 } else if (!strcmp(name, "stage")) { 51 *fieldp = bcb.stage; 52 *sizep = sizeof(bcb.stage); 53 } else if (!strcmp(name, "reserved")) { 54 *fieldp = bcb.reserved; 55 *sizep = sizeof(bcb.reserved); 56 } else { 57 printf("Error: Unknown bcb field '%s'\n", name); 58 return -1; 59 } 60 61 return 0; 62} 63 64static void __bcb_reset(void) 65{ 66 block = NULL; 67 partition = &partition_data; 68 memset(&partition_data, 0, sizeof(struct disk_partition)); 69 memset(&bcb, 0, sizeof(struct bootloader_message)); 70} 71 72static int __bcb_initialize(const char *iface, int devnum, const char *partp) 73{ 74 char *endp; 75 int part, ret; 76 77 block = blk_get_dev(iface, devnum); 78 if (!block) { 79 ret = -ENODEV; 80 goto err_read_fail; 81 } 82 83 /* 84 * always select the first hwpart in case another 85 * blk operation selected a different hwpart 86 */ 87 ret = blk_dselect_hwpart(block, 0); 88 if (IS_ERR_VALUE(ret)) { 89 ret = -ENODEV; 90 goto err_read_fail; 91 } 92 93 part = simple_strtoul(partp, &endp, 0); 94 if (*endp == '\0') { 95 ret = part_get_info(block, part, partition); 96 if (ret) 97 goto err_read_fail; 98 } else { 99 part = part_get_info_by_name(block, partp, partition); 100 if (part < 0) { 101 ret = part; 102 goto err_read_fail; 103 } 104 } 105 106 return CMD_RET_SUCCESS; 107 108err_read_fail: 109 printf("Error: %s %d:%s read failed (%d)\n", iface, devnum, 110 partition->name, ret); 111 __bcb_reset(); 112 return CMD_RET_FAILURE; 113} 114 115static int __bcb_load(void) 116{ 117 u64 cnt; 118 int ret; 119 120 cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), partition->blksz); 121 if (cnt > partition->size) 122 goto err_too_small; 123 124 if (blk_dread(block, partition->start, cnt, &bcb) != cnt) { 125 ret = -EIO; 126 goto err_read_fail; 127 } 128 129 debug("%s: Loaded from %d %d:%s\n", __func__, block->uclass_id, 130 block->devnum, partition->name); 131 132 return CMD_RET_SUCCESS; 133err_read_fail: 134 printf("Error: %d %d:%s read failed (%d)\n", block->uclass_id, 135 block->devnum, partition->name, ret); 136 goto err; 137err_too_small: 138 printf("Error: %d %d:%s too small!", block->uclass_id, 139 block->devnum, partition->name); 140err: 141 __bcb_reset(); 142 return CMD_RET_FAILURE; 143} 144 145static int do_bcb_load(struct cmd_tbl *cmdtp, int flag, int argc, 146 char * const argv[]) 147{ 148 int ret; 149 int devnum; 150 char *endp; 151 char *iface = "mmc"; 152 153 if (argc < 3) 154 return CMD_RET_USAGE; 155 156 if (argc == 4) { 157 iface = argv[1]; 158 argc--; 159 argv++; 160 } 161 162 devnum = simple_strtoul(argv[1], &endp, 0); 163 if (*endp != '\0') { 164 printf("Error: Device id '%s' not a number\n", argv[1]); 165 return CMD_RET_FAILURE; 166 } 167 168 ret = __bcb_initialize(iface, devnum, argv[2]); 169 if (ret != CMD_RET_SUCCESS) 170 return ret; 171 172 return __bcb_load(); 173} 174 175static int __bcb_set(const char *fieldp, const char *valp) 176{ 177 int size, len; 178 char *field, *str, *found, *tmp; 179 180 if (bcb_field_get(fieldp, &field, &size)) 181 return CMD_RET_FAILURE; 182 183 len = strlen(valp); 184 if (len >= size) { 185 printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n", 186 valp, len, size, fieldp); 187 return CMD_RET_FAILURE; 188 } 189 str = strdup(valp); 190 if (!str) { 191 printf("Error: Out of memory while strdup\n"); 192 return CMD_RET_FAILURE; 193 } 194 195 tmp = str; 196 field[0] = '\0'; 197 while ((found = strsep(&tmp, ":"))) { 198 if (field[0] != '\0') 199 strcat(field, "\n"); 200 strcat(field, found); 201 } 202 free(str); 203 204 return CMD_RET_SUCCESS; 205} 206 207static int do_bcb_set(struct cmd_tbl *cmdtp, int flag, int argc, 208 char * const argv[]) 209{ 210 if (argc < 3) 211 return CMD_RET_USAGE; 212 213 if (!block) 214 return bcb_not_loaded(); 215 216 return __bcb_set(argv[1], argv[2]); 217} 218 219static int do_bcb_clear(struct cmd_tbl *cmdtp, int flag, int argc, 220 char *const argv[]) 221{ 222 int size; 223 char *field; 224 225 if (!block) 226 return bcb_not_loaded(); 227 228 if (argc == 1) { 229 memset(&bcb, 0, sizeof(bcb)); 230 return CMD_RET_SUCCESS; 231 } 232 233 if (bcb_field_get(argv[1], &field, &size)) 234 return CMD_RET_FAILURE; 235 236 memset(field, 0, size); 237 238 return CMD_RET_SUCCESS; 239} 240 241static int do_bcb_test(struct cmd_tbl *cmdtp, int flag, int argc, 242 char *const argv[]) 243{ 244 int size; 245 char *field; 246 char *op; 247 248 if (argc < 4) 249 return CMD_RET_USAGE; 250 251 if (!block) 252 return bcb_not_loaded(); 253 254 op = argv[2]; 255 256 if (bcb_field_get(argv[1], &field, &size)) 257 return CMD_RET_FAILURE; 258 259 if (*op == '=' && *(op + 1) == '\0') { 260 if (!strncmp(argv[3], field, size)) 261 return CMD_RET_SUCCESS; 262 else 263 return CMD_RET_FAILURE; 264 } else if (*op == '~' && *(op + 1) == '\0') { 265 if (!strstr(field, argv[3])) 266 return CMD_RET_FAILURE; 267 else 268 return CMD_RET_SUCCESS; 269 } else { 270 printf("Error: Unknown operator '%s'\n", op); 271 } 272 273 return CMD_RET_FAILURE; 274} 275 276static int do_bcb_dump(struct cmd_tbl *cmdtp, int flag, int argc, 277 char *const argv[]) 278{ 279 int size; 280 char *field; 281 282 if (argc < 2) 283 return CMD_RET_USAGE; 284 285 if (!block) 286 return bcb_not_loaded(); 287 288 if (bcb_field_get(argv[1], &field, &size)) 289 return CMD_RET_FAILURE; 290 291 print_buffer((ulong)field - (ulong)&bcb, (void *)field, 1, size, 16); 292 293 return CMD_RET_SUCCESS; 294} 295 296static int __bcb_store(void) 297{ 298 u64 cnt; 299 int ret; 300 301 cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), partition->blksz); 302 303 if (blk_dwrite(block, partition->start, cnt, &bcb) != cnt) { 304 ret = -EIO; 305 goto err; 306 } 307 308 return CMD_RET_SUCCESS; 309err: 310 printf("Error: %d %d:%s write failed (%d)\n", block->uclass_id, 311 block->devnum, partition->name, ret); 312 313 return CMD_RET_FAILURE; 314} 315 316static int do_bcb_store(struct cmd_tbl *cmdtp, int flag, int argc, 317 char * const argv[]) 318{ 319 if (!block) 320 return bcb_not_loaded(); 321 322 return __bcb_store(); 323} 324 325int bcb_find_partition_and_load(const char *iface, int devnum, char *partp) 326{ 327 int ret; 328 329 __bcb_reset(); 330 331 ret = __bcb_initialize(iface, devnum, partp); 332 if (ret != CMD_RET_SUCCESS) 333 return ret; 334 335 return __bcb_load(); 336} 337 338int bcb_load(struct blk_desc *block_description, struct disk_partition *disk_partition) 339{ 340 __bcb_reset(); 341 342 block = block_description; 343 partition = disk_partition; 344 345 return __bcb_load(); 346} 347 348int bcb_set(enum bcb_field field, const char *value) 349{ 350 if (field > BCB_FIELD_STAGE) 351 return CMD_RET_FAILURE; 352 return __bcb_set(fields[field], value); 353} 354 355int bcb_get(enum bcb_field field, char *value_out, size_t value_size) 356{ 357 int size; 358 char *field_value; 359 360 if (field > BCB_FIELD_STAGE) 361 return CMD_RET_FAILURE; 362 if (bcb_field_get(fields[field], &field_value, &size)) 363 return CMD_RET_FAILURE; 364 365 strlcpy(value_out, field_value, value_size); 366 367 return CMD_RET_SUCCESS; 368} 369 370int bcb_store(void) 371{ 372 return __bcb_store(); 373} 374 375void bcb_reset(void) 376{ 377 __bcb_reset(); 378} 379 380__maybe_unused static int do_bcb_ab_select(struct cmd_tbl *cmdtp, 381 int flag, int argc, 382 char * const argv[]) 383{ 384 int ret; 385 struct blk_desc *dev_desc; 386 struct disk_partition part_info; 387 char slot[2]; 388 bool dec_tries = true; 389 390 if (argc < 4) 391 return CMD_RET_USAGE; 392 393 for (int i = 4; i < argc; i++) { 394 if (!strcmp(argv[i], "--no-dec")) 395 dec_tries = false; 396 else 397 return CMD_RET_USAGE; 398 } 399 400 /* Lookup the "misc" partition from argv[2] and argv[3] */ 401 if (part_get_info_by_dev_and_name_or_num(argv[2], argv[3], 402 &dev_desc, &part_info, 403 false) < 0) { 404 return CMD_RET_FAILURE; 405 } 406 407 ret = ab_select_slot(dev_desc, &part_info, dec_tries); 408 if (ret < 0) { 409 printf("Android boot failed, error %d.\n", ret); 410 return CMD_RET_FAILURE; 411 } 412 413 /* Android standard slot names are 'a', 'b', ... */ 414 slot[0] = BOOT_SLOT_NAME(ret); 415 slot[1] = '\0'; 416 env_set(argv[1], slot); 417 printf("ANDROID: Booting slot: %s\n", slot); 418 419 return CMD_RET_SUCCESS; 420} 421 422__maybe_unused static int do_bcb_ab_dump(struct cmd_tbl *cmdtp, 423 int flag, int argc, 424 char *const argv[]) 425{ 426 int ret; 427 struct blk_desc *dev_desc; 428 struct disk_partition part_info; 429 430 if (argc < 3) 431 return CMD_RET_USAGE; 432 433 if (part_get_info_by_dev_and_name_or_num(argv[1], argv[2], 434 &dev_desc, &part_info, 435 false) < 0) { 436 return CMD_RET_FAILURE; 437 } 438 439 ret = ab_dump_abc(dev_desc, &part_info); 440 if (ret < 0) { 441 printf("Cannot dump ABC data, error %d.\n", ret); 442 return CMD_RET_FAILURE; 443 } 444 445 return CMD_RET_SUCCESS; 446} 447 448U_BOOT_LONGHELP(bcb, 449 "load <interface> <dev> <part> - load BCB from <interface> <dev>:<part>\n" 450 "load <dev> <part> - load BCB from mmc <dev>:<part>\n" 451 "bcb set <field> <val> - set BCB <field> to <val>\n" 452 "bcb clear [<field>] - clear BCB <field> or all fields\n" 453 "bcb test <field> <op> <val> - test BCB <field> against <val>\n" 454 "bcb dump <field> - dump BCB <field>\n" 455 "bcb store - store BCB back to <interface>\n" 456 "\n" 457#if IS_ENABLED(CONFIG_ANDROID_AB) 458 "bcb ab_select -\n" 459 " Select the slot used to boot from and register the boot attempt.\n" 460 " <slot_var_name> <interface> <dev[:part|#part_name]> [--no-dec]\n" 461 " - Load the slot metadata from the partition 'part' on\n" 462 " device type 'interface' instance 'dev' and store the active\n" 463 " slot in the 'slot_var_name' variable. This also updates the\n" 464 " Android slot metadata with a boot attempt, which can cause\n" 465 " successive calls to this function to return a different result\n" 466 " if the returned slot runs out of boot attempts.\n" 467 " - If 'part_name' is passed, preceded with a # instead of :, the\n" 468 " partition name whose label is 'part_name' will be looked up in\n" 469 " the partition table. This is commonly the \"misc\" partition.\n" 470 " - If '--no-dec' is set, the number of tries remaining will not\n" 471 " decremented for the selected boot slot\n" 472 "\n" 473 "bcb ab_dump -\n" 474 " Dump boot_control information from specific partition.\n" 475 " <interface> <dev[:part|#part_name]>\n" 476 "\n" 477#endif 478 "Legend:\n" 479 "<interface> - storage device interface (virtio, mmc, etc)\n" 480 "<dev> - storage device index containing the BCB partition\n" 481 "<part> - partition index or name containing the BCB\n" 482 "<field> - one of {command,status,recovery,stage,reserved}\n" 483 "<op> - the binary operator used in 'bcb test':\n" 484 " '=' returns true if <val> matches the string stored in <field>\n" 485 " '~' returns true if <val> matches a subset of <field>'s string\n" 486 "<val> - string/text provided as input to bcb {set,test}\n" 487 " NOTE: any ':' character in <val> will be replaced by line feed\n" 488 " during 'bcb set' and used as separator by upper layers\n" 489); 490 491U_BOOT_CMD_WITH_SUBCMDS(bcb, 492 "Load/set/clear/test/dump/store Android BCB fields", bcb_help_text, 493 U_BOOT_SUBCMD_MKENT(load, 4, 1, do_bcb_load), 494 U_BOOT_SUBCMD_MKENT(set, 3, 1, do_bcb_set), 495 U_BOOT_SUBCMD_MKENT(clear, 2, 1, do_bcb_clear), 496 U_BOOT_SUBCMD_MKENT(test, 4, 1, do_bcb_test), 497 U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_bcb_dump), 498 U_BOOT_SUBCMD_MKENT(store, 1, 1, do_bcb_store), 499#if IS_ENABLED(CONFIG_ANDROID_AB) 500 U_BOOT_SUBCMD_MKENT(ab_select, 5, 1, do_bcb_ab_select), 501 U_BOOT_SUBCMD_MKENT(ab_dump, 3, 1, do_bcb_ab_dump), 502#endif 503);