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