Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/kernel/crash_reserve.c
26243 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* crash.c - kernel crash support code.
4
* Copyright (C) 2002-2004 Eric Biederman <[email protected]>
5
*/
6
7
#include <linux/buildid.h>
8
#include <linux/init.h>
9
#include <linux/utsname.h>
10
#include <linux/vmalloc.h>
11
#include <linux/sizes.h>
12
#include <linux/kexec.h>
13
#include <linux/memory.h>
14
#include <linux/cpuhotplug.h>
15
#include <linux/memblock.h>
16
#include <linux/kmemleak.h>
17
#include <linux/cma.h>
18
#include <linux/crash_reserve.h>
19
20
#include <asm/page.h>
21
#include <asm/sections.h>
22
23
#include <crypto/sha1.h>
24
25
#include "kallsyms_internal.h"
26
#include "kexec_internal.h"
27
28
/* Location of the reserved area for the crash kernel */
29
struct resource crashk_res = {
30
.name = "Crash kernel",
31
.start = 0,
32
.end = 0,
33
.flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM,
34
.desc = IORES_DESC_CRASH_KERNEL
35
};
36
struct resource crashk_low_res = {
37
.name = "Crash kernel",
38
.start = 0,
39
.end = 0,
40
.flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM,
41
.desc = IORES_DESC_CRASH_KERNEL
42
};
43
44
/*
45
* parsing the "crashkernel" commandline
46
*
47
* this code is intended to be called from architecture specific code
48
*/
49
50
51
/*
52
* This function parses command lines in the format
53
*
54
* crashkernel=ramsize-range:size[,...][@offset]
55
*
56
* The function returns 0 on success and -EINVAL on failure.
57
*/
58
static int __init parse_crashkernel_mem(char *cmdline,
59
unsigned long long system_ram,
60
unsigned long long *crash_size,
61
unsigned long long *crash_base)
62
{
63
char *cur = cmdline, *tmp;
64
unsigned long long total_mem = system_ram;
65
66
/*
67
* Firmware sometimes reserves some memory regions for its own use,
68
* so the system memory size is less than the actual physical memory
69
* size. Work around this by rounding up the total size to 128M,
70
* which is enough for most test cases.
71
*/
72
total_mem = roundup(total_mem, SZ_128M);
73
74
/* for each entry of the comma-separated list */
75
do {
76
unsigned long long start, end = ULLONG_MAX, size;
77
78
/* get the start of the range */
79
start = memparse(cur, &tmp);
80
if (cur == tmp) {
81
pr_warn("crashkernel: Memory value expected\n");
82
return -EINVAL;
83
}
84
cur = tmp;
85
if (*cur != '-') {
86
pr_warn("crashkernel: '-' expected\n");
87
return -EINVAL;
88
}
89
cur++;
90
91
/* if no ':' is here, than we read the end */
92
if (*cur != ':') {
93
end = memparse(cur, &tmp);
94
if (cur == tmp) {
95
pr_warn("crashkernel: Memory value expected\n");
96
return -EINVAL;
97
}
98
cur = tmp;
99
if (end <= start) {
100
pr_warn("crashkernel: end <= start\n");
101
return -EINVAL;
102
}
103
}
104
105
if (*cur != ':') {
106
pr_warn("crashkernel: ':' expected\n");
107
return -EINVAL;
108
}
109
cur++;
110
111
size = memparse(cur, &tmp);
112
if (cur == tmp) {
113
pr_warn("crashkernel: Memory value expected\n");
114
return -EINVAL;
115
}
116
cur = tmp;
117
if (size >= total_mem) {
118
pr_warn("crashkernel: invalid size\n");
119
return -EINVAL;
120
}
121
122
/* match ? */
123
if (total_mem >= start && total_mem < end) {
124
*crash_size = size;
125
break;
126
}
127
} while (*cur++ == ',');
128
129
if (*crash_size > 0) {
130
while (*cur && *cur != ' ' && *cur != '@')
131
cur++;
132
if (*cur == '@') {
133
cur++;
134
*crash_base = memparse(cur, &tmp);
135
if (cur == tmp) {
136
pr_warn("crashkernel: Memory value expected after '@'\n");
137
return -EINVAL;
138
}
139
}
140
} else
141
pr_info("crashkernel size resulted in zero bytes\n");
142
143
return 0;
144
}
145
146
/*
147
* That function parses "simple" (old) crashkernel command lines like
148
*
149
* crashkernel=size[@offset]
150
*
151
* It returns 0 on success and -EINVAL on failure.
152
*/
153
static int __init parse_crashkernel_simple(char *cmdline,
154
unsigned long long *crash_size,
155
unsigned long long *crash_base)
156
{
157
char *cur = cmdline;
158
159
*crash_size = memparse(cmdline, &cur);
160
if (cmdline == cur) {
161
pr_warn("crashkernel: memory value expected\n");
162
return -EINVAL;
163
}
164
165
if (*cur == '@')
166
*crash_base = memparse(cur+1, &cur);
167
else if (*cur != ' ' && *cur != '\0') {
168
pr_warn("crashkernel: unrecognized char: %c\n", *cur);
169
return -EINVAL;
170
}
171
172
return 0;
173
}
174
175
#define SUFFIX_HIGH 0
176
#define SUFFIX_LOW 1
177
#define SUFFIX_CMA 2
178
#define SUFFIX_NULL 3
179
static __initdata char *suffix_tbl[] = {
180
[SUFFIX_HIGH] = ",high",
181
[SUFFIX_LOW] = ",low",
182
[SUFFIX_CMA] = ",cma",
183
[SUFFIX_NULL] = NULL,
184
};
185
186
/*
187
* That function parses "suffix" crashkernel command lines like
188
*
189
* crashkernel=size,[high|low|cma]
190
*
191
* It returns 0 on success and -EINVAL on failure.
192
*/
193
static int __init parse_crashkernel_suffix(char *cmdline,
194
unsigned long long *crash_size,
195
const char *suffix)
196
{
197
char *cur = cmdline;
198
199
*crash_size = memparse(cmdline, &cur);
200
if (cmdline == cur) {
201
pr_warn("crashkernel: memory value expected\n");
202
return -EINVAL;
203
}
204
205
/* check with suffix */
206
if (strncmp(cur, suffix, strlen(suffix))) {
207
pr_warn("crashkernel: unrecognized char: %c\n", *cur);
208
return -EINVAL;
209
}
210
cur += strlen(suffix);
211
if (*cur != ' ' && *cur != '\0') {
212
pr_warn("crashkernel: unrecognized char: %c\n", *cur);
213
return -EINVAL;
214
}
215
216
return 0;
217
}
218
219
static __init char *get_last_crashkernel(char *cmdline,
220
const char *name,
221
const char *suffix)
222
{
223
char *p = cmdline, *ck_cmdline = NULL;
224
225
/* find crashkernel and use the last one if there are more */
226
p = strstr(p, name);
227
while (p) {
228
char *end_p = strchr(p, ' ');
229
char *q;
230
231
if (!end_p)
232
end_p = p + strlen(p);
233
234
if (!suffix) {
235
int i;
236
237
/* skip the one with any known suffix */
238
for (i = 0; suffix_tbl[i]; i++) {
239
q = end_p - strlen(suffix_tbl[i]);
240
if (!strncmp(q, suffix_tbl[i],
241
strlen(suffix_tbl[i])))
242
goto next;
243
}
244
ck_cmdline = p;
245
} else {
246
q = end_p - strlen(suffix);
247
if (!strncmp(q, suffix, strlen(suffix)))
248
ck_cmdline = p;
249
}
250
next:
251
p = strstr(p+1, name);
252
}
253
254
return ck_cmdline;
255
}
256
257
static int __init __parse_crashkernel(char *cmdline,
258
unsigned long long system_ram,
259
unsigned long long *crash_size,
260
unsigned long long *crash_base,
261
const char *suffix)
262
{
263
char *first_colon, *first_space;
264
char *ck_cmdline;
265
char *name = "crashkernel=";
266
267
BUG_ON(!crash_size || !crash_base);
268
*crash_size = 0;
269
*crash_base = 0;
270
271
ck_cmdline = get_last_crashkernel(cmdline, name, suffix);
272
if (!ck_cmdline)
273
return -ENOENT;
274
275
ck_cmdline += strlen(name);
276
277
if (suffix)
278
return parse_crashkernel_suffix(ck_cmdline, crash_size,
279
suffix);
280
/*
281
* if the commandline contains a ':', then that's the extended
282
* syntax -- if not, it must be the classic syntax
283
*/
284
first_colon = strchr(ck_cmdline, ':');
285
first_space = strchr(ck_cmdline, ' ');
286
if (first_colon && (!first_space || first_colon < first_space))
287
return parse_crashkernel_mem(ck_cmdline, system_ram,
288
crash_size, crash_base);
289
290
return parse_crashkernel_simple(ck_cmdline, crash_size, crash_base);
291
}
292
293
/*
294
* That function is the entry point for command line parsing and should be
295
* called from the arch-specific code.
296
*
297
* If crashkernel=,high|low is supported on architecture, non-NULL values
298
* should be passed to parameters 'low_size' and 'high'.
299
*/
300
int __init parse_crashkernel(char *cmdline,
301
unsigned long long system_ram,
302
unsigned long long *crash_size,
303
unsigned long long *crash_base,
304
unsigned long long *low_size,
305
unsigned long long *cma_size,
306
bool *high)
307
{
308
int ret;
309
unsigned long long __always_unused cma_base;
310
311
/* crashkernel=X[@offset] */
312
ret = __parse_crashkernel(cmdline, system_ram, crash_size,
313
crash_base, NULL);
314
#ifdef CONFIG_ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION
315
/*
316
* If non-NULL 'high' passed in and no normal crashkernel
317
* setting detected, try parsing crashkernel=,high|low.
318
*/
319
if (high && ret == -ENOENT) {
320
ret = __parse_crashkernel(cmdline, 0, crash_size,
321
crash_base, suffix_tbl[SUFFIX_HIGH]);
322
if (ret || !*crash_size)
323
return -EINVAL;
324
325
/*
326
* crashkernel=Y,low can be specified or not, but invalid value
327
* is not allowed.
328
*/
329
ret = __parse_crashkernel(cmdline, 0, low_size,
330
crash_base, suffix_tbl[SUFFIX_LOW]);
331
if (ret == -ENOENT) {
332
*low_size = DEFAULT_CRASH_KERNEL_LOW_SIZE;
333
ret = 0;
334
} else if (ret) {
335
return ret;
336
}
337
338
*high = true;
339
}
340
341
/*
342
* optional CMA reservation
343
* cma_base is ignored
344
*/
345
if (cma_size)
346
__parse_crashkernel(cmdline, 0, cma_size,
347
&cma_base, suffix_tbl[SUFFIX_CMA]);
348
#endif
349
if (!*crash_size)
350
ret = -EINVAL;
351
352
if (*crash_size >= system_ram)
353
ret = -EINVAL;
354
355
return ret;
356
}
357
358
/*
359
* Add a dummy early_param handler to mark crashkernel= as a known command line
360
* parameter and suppress incorrect warnings in init/main.c.
361
*/
362
static int __init parse_crashkernel_dummy(char *arg)
363
{
364
return 0;
365
}
366
early_param("crashkernel", parse_crashkernel_dummy);
367
368
#ifdef CONFIG_ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION
369
static int __init reserve_crashkernel_low(unsigned long long low_size)
370
{
371
#ifdef CONFIG_64BIT
372
unsigned long long low_base;
373
374
low_base = memblock_phys_alloc_range(low_size, CRASH_ALIGN, 0, CRASH_ADDR_LOW_MAX);
375
if (!low_base) {
376
pr_err("cannot allocate crashkernel low memory (size:0x%llx).\n", low_size);
377
return -ENOMEM;
378
}
379
380
pr_info("crashkernel low memory reserved: 0x%08llx - 0x%08llx (%lld MB)\n",
381
low_base, low_base + low_size, low_size >> 20);
382
383
crashk_low_res.start = low_base;
384
crashk_low_res.end = low_base + low_size - 1;
385
#ifdef HAVE_ARCH_ADD_CRASH_RES_TO_IOMEM_EARLY
386
insert_resource(&iomem_resource, &crashk_low_res);
387
#endif
388
#endif
389
return 0;
390
}
391
392
void __init reserve_crashkernel_generic(unsigned long long crash_size,
393
unsigned long long crash_base,
394
unsigned long long crash_low_size,
395
bool high)
396
{
397
unsigned long long search_end = CRASH_ADDR_LOW_MAX, search_base = 0;
398
bool fixed_base = false;
399
400
/* User specifies base address explicitly. */
401
if (crash_base) {
402
fixed_base = true;
403
search_base = crash_base;
404
search_end = crash_base + crash_size;
405
} else if (high) {
406
search_base = CRASH_ADDR_LOW_MAX;
407
search_end = CRASH_ADDR_HIGH_MAX;
408
}
409
410
retry:
411
crash_base = memblock_phys_alloc_range(crash_size, CRASH_ALIGN,
412
search_base, search_end);
413
if (!crash_base) {
414
/*
415
* For crashkernel=size[KMG]@offset[KMG], print out failure
416
* message if can't reserve the specified region.
417
*/
418
if (fixed_base) {
419
pr_warn("crashkernel reservation failed - memory is in use.\n");
420
return;
421
}
422
423
/*
424
* For crashkernel=size[KMG], if the first attempt was for
425
* low memory, fall back to high memory, the minimum required
426
* low memory will be reserved later.
427
*/
428
if (!high && search_end == CRASH_ADDR_LOW_MAX) {
429
search_end = CRASH_ADDR_HIGH_MAX;
430
search_base = CRASH_ADDR_LOW_MAX;
431
crash_low_size = DEFAULT_CRASH_KERNEL_LOW_SIZE;
432
goto retry;
433
}
434
435
/*
436
* For crashkernel=size[KMG],high, if the first attempt was
437
* for high memory, fall back to low memory.
438
*/
439
if (high && search_end == CRASH_ADDR_HIGH_MAX) {
440
search_end = CRASH_ADDR_LOW_MAX;
441
search_base = 0;
442
if (search_end != CRASH_ADDR_HIGH_MAX)
443
goto retry;
444
}
445
pr_warn("cannot allocate crashkernel (size:0x%llx)\n",
446
crash_size);
447
return;
448
}
449
450
if ((crash_base >= CRASH_ADDR_LOW_MAX) &&
451
crash_low_size && reserve_crashkernel_low(crash_low_size)) {
452
memblock_phys_free(crash_base, crash_size);
453
return;
454
}
455
456
pr_info("crashkernel reserved: 0x%016llx - 0x%016llx (%lld MB)\n",
457
crash_base, crash_base + crash_size, crash_size >> 20);
458
459
/*
460
* The crashkernel memory will be removed from the kernel linear
461
* map. Inform kmemleak so that it won't try to access it.
462
*/
463
kmemleak_ignore_phys(crash_base);
464
if (crashk_low_res.end)
465
kmemleak_ignore_phys(crashk_low_res.start);
466
467
crashk_res.start = crash_base;
468
crashk_res.end = crash_base + crash_size - 1;
469
#ifdef HAVE_ARCH_ADD_CRASH_RES_TO_IOMEM_EARLY
470
insert_resource(&iomem_resource, &crashk_res);
471
#endif
472
}
473
474
struct range crashk_cma_ranges[CRASHKERNEL_CMA_RANGES_MAX];
475
#ifdef CRASHKERNEL_CMA
476
int crashk_cma_cnt;
477
void __init reserve_crashkernel_cma(unsigned long long cma_size)
478
{
479
unsigned long long request_size = roundup(cma_size, PAGE_SIZE);
480
unsigned long long reserved_size = 0;
481
482
if (!cma_size)
483
return;
484
485
while (cma_size > reserved_size &&
486
crashk_cma_cnt < CRASHKERNEL_CMA_RANGES_MAX) {
487
488
struct cma *res;
489
490
if (cma_declare_contiguous(0, request_size, 0, 0, 0, false,
491
"crashkernel", &res)) {
492
/* reservation failed, try half-sized blocks */
493
if (request_size <= PAGE_SIZE)
494
break;
495
496
request_size = roundup(request_size / 2, PAGE_SIZE);
497
continue;
498
}
499
500
crashk_cma_ranges[crashk_cma_cnt].start = cma_get_base(res);
501
crashk_cma_ranges[crashk_cma_cnt].end =
502
crashk_cma_ranges[crashk_cma_cnt].start +
503
cma_get_size(res) - 1;
504
++crashk_cma_cnt;
505
reserved_size += request_size;
506
}
507
508
if (cma_size > reserved_size)
509
pr_warn("crashkernel CMA reservation failed: %lld MB requested, %lld MB reserved in %d ranges\n",
510
cma_size >> 20, reserved_size >> 20, crashk_cma_cnt);
511
else
512
pr_info("crashkernel CMA reserved: %lld MB in %d ranges\n",
513
reserved_size >> 20, crashk_cma_cnt);
514
}
515
516
#else /* CRASHKERNEL_CMA */
517
void __init reserve_crashkernel_cma(unsigned long long cma_size)
518
{
519
if (cma_size)
520
pr_warn("crashkernel CMA reservation not supported\n");
521
}
522
#endif
523
524
#ifndef HAVE_ARCH_ADD_CRASH_RES_TO_IOMEM_EARLY
525
static __init int insert_crashkernel_resources(void)
526
{
527
if (crashk_res.start < crashk_res.end)
528
insert_resource(&iomem_resource, &crashk_res);
529
530
if (crashk_low_res.start < crashk_low_res.end)
531
insert_resource(&iomem_resource, &crashk_low_res);
532
533
return 0;
534
}
535
early_initcall(insert_crashkernel_resources);
536
#endif
537
#endif
538
539