Blame view

kernel/crash_core.c 11.3 KB
692f66f26   Hari Bathini   crash: move crash...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /*
   * crash.c - kernel crash support code.
   * Copyright (C) 2002-2004 Eric Biederman  <ebiederm@xmission.com>
   *
   * This source code is licensed under the GNU General Public License,
   * Version 2.  See the file COPYING for more details.
   */
  
  #include <linux/crash_core.h>
  #include <linux/utsname.h>
  #include <linux/vmalloc.h>
  
  #include <asm/page.h>
  #include <asm/sections.h>
  
  /* vmcoreinfo stuff */
203e9e412   Xunlei Pang   kexec: move vmcor...
17
  static unsigned char *vmcoreinfo_data;
1229384f5   Xunlei Pang   kdump: protect vm...
18
  static size_t vmcoreinfo_size;
203e9e412   Xunlei Pang   kexec: move vmcor...
19
  u32 *vmcoreinfo_note;
692f66f26   Hari Bathini   crash: move crash...
20

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