bcb.c 8.7 KB

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