1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2019 Eugeniu Rosca <rosca.eugeniu@gmail.com>
5 * Command to read/modify/write Android BCB fields
8 #include <android_bootloader_message.h>
11 #include <android_ab.h>
12 #include <display_options.h>
18 #include <linux/err.h>
20 static const char * const fields
[] = {
27 static struct bootloader_message bcb
__aligned(ARCH_DMA_MINALIGN
) = { { 0 } };
28 static struct disk_partition partition_data
;
30 static struct blk_desc
*block
;
31 static struct disk_partition
*partition
= &partition_data
;
33 static int bcb_not_loaded(void)
35 printf("Error: Please, load BCB first!\n");
39 static int bcb_field_get(const char *name
, char **fieldp
, int *sizep
)
41 if (!strcmp(name
, "command")) {
42 *fieldp
= bcb
.command
;
43 *sizep
= sizeof(bcb
.command
);
44 } else if (!strcmp(name
, "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")) {
52 *sizep
= sizeof(bcb
.stage
);
53 } else if (!strcmp(name
, "reserved")) {
54 *fieldp
= bcb
.reserved
;
55 *sizep
= sizeof(bcb
.reserved
);
57 printf("Error: Unknown bcb field '%s'\n", name
);
64 static void __bcb_reset(void)
67 partition
= &partition_data
;
68 memset(&partition_data
, 0, sizeof(struct disk_partition
));
69 memset(&bcb
, 0, sizeof(struct bootloader_message
));
72 static int __bcb_initialize(const char *iface
, int devnum
, const char *partp
)
77 block
= blk_get_dev(iface
, devnum
);
84 * always select the first hwpart in case another
85 * blk operation selected a different hwpart
87 ret
= blk_dselect_hwpart(block
, 0);
88 if (IS_ERR_VALUE(ret
)) {
93 part
= simple_strtoul(partp
, &endp
, 0);
95 ret
= part_get_info(block
, part
, partition
);
99 part
= part_get_info_by_name(block
, partp
, partition
);
106 return CMD_RET_SUCCESS
;
109 printf("Error: %s %d:%s read failed (%d)\n", iface
, devnum
,
110 partition
->name
, ret
);
112 return CMD_RET_FAILURE
;
115 static int __bcb_load(void)
120 cnt
= DIV_ROUND_UP(sizeof(struct bootloader_message
), partition
->blksz
);
121 if (cnt
> partition
->size
)
124 if (blk_dread(block
, partition
->start
, cnt
, &bcb
) != cnt
) {
129 debug("%s: Loaded from %d %d:%s\n", __func__
, block
->uclass_id
,
130 block
->devnum
, partition
->name
);
132 return CMD_RET_SUCCESS
;
134 printf("Error: %d %d:%s read failed (%d)\n", block
->uclass_id
,
135 block
->devnum
, partition
->name
, ret
);
138 printf("Error: %d %d:%s too small!", block
->uclass_id
,
139 block
->devnum
, partition
->name
);
142 return CMD_RET_FAILURE
;
145 static int do_bcb_load(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
154 return CMD_RET_USAGE
;
162 devnum
= simple_strtoul(argv
[1], &endp
, 0);
164 printf("Error: Device id '%s' not a number\n", argv
[1]);
165 return CMD_RET_FAILURE
;
168 ret
= __bcb_initialize(iface
, devnum
, argv
[2]);
169 if (ret
!= CMD_RET_SUCCESS
)
175 static int __bcb_set(const char *fieldp
, const char *valp
)
178 char *field
, *str
, *found
, *tmp
;
180 if (bcb_field_get(fieldp
, &field
, &size
))
181 return CMD_RET_FAILURE
;
185 printf("Error: sizeof('%s') = %d >= %d = sizeof(bcb.%s)\n",
186 valp
, len
, size
, fieldp
);
187 return CMD_RET_FAILURE
;
191 printf("Error: Out of memory while strdup\n");
192 return CMD_RET_FAILURE
;
197 while ((found
= strsep(&tmp
, ":"))) {
198 if (field
[0] != '\0')
200 strcat(field
, found
);
204 return CMD_RET_SUCCESS
;
207 static int do_bcb_set(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
211 return CMD_RET_USAGE
;
214 return bcb_not_loaded();
216 return __bcb_set(argv
[1], argv
[2]);
219 static int do_bcb_clear(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
226 return bcb_not_loaded();
229 memset(&bcb
, 0, sizeof(bcb
));
230 return CMD_RET_SUCCESS
;
233 if (bcb_field_get(argv
[1], &field
, &size
))
234 return CMD_RET_FAILURE
;
236 memset(field
, 0, size
);
238 return CMD_RET_SUCCESS
;
241 static int do_bcb_test(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
249 return CMD_RET_USAGE
;
252 return bcb_not_loaded();
256 if (bcb_field_get(argv
[1], &field
, &size
))
257 return CMD_RET_FAILURE
;
259 if (*op
== '=' && *(op
+ 1) == '\0') {
260 if (!strncmp(argv
[3], field
, size
))
261 return CMD_RET_SUCCESS
;
263 return CMD_RET_FAILURE
;
264 } else if (*op
== '~' && *(op
+ 1) == '\0') {
265 if (!strstr(field
, argv
[3]))
266 return CMD_RET_FAILURE
;
268 return CMD_RET_SUCCESS
;
270 printf("Error: Unknown operator '%s'\n", op
);
273 return CMD_RET_FAILURE
;
276 static int do_bcb_dump(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
283 return CMD_RET_USAGE
;
286 return bcb_not_loaded();
288 if (bcb_field_get(argv
[1], &field
, &size
))
289 return CMD_RET_FAILURE
;
291 print_buffer((ulong
)field
- (ulong
)&bcb
, (void *)field
, 1, size
, 16);
293 return CMD_RET_SUCCESS
;
296 static int __bcb_store(void)
301 cnt
= DIV_ROUND_UP(sizeof(struct bootloader_message
), partition
->blksz
);
303 if (blk_dwrite(block
, partition
->start
, cnt
, &bcb
) != cnt
) {
308 return CMD_RET_SUCCESS
;
310 printf("Error: %d %d:%s write failed (%d)\n", block
->uclass_id
,
311 block
->devnum
, partition
->name
, ret
);
313 return CMD_RET_FAILURE
;
316 static int do_bcb_store(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
320 return bcb_not_loaded();
322 return __bcb_store();
325 int bcb_find_partition_and_load(const char *iface
, int devnum
, char *partp
)
331 ret
= __bcb_initialize(iface
, devnum
, partp
);
332 if (ret
!= CMD_RET_SUCCESS
)
338 int bcb_load(struct blk_desc
*block_description
, struct disk_partition
*disk_partition
)
342 block
= block_description
;
343 partition
= disk_partition
;
348 int bcb_set(enum bcb_field field
, const char *value
)
350 if (field
> BCB_FIELD_STAGE
)
351 return CMD_RET_FAILURE
;
352 return __bcb_set(fields
[field
], value
);
355 int bcb_get(enum bcb_field field
, char *value_out
, size_t value_size
)
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
;
365 strlcpy(value_out
, field_value
, value_size
);
367 return CMD_RET_SUCCESS
;
372 return __bcb_store();
380 __maybe_unused
static int do_bcb_ab_select(struct cmd_tbl
*cmdtp
,
385 struct blk_desc
*dev_desc
;
386 struct disk_partition part_info
;
388 bool dec_tries
= true;
391 return CMD_RET_USAGE
;
393 for (int i
= 4; i
< argc
; i
++) {
394 if (!strcmp(argv
[i
], "--no-dec"))
397 return CMD_RET_USAGE
;
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
,
404 return CMD_RET_FAILURE
;
407 ret
= ab_select_slot(dev_desc
, &part_info
, dec_tries
);
409 printf("Android boot failed, error %d.\n", ret
);
410 return CMD_RET_FAILURE
;
413 /* Android standard slot names are 'a', 'b', ... */
414 slot
[0] = BOOT_SLOT_NAME(ret
);
416 env_set(argv
[1], slot
);
417 printf("ANDROID: Booting slot: %s\n", slot
);
419 return CMD_RET_SUCCESS
;
422 __maybe_unused
static int do_bcb_ab_dump(struct cmd_tbl
*cmdtp
,
427 struct blk_desc
*dev_desc
;
428 struct disk_partition part_info
;
431 return CMD_RET_USAGE
;
433 if (part_get_info_by_dev_and_name_or_num(argv
[1], argv
[2],
434 &dev_desc
, &part_info
,
436 return CMD_RET_FAILURE
;
439 ret
= ab_dump_abc(dev_desc
, &part_info
);
441 printf("Cannot dump ABC data, error %d.\n", ret
);
442 return CMD_RET_FAILURE
;
445 return CMD_RET_SUCCESS
;
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"
457 #if IS_ENABLED(CONFIG_ANDROID_AB)
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"
474 " Dump boot_control information from specific partition.\n"
475 " <interface> <dev[:part|#part_name]>\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"
491 U_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
),