pstore.c 16 KB

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