root/kernel/crash_core.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. parse_crashkernel_mem
  2. parse_crashkernel_simple
  3. parse_crashkernel_suffix
  4. get_last_crashkernel
  5. __parse_crashkernel
  6. parse_crashkernel
  7. parse_crashkernel_high
  8. parse_crashkernel_low
  9. append_elf_note
  10. final_note
  11. update_vmcoreinfo_note
  12. crash_update_vmcoreinfo_safecopy
  13. crash_save_vmcoreinfo
  14. vmcoreinfo_append_str
  15. arch_crash_save_vmcoreinfo
  16. paddr_vmcoreinfo_note
  17. crash_save_vmcoreinfo_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * crash.c - kernel crash support code.
   4  * Copyright (C) 2002-2004 Eric Biederman  <ebiederm@xmission.com>
   5  */
   6 
   7 #include <linux/crash_core.h>
   8 #include <linux/utsname.h>
   9 #include <linux/vmalloc.h>
  10 
  11 #include <asm/page.h>
  12 #include <asm/sections.h>
  13 
  14 /* vmcoreinfo stuff */
  15 unsigned char *vmcoreinfo_data;
  16 size_t vmcoreinfo_size;
  17 u32 *vmcoreinfo_note;
  18 
  19 /* trusted vmcoreinfo, e.g. we can make a copy in the crash memory */
  20 static unsigned char *vmcoreinfo_data_safecopy;
  21 
  22 /*
  23  * parsing the "crashkernel" commandline
  24  *
  25  * this code is intended to be called from architecture specific code
  26  */
  27 
  28 
  29 /*
  30  * This function parses command lines in the format
  31  *
  32  *   crashkernel=ramsize-range:size[,...][@offset]
  33  *
  34  * The function returns 0 on success and -EINVAL on failure.
  35  */
  36 static int __init parse_crashkernel_mem(char *cmdline,
  37                                         unsigned long long system_ram,
  38                                         unsigned long long *crash_size,
  39                                         unsigned long long *crash_base)
  40 {
  41         char *cur = cmdline, *tmp;
  42 
  43         /* for each entry of the comma-separated list */
  44         do {
  45                 unsigned long long start, end = ULLONG_MAX, size;
  46 
  47                 /* get the start of the range */
  48                 start = memparse(cur, &tmp);
  49                 if (cur == tmp) {
  50                         pr_warn("crashkernel: Memory value expected\n");
  51                         return -EINVAL;
  52                 }
  53                 cur = tmp;
  54                 if (*cur != '-') {
  55                         pr_warn("crashkernel: '-' expected\n");
  56                         return -EINVAL;
  57                 }
  58                 cur++;
  59 
  60                 /* if no ':' is here, than we read the end */
  61                 if (*cur != ':') {
  62                         end = memparse(cur, &tmp);
  63                         if (cur == tmp) {
  64                                 pr_warn("crashkernel: Memory value expected\n");
  65                                 return -EINVAL;
  66                         }
  67                         cur = tmp;
  68                         if (end <= start) {
  69                                 pr_warn("crashkernel: end <= start\n");
  70                                 return -EINVAL;
  71                         }
  72                 }
  73 
  74                 if (*cur != ':') {
  75                         pr_warn("crashkernel: ':' expected\n");
  76                         return -EINVAL;
  77                 }
  78                 cur++;
  79 
  80                 size = memparse(cur, &tmp);
  81                 if (cur == tmp) {
  82                         pr_warn("Memory value expected\n");
  83                         return -EINVAL;
  84                 }
  85                 cur = tmp;
  86                 if (size >= system_ram) {
  87                         pr_warn("crashkernel: invalid size\n");
  88                         return -EINVAL;
  89                 }
  90 
  91                 /* match ? */
  92                 if (system_ram >= start && system_ram < end) {
  93                         *crash_size = size;
  94                         break;
  95                 }
  96         } while (*cur++ == ',');
  97 
  98         if (*crash_size > 0) {
  99                 while (*cur && *cur != ' ' && *cur != '@')
 100                         cur++;
 101                 if (*cur == '@') {
 102                         cur++;
 103                         *crash_base = memparse(cur, &tmp);
 104                         if (cur == tmp) {
 105                                 pr_warn("Memory value expected after '@'\n");
 106                                 return -EINVAL;
 107                         }
 108                 }
 109         } else
 110                 pr_info("crashkernel size resulted in zero bytes\n");
 111 
 112         return 0;
 113 }
 114 
 115 /*
 116  * That function parses "simple" (old) crashkernel command lines like
 117  *
 118  *      crashkernel=size[@offset]
 119  *
 120  * It returns 0 on success and -EINVAL on failure.
 121  */
 122 static int __init parse_crashkernel_simple(char *cmdline,
 123                                            unsigned long long *crash_size,
 124                                            unsigned long long *crash_base)
 125 {
 126         char *cur = cmdline;
 127 
 128         *crash_size = memparse(cmdline, &cur);
 129         if (cmdline == cur) {
 130                 pr_warn("crashkernel: memory value expected\n");
 131                 return -EINVAL;
 132         }
 133 
 134         if (*cur == '@')
 135                 *crash_base = memparse(cur+1, &cur);
 136         else if (*cur != ' ' && *cur != '\0') {
 137                 pr_warn("crashkernel: unrecognized char: %c\n", *cur);
 138                 return -EINVAL;
 139         }
 140 
 141         return 0;
 142 }
 143 
 144 #define SUFFIX_HIGH 0
 145 #define SUFFIX_LOW  1
 146 #define SUFFIX_NULL 2
 147 static __initdata char *suffix_tbl[] = {
 148         [SUFFIX_HIGH] = ",high",
 149         [SUFFIX_LOW]  = ",low",
 150         [SUFFIX_NULL] = NULL,
 151 };
 152 
 153 /*
 154  * That function parses "suffix"  crashkernel command lines like
 155  *
 156  *      crashkernel=size,[high|low]
 157  *
 158  * It returns 0 on success and -EINVAL on failure.
 159  */
 160 static int __init parse_crashkernel_suffix(char *cmdline,
 161                                            unsigned long long   *crash_size,
 162                                            const char *suffix)
 163 {
 164         char *cur = cmdline;
 165 
 166         *crash_size = memparse(cmdline, &cur);
 167         if (cmdline == cur) {
 168                 pr_warn("crashkernel: memory value expected\n");
 169                 return -EINVAL;
 170         }
 171 
 172         /* check with suffix */
 173         if (strncmp(cur, suffix, strlen(suffix))) {
 174                 pr_warn("crashkernel: unrecognized char: %c\n", *cur);
 175                 return -EINVAL;
 176         }
 177         cur += strlen(suffix);
 178         if (*cur != ' ' && *cur != '\0') {
 179                 pr_warn("crashkernel: unrecognized char: %c\n", *cur);
 180                 return -EINVAL;
 181         }
 182 
 183         return 0;
 184 }
 185 
 186 static __init char *get_last_crashkernel(char *cmdline,
 187                              const char *name,
 188                              const char *suffix)
 189 {
 190         char *p = cmdline, *ck_cmdline = NULL;
 191 
 192         /* find crashkernel and use the last one if there are more */
 193         p = strstr(p, name);
 194         while (p) {
 195                 char *end_p = strchr(p, ' ');
 196                 char *q;
 197 
 198                 if (!end_p)
 199                         end_p = p + strlen(p);
 200 
 201                 if (!suffix) {
 202                         int i;
 203 
 204                         /* skip the one with any known suffix */
 205                         for (i = 0; suffix_tbl[i]; i++) {
 206                                 q = end_p - strlen(suffix_tbl[i]);
 207                                 if (!strncmp(q, suffix_tbl[i],
 208                                              strlen(suffix_tbl[i])))
 209                                         goto next;
 210                         }
 211                         ck_cmdline = p;
 212                 } else {
 213                         q = end_p - strlen(suffix);
 214                         if (!strncmp(q, suffix, strlen(suffix)))
 215                                 ck_cmdline = p;
 216                 }
 217 next:
 218                 p = strstr(p+1, name);
 219         }
 220 
 221         if (!ck_cmdline)
 222                 return NULL;
 223 
 224         return ck_cmdline;
 225 }
 226 
 227 static int __init __parse_crashkernel(char *cmdline,
 228                              unsigned long long system_ram,
 229                              unsigned long long *crash_size,
 230                              unsigned long long *crash_base,
 231                              const char *name,
 232                              const char *suffix)
 233 {
 234         char    *first_colon, *first_space;
 235         char    *ck_cmdline;
 236 
 237         BUG_ON(!crash_size || !crash_base);
 238         *crash_size = 0;
 239         *crash_base = 0;
 240 
 241         ck_cmdline = get_last_crashkernel(cmdline, name, suffix);
 242 
 243         if (!ck_cmdline)
 244                 return -EINVAL;
 245 
 246         ck_cmdline += strlen(name);
 247 
 248         if (suffix)
 249                 return parse_crashkernel_suffix(ck_cmdline, crash_size,
 250                                 suffix);
 251         /*
 252          * if the commandline contains a ':', then that's the extended
 253          * syntax -- if not, it must be the classic syntax
 254          */
 255         first_colon = strchr(ck_cmdline, ':');
 256         first_space = strchr(ck_cmdline, ' ');
 257         if (first_colon && (!first_space || first_colon < first_space))
 258                 return parse_crashkernel_mem(ck_cmdline, system_ram,
 259                                 crash_size, crash_base);
 260 
 261         return parse_crashkernel_simple(ck_cmdline, crash_size, crash_base);
 262 }
 263 
 264 /*
 265  * That function is the entry point for command line parsing and should be
 266  * called from the arch-specific code.
 267  */
 268 int __init parse_crashkernel(char *cmdline,
 269                              unsigned long long system_ram,
 270                              unsigned long long *crash_size,
 271                              unsigned long long *crash_base)
 272 {
 273         return __parse_crashkernel(cmdline, system_ram, crash_size, crash_base,
 274                                         "crashkernel=", NULL);
 275 }
 276 
 277 int __init parse_crashkernel_high(char *cmdline,
 278                              unsigned long long system_ram,
 279                              unsigned long long *crash_size,
 280                              unsigned long long *crash_base)
 281 {
 282         return __parse_crashkernel(cmdline, system_ram, crash_size, crash_base,
 283                                 "crashkernel=", suffix_tbl[SUFFIX_HIGH]);
 284 }
 285 
 286 int __init parse_crashkernel_low(char *cmdline,
 287                              unsigned long long system_ram,
 288                              unsigned long long *crash_size,
 289                              unsigned long long *crash_base)
 290 {
 291         return __parse_crashkernel(cmdline, system_ram, crash_size, crash_base,
 292                                 "crashkernel=", suffix_tbl[SUFFIX_LOW]);
 293 }
 294 
 295 Elf_Word *append_elf_note(Elf_Word *buf, char *name, unsigned int type,
 296                           void *data, size_t data_len)
 297 {
 298         struct elf_note *note = (struct elf_note *)buf;
 299 
 300         note->n_namesz = strlen(name) + 1;
 301         note->n_descsz = data_len;
 302         note->n_type   = type;
 303         buf += DIV_ROUND_UP(sizeof(*note), sizeof(Elf_Word));
 304         memcpy(buf, name, note->n_namesz);
 305         buf += DIV_ROUND_UP(note->n_namesz, sizeof(Elf_Word));
 306         memcpy(buf, data, data_len);
 307         buf += DIV_ROUND_UP(data_len, sizeof(Elf_Word));
 308 
 309         return buf;
 310 }
 311 
 312 void final_note(Elf_Word *buf)
 313 {
 314         memset(buf, 0, sizeof(struct elf_note));
 315 }
 316 
 317 static void update_vmcoreinfo_note(void)
 318 {
 319         u32 *buf = vmcoreinfo_note;
 320 
 321         if (!vmcoreinfo_size)
 322                 return;
 323         buf = append_elf_note(buf, VMCOREINFO_NOTE_NAME, 0, vmcoreinfo_data,
 324                               vmcoreinfo_size);
 325         final_note(buf);
 326 }
 327 
 328 void crash_update_vmcoreinfo_safecopy(void *ptr)
 329 {
 330         if (ptr)
 331                 memcpy(ptr, vmcoreinfo_data, vmcoreinfo_size);
 332 
 333         vmcoreinfo_data_safecopy = ptr;
 334 }
 335 
 336 void crash_save_vmcoreinfo(void)
 337 {
 338         if (!vmcoreinfo_note)
 339                 return;
 340 
 341         /* Use the safe copy to generate vmcoreinfo note if have */
 342         if (vmcoreinfo_data_safecopy)
 343                 vmcoreinfo_data = vmcoreinfo_data_safecopy;
 344 
 345         vmcoreinfo_append_str("CRASHTIME=%lld\n", ktime_get_real_seconds());
 346         update_vmcoreinfo_note();
 347 }
 348 
 349 void vmcoreinfo_append_str(const char *fmt, ...)
 350 {
 351         va_list args;
 352         char buf[0x50];
 353         size_t r;
 354 
 355         va_start(args, fmt);
 356         r = vscnprintf(buf, sizeof(buf), fmt, args);
 357         va_end(args);
 358 
 359         r = min(r, (size_t)VMCOREINFO_BYTES - vmcoreinfo_size);
 360 
 361         memcpy(&vmcoreinfo_data[vmcoreinfo_size], buf, r);
 362 
 363         vmcoreinfo_size += r;
 364 }
 365 
 366 /*
 367  * provide an empty default implementation here -- architecture
 368  * code may override this
 369  */
 370 void __weak arch_crash_save_vmcoreinfo(void)
 371 {}
 372 
 373 phys_addr_t __weak paddr_vmcoreinfo_note(void)
 374 {
 375         return __pa(vmcoreinfo_note);
 376 }
 377 EXPORT_SYMBOL(paddr_vmcoreinfo_note);
 378 
 379 static int __init crash_save_vmcoreinfo_init(void)
 380 {
 381         vmcoreinfo_data = (unsigned char *)get_zeroed_page(GFP_KERNEL);
 382         if (!vmcoreinfo_data) {
 383                 pr_warn("Memory allocation for vmcoreinfo_data failed\n");
 384                 return -ENOMEM;
 385         }
 386 
 387         vmcoreinfo_note = alloc_pages_exact(VMCOREINFO_NOTE_SIZE,
 388                                                 GFP_KERNEL | __GFP_ZERO);
 389         if (!vmcoreinfo_note) {
 390                 free_page((unsigned long)vmcoreinfo_data);
 391                 vmcoreinfo_data = NULL;
 392                 pr_warn("Memory allocation for vmcoreinfo_note failed\n");
 393                 return -ENOMEM;
 394         }
 395 
 396         VMCOREINFO_OSRELEASE(init_uts_ns.name.release);
 397         VMCOREINFO_PAGESIZE(PAGE_SIZE);
 398 
 399         VMCOREINFO_SYMBOL(init_uts_ns);
 400         VMCOREINFO_SYMBOL(node_online_map);
 401 #ifdef CONFIG_MMU
 402         VMCOREINFO_SYMBOL_ARRAY(swapper_pg_dir);
 403 #endif
 404         VMCOREINFO_SYMBOL(_stext);
 405         VMCOREINFO_SYMBOL(vmap_area_list);
 406 
 407 #ifndef CONFIG_NEED_MULTIPLE_NODES
 408         VMCOREINFO_SYMBOL(mem_map);
 409         VMCOREINFO_SYMBOL(contig_page_data);
 410 #endif
 411 #ifdef CONFIG_SPARSEMEM
 412         VMCOREINFO_SYMBOL_ARRAY(mem_section);
 413         VMCOREINFO_LENGTH(mem_section, NR_SECTION_ROOTS);
 414         VMCOREINFO_STRUCT_SIZE(mem_section);
 415         VMCOREINFO_OFFSET(mem_section, section_mem_map);
 416 #endif
 417         VMCOREINFO_STRUCT_SIZE(page);
 418         VMCOREINFO_STRUCT_SIZE(pglist_data);
 419         VMCOREINFO_STRUCT_SIZE(zone);
 420         VMCOREINFO_STRUCT_SIZE(free_area);
 421         VMCOREINFO_STRUCT_SIZE(list_head);
 422         VMCOREINFO_SIZE(nodemask_t);
 423         VMCOREINFO_OFFSET(page, flags);
 424         VMCOREINFO_OFFSET(page, _refcount);
 425         VMCOREINFO_OFFSET(page, mapping);
 426         VMCOREINFO_OFFSET(page, lru);
 427         VMCOREINFO_OFFSET(page, _mapcount);
 428         VMCOREINFO_OFFSET(page, private);
 429         VMCOREINFO_OFFSET(page, compound_dtor);
 430         VMCOREINFO_OFFSET(page, compound_order);
 431         VMCOREINFO_OFFSET(page, compound_head);
 432         VMCOREINFO_OFFSET(pglist_data, node_zones);
 433         VMCOREINFO_OFFSET(pglist_data, nr_zones);
 434 #ifdef CONFIG_FLAT_NODE_MEM_MAP
 435         VMCOREINFO_OFFSET(pglist_data, node_mem_map);
 436 #endif
 437         VMCOREINFO_OFFSET(pglist_data, node_start_pfn);
 438         VMCOREINFO_OFFSET(pglist_data, node_spanned_pages);
 439         VMCOREINFO_OFFSET(pglist_data, node_id);
 440         VMCOREINFO_OFFSET(zone, free_area);
 441         VMCOREINFO_OFFSET(zone, vm_stat);
 442         VMCOREINFO_OFFSET(zone, spanned_pages);
 443         VMCOREINFO_OFFSET(free_area, free_list);
 444         VMCOREINFO_OFFSET(list_head, next);
 445         VMCOREINFO_OFFSET(list_head, prev);
 446         VMCOREINFO_OFFSET(vmap_area, va_start);
 447         VMCOREINFO_OFFSET(vmap_area, list);
 448         VMCOREINFO_LENGTH(zone.free_area, MAX_ORDER);
 449         log_buf_vmcoreinfo_setup();
 450         VMCOREINFO_LENGTH(free_area.free_list, MIGRATE_TYPES);
 451         VMCOREINFO_NUMBER(NR_FREE_PAGES);
 452         VMCOREINFO_NUMBER(PG_lru);
 453         VMCOREINFO_NUMBER(PG_private);
 454         VMCOREINFO_NUMBER(PG_swapcache);
 455         VMCOREINFO_NUMBER(PG_swapbacked);
 456         VMCOREINFO_NUMBER(PG_slab);
 457 #ifdef CONFIG_MEMORY_FAILURE
 458         VMCOREINFO_NUMBER(PG_hwpoison);
 459 #endif
 460         VMCOREINFO_NUMBER(PG_head_mask);
 461 #define PAGE_BUDDY_MAPCOUNT_VALUE       (~PG_buddy)
 462         VMCOREINFO_NUMBER(PAGE_BUDDY_MAPCOUNT_VALUE);
 463 #ifdef CONFIG_HUGETLB_PAGE
 464         VMCOREINFO_NUMBER(HUGETLB_PAGE_DTOR);
 465 #define PAGE_OFFLINE_MAPCOUNT_VALUE     (~PG_offline)
 466         VMCOREINFO_NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE);
 467 #endif
 468 
 469         arch_crash_save_vmcoreinfo();
 470         update_vmcoreinfo_note();
 471 
 472         return 0;
 473 }
 474 
 475 subsys_initcall(crash_save_vmcoreinfo_init);

/* [<][>][^][v][top][bottom][index][help] */