diff options
Diffstat (limited to 'arch/powerpc/kernel/fadump.c')
-rw-r--r-- | arch/powerpc/kernel/fadump.c | 154 |
1 files changed, 129 insertions, 25 deletions
diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 761b28b1427d..45a8d0be1c96 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/kobject.h> | 35 | #include <linux/kobject.h> |
36 | #include <linux/sysfs.h> | 36 | #include <linux/sysfs.h> |
37 | #include <linux/slab.h> | 37 | #include <linux/slab.h> |
38 | #include <linux/cma.h> | ||
38 | 39 | ||
39 | #include <asm/debugfs.h> | 40 | #include <asm/debugfs.h> |
40 | #include <asm/page.h> | 41 | #include <asm/page.h> |
@@ -46,6 +47,9 @@ | |||
46 | static struct fw_dump fw_dump; | 47 | static struct fw_dump fw_dump; |
47 | static struct fadump_mem_struct fdm; | 48 | static struct fadump_mem_struct fdm; |
48 | static const struct fadump_mem_struct *fdm_active; | 49 | static const struct fadump_mem_struct *fdm_active; |
50 | #ifdef CONFIG_CMA | ||
51 | static struct cma *fadump_cma; | ||
52 | #endif | ||
49 | 53 | ||
50 | static DEFINE_MUTEX(fadump_mutex); | 54 | static DEFINE_MUTEX(fadump_mutex); |
51 | struct fad_crash_memory_ranges *crash_memory_ranges; | 55 | struct fad_crash_memory_ranges *crash_memory_ranges; |
@@ -53,6 +57,67 @@ int crash_memory_ranges_size; | |||
53 | int crash_mem_ranges; | 57 | int crash_mem_ranges; |
54 | int max_crash_mem_ranges; | 58 | int max_crash_mem_ranges; |
55 | 59 | ||
60 | #ifdef CONFIG_CMA | ||
61 | /* | ||
62 | * fadump_cma_init() - Initialize CMA area from a fadump reserved memory | ||
63 | * | ||
64 | * This function initializes CMA area from fadump reserved memory. | ||
65 | * The total size of fadump reserved memory covers for boot memory size | ||
66 | * + cpu data size + hpte size and metadata. | ||
67 | * Initialize only the area equivalent to boot memory size for CMA use. | ||
68 | * The reamining portion of fadump reserved memory will be not given | ||
69 | * to CMA and pages for thoes will stay reserved. boot memory size is | ||
70 | * aligned per CMA requirement to satisy cma_init_reserved_mem() call. | ||
71 | * But for some reason even if it fails we still have the memory reservation | ||
72 | * with us and we can still continue doing fadump. | ||
73 | */ | ||
74 | int __init fadump_cma_init(void) | ||
75 | { | ||
76 | unsigned long long base, size; | ||
77 | int rc; | ||
78 | |||
79 | if (!fw_dump.fadump_enabled) | ||
80 | return 0; | ||
81 | |||
82 | /* | ||
83 | * Do not use CMA if user has provided fadump=nocma kernel parameter. | ||
84 | * Return 1 to continue with fadump old behaviour. | ||
85 | */ | ||
86 | if (fw_dump.nocma) | ||
87 | return 1; | ||
88 | |||
89 | base = fw_dump.reserve_dump_area_start; | ||
90 | size = fw_dump.boot_memory_size; | ||
91 | |||
92 | if (!size) | ||
93 | return 0; | ||
94 | |||
95 | rc = cma_init_reserved_mem(base, size, 0, "fadump_cma", &fadump_cma); | ||
96 | if (rc) { | ||
97 | pr_err("Failed to init cma area for firmware-assisted dump,%d\n", rc); | ||
98 | /* | ||
99 | * Though the CMA init has failed we still have memory | ||
100 | * reservation with us. The reserved memory will be | ||
101 | * blocked from production system usage. Hence return 1, | ||
102 | * so that we can continue with fadump. | ||
103 | */ | ||
104 | return 1; | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * So we now have successfully initialized cma area for fadump. | ||
109 | */ | ||
110 | pr_info("Initialized 0x%lx bytes cma area at %ldMB from 0x%lx " | ||
111 | "bytes of memory reserved for firmware-assisted dump\n", | ||
112 | cma_get_size(fadump_cma), | ||
113 | (unsigned long)cma_get_base(fadump_cma) >> 20, | ||
114 | fw_dump.reserve_dump_area_size); | ||
115 | return 1; | ||
116 | } | ||
117 | #else | ||
118 | static int __init fadump_cma_init(void) { return 1; } | ||
119 | #endif /* CONFIG_CMA */ | ||
120 | |||
56 | /* Scan the Firmware Assisted dump configuration details. */ | 121 | /* Scan the Firmware Assisted dump configuration details. */ |
57 | int __init early_init_dt_scan_fw_dump(unsigned long node, | 122 | int __init early_init_dt_scan_fw_dump(unsigned long node, |
58 | const char *uname, int depth, void *data) | 123 | const char *uname, int depth, void *data) |
@@ -118,13 +183,19 @@ int __init early_init_dt_scan_fw_dump(unsigned long node, | |||
118 | 183 | ||
119 | /* | 184 | /* |
120 | * If fadump is registered, check if the memory provided | 185 | * If fadump is registered, check if the memory provided |
121 | * falls within boot memory area. | 186 | * falls within boot memory area and reserved memory area. |
122 | */ | 187 | */ |
123 | int is_fadump_boot_memory_area(u64 addr, ulong size) | 188 | int is_fadump_memory_area(u64 addr, ulong size) |
124 | { | 189 | { |
190 | u64 d_start = fw_dump.reserve_dump_area_start; | ||
191 | u64 d_end = d_start + fw_dump.reserve_dump_area_size; | ||
192 | |||
125 | if (!fw_dump.dump_registered) | 193 | if (!fw_dump.dump_registered) |
126 | return 0; | 194 | return 0; |
127 | 195 | ||
196 | if (((addr + size) > d_start) && (addr <= d_end)) | ||
197 | return 1; | ||
198 | |||
128 | return (addr + size) > RMA_START && addr <= fw_dump.boot_memory_size; | 199 | return (addr + size) > RMA_START && addr <= fw_dump.boot_memory_size; |
129 | } | 200 | } |
130 | 201 | ||
@@ -172,6 +243,35 @@ static int is_boot_memory_area_contiguous(void) | |||
172 | return ret; | 243 | return ret; |
173 | } | 244 | } |
174 | 245 | ||
246 | /* | ||
247 | * Returns true, if there are no holes in reserved memory area, | ||
248 | * false otherwise. | ||
249 | */ | ||
250 | static bool is_reserved_memory_area_contiguous(void) | ||
251 | { | ||
252 | struct memblock_region *reg; | ||
253 | unsigned long start, end; | ||
254 | unsigned long d_start = fw_dump.reserve_dump_area_start; | ||
255 | unsigned long d_end = d_start + fw_dump.reserve_dump_area_size; | ||
256 | |||
257 | for_each_memblock(memory, reg) { | ||
258 | start = max(d_start, (unsigned long)reg->base); | ||
259 | end = min(d_end, (unsigned long)(reg->base + reg->size)); | ||
260 | if (d_start < end) { | ||
261 | /* Memory hole from d_start to start */ | ||
262 | if (start > d_start) | ||
263 | break; | ||
264 | |||
265 | if (end == d_end) | ||
266 | return true; | ||
267 | |||
268 | d_start = end + 1; | ||
269 | } | ||
270 | } | ||
271 | |||
272 | return false; | ||
273 | } | ||
274 | |||
175 | /* Print firmware assisted dump configurations for debugging purpose. */ | 275 | /* Print firmware assisted dump configurations for debugging purpose. */ |
176 | static void fadump_show_config(void) | 276 | static void fadump_show_config(void) |
177 | { | 277 | { |
@@ -378,8 +478,15 @@ int __init fadump_reserve_mem(void) | |||
378 | */ | 478 | */ |
379 | if (fdm_active) | 479 | if (fdm_active) |
380 | fw_dump.boot_memory_size = be64_to_cpu(fdm_active->rmr_region.source_len); | 480 | fw_dump.boot_memory_size = be64_to_cpu(fdm_active->rmr_region.source_len); |
381 | else | 481 | else { |
382 | fw_dump.boot_memory_size = fadump_calculate_reserve_size(); | 482 | fw_dump.boot_memory_size = fadump_calculate_reserve_size(); |
483 | #ifdef CONFIG_CMA | ||
484 | if (!fw_dump.nocma) | ||
485 | fw_dump.boot_memory_size = | ||
486 | ALIGN(fw_dump.boot_memory_size, | ||
487 | FADUMP_CMA_ALIGNMENT); | ||
488 | #endif | ||
489 | } | ||
383 | 490 | ||
384 | /* | 491 | /* |
385 | * Calculate the memory boundary. | 492 | * Calculate the memory boundary. |
@@ -426,8 +533,9 @@ int __init fadump_reserve_mem(void) | |||
426 | fw_dump.fadumphdr_addr = | 533 | fw_dump.fadumphdr_addr = |
427 | be64_to_cpu(fdm_active->rmr_region.destination_address) + | 534 | be64_to_cpu(fdm_active->rmr_region.destination_address) + |
428 | be64_to_cpu(fdm_active->rmr_region.source_len); | 535 | be64_to_cpu(fdm_active->rmr_region.source_len); |
429 | pr_debug("fadumphdr_addr = %p\n", | 536 | pr_debug("fadumphdr_addr = %pa\n", &fw_dump.fadumphdr_addr); |
430 | (void *) fw_dump.fadumphdr_addr); | 537 | fw_dump.reserve_dump_area_start = base; |
538 | fw_dump.reserve_dump_area_size = size; | ||
431 | } else { | 539 | } else { |
432 | size = get_fadump_area_size(); | 540 | size = get_fadump_area_size(); |
433 | 541 | ||
@@ -455,10 +563,11 @@ int __init fadump_reserve_mem(void) | |||
455 | (unsigned long)(size >> 20), | 563 | (unsigned long)(size >> 20), |
456 | (unsigned long)(base >> 20), | 564 | (unsigned long)(base >> 20), |
457 | (unsigned long)(memblock_phys_mem_size() >> 20)); | 565 | (unsigned long)(memblock_phys_mem_size() >> 20)); |
458 | } | ||
459 | 566 | ||
460 | fw_dump.reserve_dump_area_start = base; | 567 | fw_dump.reserve_dump_area_start = base; |
461 | fw_dump.reserve_dump_area_size = size; | 568 | fw_dump.reserve_dump_area_size = size; |
569 | return fadump_cma_init(); | ||
570 | } | ||
462 | return 1; | 571 | return 1; |
463 | } | 572 | } |
464 | 573 | ||
@@ -477,6 +586,10 @@ static int __init early_fadump_param(char *p) | |||
477 | fw_dump.fadump_enabled = 1; | 586 | fw_dump.fadump_enabled = 1; |
478 | else if (strncmp(p, "off", 3) == 0) | 587 | else if (strncmp(p, "off", 3) == 0) |
479 | fw_dump.fadump_enabled = 0; | 588 | fw_dump.fadump_enabled = 0; |
589 | else if (strncmp(p, "nocma", 5) == 0) { | ||
590 | fw_dump.fadump_enabled = 1; | ||
591 | fw_dump.nocma = 1; | ||
592 | } | ||
480 | 593 | ||
481 | return 0; | 594 | return 0; |
482 | } | 595 | } |
@@ -525,8 +638,10 @@ static int register_fw_dump(struct fadump_mem_struct *fdm) | |||
525 | break; | 638 | break; |
526 | case -3: | 639 | case -3: |
527 | if (!is_boot_memory_area_contiguous()) | 640 | if (!is_boot_memory_area_contiguous()) |
528 | pr_err("Can't have holes in boot memory area while " | 641 | pr_err("Can't have holes in boot memory area while registering fadump\n"); |
529 | "registering fadump\n"); | 642 | else if (!is_reserved_memory_area_contiguous()) |
643 | pr_err("Can't have holes in reserved memory area while" | ||
644 | " registering fadump\n"); | ||
530 | 645 | ||
531 | printk(KERN_ERR "Failed to register firmware-assisted kernel" | 646 | printk(KERN_ERR "Failed to register firmware-assisted kernel" |
532 | " dump. Parameter Error(%d).\n", rc); | 647 | " dump. Parameter Error(%d).\n", rc); |
@@ -1229,7 +1344,7 @@ static int fadump_unregister_dump(struct fadump_mem_struct *fdm) | |||
1229 | return 0; | 1344 | return 0; |
1230 | } | 1345 | } |
1231 | 1346 | ||
1232 | static int fadump_invalidate_dump(struct fadump_mem_struct *fdm) | 1347 | static int fadump_invalidate_dump(const struct fadump_mem_struct *fdm) |
1233 | { | 1348 | { |
1234 | int rc = 0; | 1349 | int rc = 0; |
1235 | unsigned int wait_time; | 1350 | unsigned int wait_time; |
@@ -1260,9 +1375,8 @@ void fadump_cleanup(void) | |||
1260 | { | 1375 | { |
1261 | /* Invalidate the registration only if dump is active. */ | 1376 | /* Invalidate the registration only if dump is active. */ |
1262 | if (fw_dump.dump_active) { | 1377 | if (fw_dump.dump_active) { |
1263 | init_fadump_mem_struct(&fdm, | 1378 | /* pass the same memory dump structure provided by platform */ |
1264 | be64_to_cpu(fdm_active->cpu_state_data.destination_address)); | 1379 | fadump_invalidate_dump(fdm_active); |
1265 | fadump_invalidate_dump(&fdm); | ||
1266 | } else if (fw_dump.dump_registered) { | 1380 | } else if (fw_dump.dump_registered) { |
1267 | /* Un-register Firmware-assisted dump if it was registered. */ | 1381 | /* Un-register Firmware-assisted dump if it was registered. */ |
1268 | fadump_unregister_dump(&fdm); | 1382 | fadump_unregister_dump(&fdm); |
@@ -1531,17 +1645,7 @@ static struct kobj_attribute fadump_register_attr = __ATTR(fadump_registered, | |||
1531 | 0644, fadump_register_show, | 1645 | 0644, fadump_register_show, |
1532 | fadump_register_store); | 1646 | fadump_register_store); |
1533 | 1647 | ||
1534 | static int fadump_region_open(struct inode *inode, struct file *file) | 1648 | DEFINE_SHOW_ATTRIBUTE(fadump_region); |
1535 | { | ||
1536 | return single_open(file, fadump_region_show, inode->i_private); | ||
1537 | } | ||
1538 | |||
1539 | static const struct file_operations fadump_region_fops = { | ||
1540 | .open = fadump_region_open, | ||
1541 | .read = seq_read, | ||
1542 | .llseek = seq_lseek, | ||
1543 | .release = single_release, | ||
1544 | }; | ||
1545 | 1649 | ||
1546 | static void fadump_init_files(void) | 1650 | static void fadump_init_files(void) |
1547 | { | 1651 | { |