diff options
author | Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> | 2012-02-19 21:15:03 -0500 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-02-22 18:50:01 -0500 |
commit | 3ccc00a7e04ff7718c9aebb4b0c982571c798759 (patch) | |
tree | 8e3dd27ba9fcc6c504409e5e0f76a73dbd38479a /arch/powerpc/kernel/fadump.c | |
parent | eb39c8803d0e3d98fe74825f99287f63d55e6460 (diff) |
fadump: Register for firmware assisted dump.
On 2012-02-20 11:02:51 Mon, Paul Mackerras wrote:
> On Thu, Feb 16, 2012 at 04:44:30PM +0530, Mahesh J Salgaonkar wrote:
>
> If I have read the code correctly, we are going to get this printk on
> non-pSeries machines or on older pSeries machines, even if the user
> has not put the fadump=on option on the kernel command line. The
> printk will be annoying since there is no actual error condition. It
> seems to me that the condition for the printk should include
> fw_dump.fadump_enabled. In other words you should probably add
>
> if (!fw_dump.fadump_enabled)
> return 0;
>
> at the beginning of the function.
Hi Paul,
Thanks for pointing it out. Please find the updated patch below.
The existing patches above this (4/10 through 10/10) cleanly applies
on this update.
Thanks,
-Mahesh.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel/fadump.c')
-rw-r--r-- | arch/powerpc/kernel/fadump.c | 355 |
1 files changed, 352 insertions, 3 deletions
diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index deb276a9ce71..eb8f782afade 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c | |||
@@ -29,6 +29,9 @@ | |||
29 | 29 | ||
30 | #include <linux/string.h> | 30 | #include <linux/string.h> |
31 | #include <linux/memblock.h> | 31 | #include <linux/memblock.h> |
32 | #include <linux/delay.h> | ||
33 | #include <linux/debugfs.h> | ||
34 | #include <linux/seq_file.h> | ||
32 | 35 | ||
33 | #include <asm/page.h> | 36 | #include <asm/page.h> |
34 | #include <asm/prom.h> | 37 | #include <asm/prom.h> |
@@ -36,6 +39,10 @@ | |||
36 | #include <asm/fadump.h> | 39 | #include <asm/fadump.h> |
37 | 40 | ||
38 | static struct fw_dump fw_dump; | 41 | static struct fw_dump fw_dump; |
42 | static struct fadump_mem_struct fdm; | ||
43 | static const struct fadump_mem_struct *fdm_active; | ||
44 | |||
45 | static DEFINE_MUTEX(fadump_mutex); | ||
39 | 46 | ||
40 | /* Scan the Firmware Assisted dump configuration details. */ | 47 | /* Scan the Firmware Assisted dump configuration details. */ |
41 | int __init early_init_dt_scan_fw_dump(unsigned long node, | 48 | int __init early_init_dt_scan_fw_dump(unsigned long node, |
@@ -64,7 +71,8 @@ int __init early_init_dt_scan_fw_dump(unsigned long node, | |||
64 | * The 'ibm,kernel-dump' rtas node is present only if there is | 71 | * The 'ibm,kernel-dump' rtas node is present only if there is |
65 | * dump data waiting for us. | 72 | * dump data waiting for us. |
66 | */ | 73 | */ |
67 | if (of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL)) | 74 | fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL); |
75 | if (fdm_active) | ||
68 | fw_dump.dump_active = 1; | 76 | fw_dump.dump_active = 1; |
69 | 77 | ||
70 | /* Get the sizes required to store dump data for the firmware provided | 78 | /* Get the sizes required to store dump data for the firmware provided |
@@ -98,6 +106,85 @@ int __init early_init_dt_scan_fw_dump(unsigned long node, | |||
98 | return 1; | 106 | return 1; |
99 | } | 107 | } |
100 | 108 | ||
109 | int is_fadump_active(void) | ||
110 | { | ||
111 | return fw_dump.dump_active; | ||
112 | } | ||
113 | |||
114 | /* Print firmware assisted dump configurations for debugging purpose. */ | ||
115 | static void fadump_show_config(void) | ||
116 | { | ||
117 | pr_debug("Support for firmware-assisted dump (fadump): %s\n", | ||
118 | (fw_dump.fadump_supported ? "present" : "no support")); | ||
119 | |||
120 | if (!fw_dump.fadump_supported) | ||
121 | return; | ||
122 | |||
123 | pr_debug("Fadump enabled : %s\n", | ||
124 | (fw_dump.fadump_enabled ? "yes" : "no")); | ||
125 | pr_debug("Dump Active : %s\n", | ||
126 | (fw_dump.dump_active ? "yes" : "no")); | ||
127 | pr_debug("Dump section sizes:\n"); | ||
128 | pr_debug(" CPU state data size: %lx\n", fw_dump.cpu_state_data_size); | ||
129 | pr_debug(" HPTE region size : %lx\n", fw_dump.hpte_region_size); | ||
130 | pr_debug("Boot memory size : %lx\n", fw_dump.boot_memory_size); | ||
131 | } | ||
132 | |||
133 | static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm, | ||
134 | unsigned long addr) | ||
135 | { | ||
136 | if (!fdm) | ||
137 | return 0; | ||
138 | |||
139 | memset(fdm, 0, sizeof(struct fadump_mem_struct)); | ||
140 | addr = addr & PAGE_MASK; | ||
141 | |||
142 | fdm->header.dump_format_version = 0x00000001; | ||
143 | fdm->header.dump_num_sections = 3; | ||
144 | fdm->header.dump_status_flag = 0; | ||
145 | fdm->header.offset_first_dump_section = | ||
146 | (u32)offsetof(struct fadump_mem_struct, cpu_state_data); | ||
147 | |||
148 | /* | ||
149 | * Fields for disk dump option. | ||
150 | * We are not using disk dump option, hence set these fields to 0. | ||
151 | */ | ||
152 | fdm->header.dd_block_size = 0; | ||
153 | fdm->header.dd_block_offset = 0; | ||
154 | fdm->header.dd_num_blocks = 0; | ||
155 | fdm->header.dd_offset_disk_path = 0; | ||
156 | |||
157 | /* set 0 to disable an automatic dump-reboot. */ | ||
158 | fdm->header.max_time_auto = 0; | ||
159 | |||
160 | /* Kernel dump sections */ | ||
161 | /* cpu state data section. */ | ||
162 | fdm->cpu_state_data.request_flag = FADUMP_REQUEST_FLAG; | ||
163 | fdm->cpu_state_data.source_data_type = FADUMP_CPU_STATE_DATA; | ||
164 | fdm->cpu_state_data.source_address = 0; | ||
165 | fdm->cpu_state_data.source_len = fw_dump.cpu_state_data_size; | ||
166 | fdm->cpu_state_data.destination_address = addr; | ||
167 | addr += fw_dump.cpu_state_data_size; | ||
168 | |||
169 | /* hpte region section */ | ||
170 | fdm->hpte_region.request_flag = FADUMP_REQUEST_FLAG; | ||
171 | fdm->hpte_region.source_data_type = FADUMP_HPTE_REGION; | ||
172 | fdm->hpte_region.source_address = 0; | ||
173 | fdm->hpte_region.source_len = fw_dump.hpte_region_size; | ||
174 | fdm->hpte_region.destination_address = addr; | ||
175 | addr += fw_dump.hpte_region_size; | ||
176 | |||
177 | /* RMA region section */ | ||
178 | fdm->rmr_region.request_flag = FADUMP_REQUEST_FLAG; | ||
179 | fdm->rmr_region.source_data_type = FADUMP_REAL_MODE_REGION; | ||
180 | fdm->rmr_region.source_address = RMA_START; | ||
181 | fdm->rmr_region.source_len = fw_dump.boot_memory_size; | ||
182 | fdm->rmr_region.destination_address = addr; | ||
183 | addr += fw_dump.boot_memory_size; | ||
184 | |||
185 | return addr; | ||
186 | } | ||
187 | |||
101 | /** | 188 | /** |
102 | * fadump_calculate_reserve_size(): reserve variable boot area 5% of System RAM | 189 | * fadump_calculate_reserve_size(): reserve variable boot area 5% of System RAM |
103 | * | 190 | * |
@@ -166,8 +253,15 @@ int __init fadump_reserve_mem(void) | |||
166 | fw_dump.fadump_enabled = 0; | 253 | fw_dump.fadump_enabled = 0; |
167 | return 0; | 254 | return 0; |
168 | } | 255 | } |
169 | /* Initialize boot memory size */ | 256 | /* |
170 | fw_dump.boot_memory_size = fadump_calculate_reserve_size(); | 257 | * Initialize boot memory size |
258 | * If dump is active then we have already calculated the size during | ||
259 | * first kernel. | ||
260 | */ | ||
261 | if (fdm_active) | ||
262 | fw_dump.boot_memory_size = fdm_active->rmr_region.source_len; | ||
263 | else | ||
264 | fw_dump.boot_memory_size = fadump_calculate_reserve_size(); | ||
171 | 265 | ||
172 | /* | 266 | /* |
173 | * Calculate the memory boundary. | 267 | * Calculate the memory boundary. |
@@ -244,3 +338,258 @@ static int __init early_fadump_reserve_mem(char *p) | |||
244 | return 0; | 338 | return 0; |
245 | } | 339 | } |
246 | early_param("fadump_reserve_mem", early_fadump_reserve_mem); | 340 | early_param("fadump_reserve_mem", early_fadump_reserve_mem); |
341 | |||
342 | static void register_fw_dump(struct fadump_mem_struct *fdm) | ||
343 | { | ||
344 | int rc; | ||
345 | unsigned int wait_time; | ||
346 | |||
347 | pr_debug("Registering for firmware-assisted kernel dump...\n"); | ||
348 | |||
349 | /* TODO: Add upper time limit for the delay */ | ||
350 | do { | ||
351 | rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL, | ||
352 | FADUMP_REGISTER, fdm, | ||
353 | sizeof(struct fadump_mem_struct)); | ||
354 | |||
355 | wait_time = rtas_busy_delay_time(rc); | ||
356 | if (wait_time) | ||
357 | mdelay(wait_time); | ||
358 | |||
359 | } while (wait_time); | ||
360 | |||
361 | switch (rc) { | ||
362 | case -1: | ||
363 | printk(KERN_ERR "Failed to register firmware-assisted kernel" | ||
364 | " dump. Hardware Error(%d).\n", rc); | ||
365 | break; | ||
366 | case -3: | ||
367 | printk(KERN_ERR "Failed to register firmware-assisted kernel" | ||
368 | " dump. Parameter Error(%d).\n", rc); | ||
369 | break; | ||
370 | case -9: | ||
371 | printk(KERN_ERR "firmware-assisted kernel dump is already " | ||
372 | " registered."); | ||
373 | fw_dump.dump_registered = 1; | ||
374 | break; | ||
375 | case 0: | ||
376 | printk(KERN_INFO "firmware-assisted kernel dump registration" | ||
377 | " is successful\n"); | ||
378 | fw_dump.dump_registered = 1; | ||
379 | break; | ||
380 | } | ||
381 | } | ||
382 | |||
383 | static void register_fadump(void) | ||
384 | { | ||
385 | /* | ||
386 | * If no memory is reserved then we can not register for firmware- | ||
387 | * assisted dump. | ||
388 | */ | ||
389 | if (!fw_dump.reserve_dump_area_size) | ||
390 | return; | ||
391 | |||
392 | /* register the future kernel dump with firmware. */ | ||
393 | register_fw_dump(&fdm); | ||
394 | } | ||
395 | |||
396 | static int fadump_unregister_dump(struct fadump_mem_struct *fdm) | ||
397 | { | ||
398 | int rc = 0; | ||
399 | unsigned int wait_time; | ||
400 | |||
401 | pr_debug("Un-register firmware-assisted dump\n"); | ||
402 | |||
403 | /* TODO: Add upper time limit for the delay */ | ||
404 | do { | ||
405 | rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL, | ||
406 | FADUMP_UNREGISTER, fdm, | ||
407 | sizeof(struct fadump_mem_struct)); | ||
408 | |||
409 | wait_time = rtas_busy_delay_time(rc); | ||
410 | if (wait_time) | ||
411 | mdelay(wait_time); | ||
412 | } while (wait_time); | ||
413 | |||
414 | if (rc) { | ||
415 | printk(KERN_ERR "Failed to un-register firmware-assisted dump." | ||
416 | " unexpected error(%d).\n", rc); | ||
417 | return rc; | ||
418 | } | ||
419 | fw_dump.dump_registered = 0; | ||
420 | return 0; | ||
421 | } | ||
422 | |||
423 | static ssize_t fadump_enabled_show(struct kobject *kobj, | ||
424 | struct kobj_attribute *attr, | ||
425 | char *buf) | ||
426 | { | ||
427 | return sprintf(buf, "%d\n", fw_dump.fadump_enabled); | ||
428 | } | ||
429 | |||
430 | static ssize_t fadump_register_show(struct kobject *kobj, | ||
431 | struct kobj_attribute *attr, | ||
432 | char *buf) | ||
433 | { | ||
434 | return sprintf(buf, "%d\n", fw_dump.dump_registered); | ||
435 | } | ||
436 | |||
437 | static ssize_t fadump_register_store(struct kobject *kobj, | ||
438 | struct kobj_attribute *attr, | ||
439 | const char *buf, size_t count) | ||
440 | { | ||
441 | int ret = 0; | ||
442 | |||
443 | if (!fw_dump.fadump_enabled || fdm_active) | ||
444 | return -EPERM; | ||
445 | |||
446 | mutex_lock(&fadump_mutex); | ||
447 | |||
448 | switch (buf[0]) { | ||
449 | case '0': | ||
450 | if (fw_dump.dump_registered == 0) { | ||
451 | ret = -EINVAL; | ||
452 | goto unlock_out; | ||
453 | } | ||
454 | /* Un-register Firmware-assisted dump */ | ||
455 | fadump_unregister_dump(&fdm); | ||
456 | break; | ||
457 | case '1': | ||
458 | if (fw_dump.dump_registered == 1) { | ||
459 | ret = -EINVAL; | ||
460 | goto unlock_out; | ||
461 | } | ||
462 | /* Register Firmware-assisted dump */ | ||
463 | register_fadump(); | ||
464 | break; | ||
465 | default: | ||
466 | ret = -EINVAL; | ||
467 | break; | ||
468 | } | ||
469 | |||
470 | unlock_out: | ||
471 | mutex_unlock(&fadump_mutex); | ||
472 | return ret < 0 ? ret : count; | ||
473 | } | ||
474 | |||
475 | static int fadump_region_show(struct seq_file *m, void *private) | ||
476 | { | ||
477 | const struct fadump_mem_struct *fdm_ptr; | ||
478 | |||
479 | if (!fw_dump.fadump_enabled) | ||
480 | return 0; | ||
481 | |||
482 | if (fdm_active) | ||
483 | fdm_ptr = fdm_active; | ||
484 | else | ||
485 | fdm_ptr = &fdm; | ||
486 | |||
487 | seq_printf(m, | ||
488 | "CPU : [%#016llx-%#016llx] %#llx bytes, " | ||
489 | "Dumped: %#llx\n", | ||
490 | fdm_ptr->cpu_state_data.destination_address, | ||
491 | fdm_ptr->cpu_state_data.destination_address + | ||
492 | fdm_ptr->cpu_state_data.source_len - 1, | ||
493 | fdm_ptr->cpu_state_data.source_len, | ||
494 | fdm_ptr->cpu_state_data.bytes_dumped); | ||
495 | seq_printf(m, | ||
496 | "HPTE: [%#016llx-%#016llx] %#llx bytes, " | ||
497 | "Dumped: %#llx\n", | ||
498 | fdm_ptr->hpte_region.destination_address, | ||
499 | fdm_ptr->hpte_region.destination_address + | ||
500 | fdm_ptr->hpte_region.source_len - 1, | ||
501 | fdm_ptr->hpte_region.source_len, | ||
502 | fdm_ptr->hpte_region.bytes_dumped); | ||
503 | seq_printf(m, | ||
504 | "DUMP: [%#016llx-%#016llx] %#llx bytes, " | ||
505 | "Dumped: %#llx\n", | ||
506 | fdm_ptr->rmr_region.destination_address, | ||
507 | fdm_ptr->rmr_region.destination_address + | ||
508 | fdm_ptr->rmr_region.source_len - 1, | ||
509 | fdm_ptr->rmr_region.source_len, | ||
510 | fdm_ptr->rmr_region.bytes_dumped); | ||
511 | |||
512 | if (!fdm_active || | ||
513 | (fw_dump.reserve_dump_area_start == | ||
514 | fdm_ptr->cpu_state_data.destination_address)) | ||
515 | return 0; | ||
516 | |||
517 | /* Dump is active. Show reserved memory region. */ | ||
518 | seq_printf(m, | ||
519 | " : [%#016llx-%#016llx] %#llx bytes, " | ||
520 | "Dumped: %#llx\n", | ||
521 | (unsigned long long)fw_dump.reserve_dump_area_start, | ||
522 | fdm_ptr->cpu_state_data.destination_address - 1, | ||
523 | fdm_ptr->cpu_state_data.destination_address - | ||
524 | fw_dump.reserve_dump_area_start, | ||
525 | fdm_ptr->cpu_state_data.destination_address - | ||
526 | fw_dump.reserve_dump_area_start); | ||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | static struct kobj_attribute fadump_attr = __ATTR(fadump_enabled, | ||
531 | 0444, fadump_enabled_show, | ||
532 | NULL); | ||
533 | static struct kobj_attribute fadump_register_attr = __ATTR(fadump_registered, | ||
534 | 0644, fadump_register_show, | ||
535 | fadump_register_store); | ||
536 | |||
537 | static int fadump_region_open(struct inode *inode, struct file *file) | ||
538 | { | ||
539 | return single_open(file, fadump_region_show, inode->i_private); | ||
540 | } | ||
541 | |||
542 | static const struct file_operations fadump_region_fops = { | ||
543 | .open = fadump_region_open, | ||
544 | .read = seq_read, | ||
545 | .llseek = seq_lseek, | ||
546 | .release = single_release, | ||
547 | }; | ||
548 | |||
549 | static void fadump_init_files(void) | ||
550 | { | ||
551 | struct dentry *debugfs_file; | ||
552 | int rc = 0; | ||
553 | |||
554 | rc = sysfs_create_file(kernel_kobj, &fadump_attr.attr); | ||
555 | if (rc) | ||
556 | printk(KERN_ERR "fadump: unable to create sysfs file" | ||
557 | " fadump_enabled (%d)\n", rc); | ||
558 | |||
559 | rc = sysfs_create_file(kernel_kobj, &fadump_register_attr.attr); | ||
560 | if (rc) | ||
561 | printk(KERN_ERR "fadump: unable to create sysfs file" | ||
562 | " fadump_registered (%d)\n", rc); | ||
563 | |||
564 | debugfs_file = debugfs_create_file("fadump_region", 0444, | ||
565 | powerpc_debugfs_root, NULL, | ||
566 | &fadump_region_fops); | ||
567 | if (!debugfs_file) | ||
568 | printk(KERN_ERR "fadump: unable to create debugfs file" | ||
569 | " fadump_region\n"); | ||
570 | return; | ||
571 | } | ||
572 | |||
573 | /* | ||
574 | * Prepare for firmware-assisted dump. | ||
575 | */ | ||
576 | int __init setup_fadump(void) | ||
577 | { | ||
578 | if (!fw_dump.fadump_enabled) | ||
579 | return 0; | ||
580 | |||
581 | if (!fw_dump.fadump_supported) { | ||
582 | printk(KERN_ERR "Firmware-assisted dump is not supported on" | ||
583 | " this hardware\n"); | ||
584 | return 0; | ||
585 | } | ||
586 | |||
587 | fadump_show_config(); | ||
588 | /* Initialize the kernel dump memory structure for FAD registration. */ | ||
589 | if (fw_dump.reserve_dump_area_size) | ||
590 | init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start); | ||
591 | fadump_init_files(); | ||
592 | |||
593 | return 1; | ||
594 | } | ||
595 | subsys_initcall(setup_fadump); | ||