bcb.c 8.6 KB

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