pstore.c 16 KB

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