pstore.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright © 2019 Collabora Ltd
  4. */
  5. #include <config.h>
  6. #include <command.h>
  7. #include <fdtdec.h>
  8. #include <fs.h>
  9. #include <log.h>
  10. #include <mapmem.h>
  11. #include <memalign.h>
  12. #include <part.h>
  13. #include <fdt_support.h>
  14. struct persistent_ram_buffer {
  15. u32 sig;
  16. u32 start;
  17. u32 size;
  18. u8 data[0];
  19. };
  20. #define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */
  21. #define RAMOOPS_KERNMSG_HDR "===="
  22. #define PSTORE_TYPE_DMESG 0
  23. #define PSTORE_TYPE_CONSOLE 2
  24. #define PSTORE_TYPE_FTRACE 3
  25. #define PSTORE_TYPE_PMSG 7
  26. #define PSTORE_TYPE_ALL 255
  27. static phys_addr_t pstore_addr = CONFIG_CMD_PSTORE_MEM_ADDR;
  28. static phys_size_t pstore_length = CONFIG_CMD_PSTORE_MEM_SIZE;
  29. static unsigned int pstore_record_size = CONFIG_CMD_PSTORE_RECORD_SIZE;
  30. static unsigned int pstore_console_size = CONFIG_CMD_PSTORE_CONSOLE_SIZE;
  31. static unsigned int pstore_ftrace_size = CONFIG_CMD_PSTORE_FTRACE_SIZE;
  32. static unsigned int pstore_pmsg_size = CONFIG_CMD_PSTORE_PMSG_SIZE;
  33. static unsigned int pstore_ecc_size = CONFIG_CMD_PSTORE_ECC_SIZE;
  34. static unsigned int buffer_size;
  35. /**
  36. * pstore_read_kmsg_hdr() - Check kernel header and get compression flag if
  37. * available.
  38. * @buffer: Kernel messages buffer.
  39. * @compressed: Returns TRUE if kernel buffer is compressed, else FALSE.
  40. *
  41. * Check if buffer starts with a kernel header of the form:
  42. * ====<secs>.<nsecs>[-<compression>]\n
  43. * If <compression> is equal to 'C' then the buffer is compressed, else iter
  44. * should be 'D'.
  45. *
  46. * Return: Length of kernel header.
  47. */
  48. static int pstore_read_kmsg_hdr(char *buffer, bool *compressed)
  49. {
  50. char *ptr = buffer;
  51. *compressed = false;
  52. if (strncmp(RAMOOPS_KERNMSG_HDR, ptr, strlen(RAMOOPS_KERNMSG_HDR)) != 0)
  53. return 0;
  54. ptr += strlen(RAMOOPS_KERNMSG_HDR);
  55. ptr = strchr(ptr, '\n');
  56. if (!ptr)
  57. return 0;
  58. if (ptr[-2] == '-' && ptr[-1] == 'C')
  59. *compressed = true;
  60. return ptr - buffer + 1;
  61. }
  62. /**
  63. * pstore_get_buffer() - Get unwrapped record buffer
  64. * @sig: Signature to check
  65. * @buffer: Buffer containing wrapped record
  66. * @size: wrapped record size
  67. * @dest: Buffer used to store unwrapped record
  68. *
  69. * The record starts with <signature><start><size> header.
  70. * The signature is 'DBGC' for all records except for Ftrace's record(s) wich
  71. * use LINUX_VERSION_CODE ^ 'DBGC'.
  72. * Use 0 for @sig to prevent checking signature.
  73. * Start and size are 4 bytes long.
  74. *
  75. * Return: record's length
  76. */
  77. static u32 pstore_get_buffer(u32 sig, phys_addr_t buffer, u32 size, char *dest)
  78. {
  79. struct persistent_ram_buffer *prb =
  80. (struct persistent_ram_buffer *)map_sysmem(buffer, size);
  81. u32 dest_size;
  82. if (sig == 0 || prb->sig == sig) {
  83. if (prb->size == 0) {
  84. log_debug("found existing empty buffer\n");
  85. return 0;
  86. }
  87. if (prb->size > size) {
  88. log_debug("found existing invalid buffer, size %u, start %u\n",
  89. prb->size, prb->start);
  90. return 0;
  91. }
  92. } else {
  93. log_debug("no valid data in buffer (sig = 0x%08x)\n", prb->sig);
  94. return 0;
  95. }
  96. log_debug("found existing buffer, size %u, start %u\n",
  97. prb->size, prb->start);
  98. memcpy(dest, &prb->data[prb->start], prb->size - prb->start);
  99. memcpy(dest + prb->size - prb->start, &prb->data[0], prb->start);
  100. dest_size = prb->size;
  101. unmap_sysmem(prb);
  102. return dest_size;
  103. }
  104. /**
  105. * pstore_init_buffer_size() - Init buffer size to largest record size
  106. *
  107. * Records, console, FTrace and user logs can use different buffer sizes.
  108. * This function allows to retrieve the biggest one.
  109. */
  110. static void pstore_init_buffer_size(void)
  111. {
  112. if (pstore_record_size > buffer_size)
  113. buffer_size = pstore_record_size;
  114. if (pstore_console_size > buffer_size)
  115. buffer_size = pstore_console_size;
  116. if (pstore_ftrace_size > buffer_size)
  117. buffer_size = pstore_ftrace_size;
  118. if (pstore_pmsg_size > buffer_size)
  119. buffer_size = pstore_pmsg_size;
  120. }
  121. /**
  122. * pstore_set() - Initialize PStore settings from command line arguments
  123. * @cmdtp: Command data struct pointer
  124. * @flag: Command flag
  125. * @argc: Command-line argument count
  126. * @argv: Array of command-line arguments
  127. *
  128. * Set pstore reserved memory info, starting at 'addr' for 'len' bytes.
  129. * Default length for records is 4K.
  130. * Mandatory arguments:
  131. * - addr: ramoops starting address
  132. * - len: ramoops total length
  133. * Optional arguments:
  134. * - record-size: size of one panic or oops record ('dump' type)
  135. * - console-size: size of the kernel logs record
  136. * - ftrace-size: size of the ftrace record(s), this can be a single record or
  137. * divided in parts based on number of CPUs
  138. * - pmsg-size: size of the user space logs record
  139. * - ecc-size: enables/disables ECC support and specifies ECC buffer size in
  140. * bytes (0 disables it, 1 is a special value, means 16 bytes ECC)
  141. *
  142. * Return: zero on success, CMD_RET_USAGE in case of misuse and negative
  143. * on error.
  144. */
  145. static int pstore_set(struct cmd_tbl *cmdtp, int flag, int argc,
  146. char * const argv[])
  147. {
  148. if (argc < 3)
  149. return CMD_RET_USAGE;
  150. /* Address is specified since argc > 2
  151. */
  152. pstore_addr = hextoul(argv[1], NULL);
  153. /* Length is specified since argc > 2
  154. */
  155. pstore_length = hextoul(argv[2], NULL);
  156. if (argc > 3)
  157. pstore_record_size = hextoul(argv[3], NULL);
  158. if (argc > 4)
  159. pstore_console_size = hextoul(argv[4], NULL);
  160. if (argc > 5)
  161. pstore_ftrace_size = hextoul(argv[5], NULL);
  162. if (argc > 6)
  163. pstore_pmsg_size = hextoul(argv[6], NULL);
  164. if (argc > 7)
  165. pstore_ecc_size = hextoul(argv[7], NULL);
  166. if (pstore_length < (pstore_record_size + pstore_console_size
  167. + pstore_ftrace_size + pstore_pmsg_size)) {
  168. printf("pstore <len> should be larger than the sum of all records sizes\n");
  169. pstore_length = 0;
  170. }
  171. log_debug("pstore set done: start 0x%08llx - length 0x%llx\n",
  172. (unsigned long long)pstore_addr,
  173. (unsigned long long)pstore_length);
  174. return 0;
  175. }
  176. /**
  177. * pstore_print_buffer() - Print buffer
  178. * @type: buffer type
  179. * @buffer: buffer to print
  180. * @size: buffer size
  181. *
  182. * Print buffer type and content
  183. */
  184. static void pstore_print_buffer(char *type, char *buffer, u32 size)
  185. {
  186. u32 i = 0;
  187. printf("**** %s\n", type);
  188. while (i < size && buffer[i] != 0) {
  189. putc(buffer[i]);
  190. i++;
  191. }
  192. }
  193. /**
  194. * pstore_display() - Display existing records in pstore reserved memory
  195. * @cmdtp: Command data struct pointer
  196. * @flag: Command flag
  197. * @argc: Command-line argument count
  198. * @argv: Array of command-line arguments
  199. *
  200. * A 'record-type' can be given to only display records of this kind.
  201. * If no 'record-type' is given, all valid records are dispayed.
  202. * 'record-type' can be one of 'dump', 'console', 'ftrace' or 'user'. For 'dump'
  203. * and 'ftrace' types, a 'nb' can be given to only display one record.
  204. *
  205. * Return: zero on success, CMD_RET_USAGE in case of misuse and negative
  206. * on error.
  207. */
  208. static int pstore_display(struct cmd_tbl *cmdtp, int flag, int argc,
  209. char * const argv[])
  210. {
  211. int type = PSTORE_TYPE_ALL;
  212. phys_addr_t ptr;
  213. char *buffer;
  214. u32 size;
  215. int header_len = 0;
  216. bool compressed;
  217. if (argc > 1) {
  218. if (!strcmp(argv[1], "dump"))
  219. type = PSTORE_TYPE_DMESG;
  220. else if (!strcmp(argv[1], "console"))
  221. type = PSTORE_TYPE_CONSOLE;
  222. else if (!strcmp(argv[1], "ftrace"))
  223. type = PSTORE_TYPE_FTRACE;
  224. else if (!strcmp(argv[1], "user"))
  225. type = PSTORE_TYPE_PMSG;
  226. else
  227. return CMD_RET_USAGE;
  228. }
  229. if (pstore_length == 0) {
  230. printf("Please set PStore configuration\n");
  231. return CMD_RET_USAGE;
  232. }
  233. if (buffer_size == 0)
  234. pstore_init_buffer_size();
  235. buffer = malloc_cache_aligned(buffer_size);
  236. if (type == PSTORE_TYPE_DMESG || type == PSTORE_TYPE_ALL) {
  237. ptr = pstore_addr;
  238. phys_addr_t ptr_end = ptr + pstore_length - pstore_pmsg_size
  239. - pstore_ftrace_size - pstore_console_size;
  240. if (argc > 2) {
  241. ptr += dectoul(argv[2], NULL)
  242. * pstore_record_size;
  243. ptr_end = ptr + pstore_record_size;
  244. }
  245. while (ptr < ptr_end) {
  246. size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr,
  247. pstore_record_size, buffer);
  248. ptr += pstore_record_size;
  249. if (size == 0)
  250. continue;
  251. header_len = pstore_read_kmsg_hdr(buffer, &compressed);
  252. if (header_len == 0) {
  253. log_debug("no valid kernel header\n");
  254. continue;
  255. }
  256. if (compressed) {
  257. printf("Compressed buffer, display not available\n");
  258. continue;
  259. }
  260. pstore_print_buffer("Dump", buffer + header_len,
  261. size - header_len);
  262. }
  263. }
  264. if (type == PSTORE_TYPE_CONSOLE || type == PSTORE_TYPE_ALL) {
  265. ptr = pstore_addr + pstore_length - pstore_pmsg_size
  266. - pstore_ftrace_size - pstore_console_size;
  267. size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr,
  268. pstore_console_size, buffer);
  269. if (size != 0)
  270. pstore_print_buffer("Console", buffer, size);
  271. }
  272. if (type == PSTORE_TYPE_FTRACE || type == PSTORE_TYPE_ALL) {
  273. ptr = pstore_addr + pstore_length - pstore_pmsg_size
  274. - pstore_ftrace_size;
  275. /* The FTrace record(s) uses LINUX_VERSION_CODE ^ 'DBGC'
  276. * signature, pass 0 to pstore_get_buffer to prevent
  277. * checking it
  278. */
  279. size = pstore_get_buffer(0, ptr, pstore_ftrace_size, buffer);
  280. if (size != 0)
  281. pstore_print_buffer("FTrace", buffer, size);
  282. }
  283. if (type == PSTORE_TYPE_PMSG || type == PSTORE_TYPE_ALL) {
  284. ptr = pstore_addr + pstore_length - pstore_pmsg_size;
  285. size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr,
  286. pstore_pmsg_size, buffer);
  287. if (size != 0)
  288. pstore_print_buffer("User", buffer, size);
  289. }
  290. free(buffer);
  291. return 0;
  292. }
  293. /**
  294. * pstore_save() - Save existing records from pstore reserved memory
  295. * @cmdtp: Command data struct pointer
  296. * @flag: Command flag
  297. * @argc: Command-line argument count
  298. * @argv: Array of command-line arguments
  299. *
  300. * the records are saved under 'directory path', which should already exist,
  301. * to partition 'part' on device type 'interface' instance 'dev'
  302. * Filenames are automatically generated, depending on record type, like in
  303. * /sys/fs/pstore under Linux
  304. *
  305. * Return: zero on success, CMD_RET_USAGE in case of misuse and negative
  306. * on error.
  307. */
  308. static int pstore_save(struct cmd_tbl *cmdtp, int flag, int argc,
  309. char * const argv[])
  310. {
  311. phys_addr_t ptr, ptr_end;
  312. char *buffer;
  313. char *save_argv[6];
  314. char addr[19], length[19];
  315. char path[256];
  316. u32 size;
  317. unsigned int index;
  318. int header_len = 0;
  319. bool compressed;
  320. if (argc < 4)
  321. return CMD_RET_USAGE;
  322. if (pstore_length == 0) {
  323. printf("Please set PStore configuration\n");
  324. return CMD_RET_USAGE;
  325. }
  326. if (buffer_size == 0)
  327. pstore_init_buffer_size();
  328. buffer = malloc_cache_aligned(buffer_size);
  329. sprintf(addr, "0x%p", buffer);
  330. save_argv[0] = argv[0];
  331. save_argv[1] = argv[1];
  332. save_argv[2] = argv[2];
  333. save_argv[3] = addr;
  334. save_argv[4] = path;
  335. save_argv[5] = length;
  336. /* Save all Dump records */
  337. ptr = pstore_addr;
  338. ptr_end = ptr + pstore_length - pstore_pmsg_size - pstore_ftrace_size
  339. - pstore_console_size;
  340. index = 0;
  341. while (ptr < ptr_end) {
  342. size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr,
  343. pstore_record_size, buffer);
  344. ptr += pstore_record_size;
  345. if (size == 0)
  346. continue;
  347. header_len = pstore_read_kmsg_hdr(buffer, &compressed);
  348. if (header_len == 0) {
  349. log_debug("no valid kernel header\n");
  350. continue;
  351. }
  352. sprintf(addr, "0x%08lx", (ulong)map_to_sysmem(buffer + header_len));
  353. sprintf(length, "0x%X", size - header_len);
  354. sprintf(path, "%s/dmesg-ramoops-%u%s", argv[3], index,
  355. compressed ? ".enc.z" : "");
  356. do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY);
  357. index++;
  358. }
  359. sprintf(addr, "0x%08lx", (ulong)map_to_sysmem(buffer));
  360. /* Save Console record */
  361. size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, pstore_console_size,
  362. buffer);
  363. if (size != 0) {
  364. sprintf(length, "0x%X", size);
  365. sprintf(path, "%s/console-ramoops-0", argv[3]);
  366. do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY);
  367. }
  368. ptr += pstore_console_size;
  369. /* Save FTrace record(s)
  370. * The FTrace record(s) uses LINUX_VERSION_CODE ^ 'DBGC' signature,
  371. * pass 0 to pstore_get_buffer to prevent checking it
  372. */
  373. size = pstore_get_buffer(0, ptr, pstore_ftrace_size, buffer);
  374. if (size != 0) {
  375. sprintf(length, "0x%X", size);
  376. sprintf(path, "%s/ftrace-ramoops-0", argv[3]);
  377. do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY);
  378. }
  379. ptr += pstore_ftrace_size;
  380. /* Save Console record */
  381. size = pstore_get_buffer(PERSISTENT_RAM_SIG, ptr, pstore_pmsg_size,
  382. buffer);
  383. if (size != 0) {
  384. sprintf(length, "0x%X", size);
  385. sprintf(path, "%s/pmsg-ramoops-0", argv[3]);
  386. do_save(cmdtp, flag, 6, save_argv, FS_TYPE_ANY);
  387. }
  388. free(buffer);
  389. return 0;
  390. }
  391. static struct cmd_tbl cmd_pstore_sub[] = {
  392. U_BOOT_CMD_MKENT(set, 8, 0, pstore_set, "", ""),
  393. U_BOOT_CMD_MKENT(display, 3, 0, pstore_display, "", ""),
  394. U_BOOT_CMD_MKENT(save, 4, 0, pstore_save, "", ""),
  395. };
  396. static int do_pstore(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
  397. {
  398. struct cmd_tbl *c;
  399. if (argc < 2)
  400. return CMD_RET_USAGE;
  401. /* Strip off leading argument */
  402. argc--;
  403. argv++;
  404. c = find_cmd_tbl(argv[0], cmd_pstore_sub, ARRAY_SIZE(cmd_pstore_sub));
  405. if (!c)
  406. return CMD_RET_USAGE;
  407. return c->cmd(cmdtp, flag, argc, argv);
  408. }
  409. void fdt_fixup_pstore(void *blob)
  410. {
  411. char node[32];
  412. int nodeoffset; /* node offset from libfdt */
  413. u32 addr_cells;
  414. u32 size_cells;
  415. nodeoffset = fdt_path_offset(blob, "/");
  416. if (nodeoffset < 0) {
  417. /* Not found or something else bad happened. */
  418. log_err("fdt_path_offset() returned %s\n", fdt_strerror(nodeoffset));
  419. return;
  420. }
  421. nodeoffset = fdt_find_or_add_subnode(blob, nodeoffset, "reserved-memory");
  422. if (nodeoffset < 0) {
  423. log_err("Add 'reserved-memory' node failed: %s\n",
  424. fdt_strerror(nodeoffset));
  425. return;
  426. }
  427. addr_cells = fdt_getprop_u32_default_node(blob, nodeoffset, 0, "#address-cells", 2);
  428. size_cells = fdt_getprop_u32_default_node(blob, nodeoffset, 0, "#size-cells", 2);
  429. fdt_setprop_u32(blob, nodeoffset, "#address-cells", addr_cells);
  430. fdt_setprop_u32(blob, nodeoffset, "#size-cells", size_cells);
  431. fdt_setprop_empty(blob, nodeoffset, "ranges");
  432. sprintf(node, "ramoops@%llx", (unsigned long long)pstore_addr);
  433. nodeoffset = fdt_add_subnode(blob, nodeoffset, node);
  434. if (nodeoffset < 0) {
  435. log_err("Add '%s' node failed: %s\n", node, fdt_strerror(nodeoffset));
  436. return;
  437. }
  438. fdt_setprop_string(blob, nodeoffset, "compatible", "ramoops");
  439. if (addr_cells == 1) {
  440. fdt_setprop_u32(blob, nodeoffset, "reg", pstore_addr);
  441. } else if (addr_cells == 2) {
  442. fdt_setprop_u64(blob, nodeoffset, "reg", pstore_addr);
  443. } else {
  444. log_err("Unsupported #address-cells: %u\n", addr_cells);
  445. goto clean_ramoops;
  446. }
  447. if (size_cells == 1) {
  448. // Let's consider that the pstore_length fits in a 32 bits value
  449. fdt_appendprop_u32(blob, nodeoffset, "reg", pstore_length);
  450. } else if (size_cells == 2) {
  451. fdt_appendprop_u64(blob, nodeoffset, "reg", pstore_length);
  452. } else {
  453. log_err("Unsupported #size-cells: %u\n", addr_cells);
  454. goto clean_ramoops;
  455. }
  456. fdt_setprop_u32(blob, nodeoffset, "record-size", pstore_record_size);
  457. fdt_setprop_u32(blob, nodeoffset, "console-size", pstore_console_size);
  458. fdt_setprop_u32(blob, nodeoffset, "ftrace-size", pstore_ftrace_size);
  459. fdt_setprop_u32(blob, nodeoffset, "pmsg-size", pstore_pmsg_size);
  460. fdt_setprop_u32(blob, nodeoffset, "ecc-size", pstore_ecc_size);
  461. clean_ramoops:
  462. fdt_del_node_and_alias(blob, node);
  463. }
  464. U_BOOT_CMD(pstore, 10, 0, do_pstore,
  465. "Manage Linux Persistent Storage",
  466. "set <addr> <len> [record-size] [console-size] [ftrace-size] [pmsg_size] [ecc-size]\n"
  467. "- Set pstore reserved memory info, starting at 'addr' for 'len' bytes.\n"
  468. " Default length for records is 4K.\n"
  469. " 'record-size' is the size of one panic or oops record ('dump' type).\n"
  470. " 'console-size' is the size of the kernel logs record.\n"
  471. " 'ftrace-size' is the size of the ftrace record(s), this can be a single\n"
  472. " record or divided in parts based on number of CPUs.\n"
  473. " 'pmsg-size' is the size of the user space logs record.\n"
  474. " 'ecc-size' enables/disables ECC support and specifies ECC buffer size in\n"
  475. " bytes (0 disables it, 1 is a special value, means 16 bytes ECC).\n"
  476. "pstore display [record-type] [nb]\n"
  477. "- Display existing records in pstore reserved memory. A 'record-type' can\n"
  478. " be given to only display records of this kind. 'record-type' can be one\n"
  479. " of 'dump', 'console', 'ftrace' or 'user'. For 'dump' and 'ftrace' types,\n"
  480. " a 'nb' can be given to only display one record.\n"
  481. "pstore save <interface> <dev[:part]> <directory-path>\n"
  482. "- Save existing records in pstore reserved memory under 'directory path'\n"
  483. " to partition 'part' on device type 'interface' instance 'dev'.\n"
  484. " Filenames are automatically generated, depending on record type, like\n"
  485. " in /sys/fs/pstore under Linux.\n"
  486. " The 'directory-path' should already exist.\n"
  487. );