1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Firmware-Assisted Dump support on POWER platform (OPAL). 4 * 5 * Copyright 2019, Hari Bathini, IBM Corporation. 6 */ 7 8 #define pr_fmt(fmt) "opal fadump: " fmt 9 10 #include <linux/string.h> 11 #include <linux/seq_file.h> 12 #include <linux/of_fdt.h> 13 #include <linux/libfdt.h> 14 #include <linux/mm.h> 15 16 #include <asm/page.h> 17 #include <asm/opal.h> 18 #include <asm/fadump-internal.h> 19 20 #include "opal-fadump.h" 21 22 static struct opal_fadump_mem_struct *opal_fdm; 23 24 /* Initialize kernel metadata */ 25 static void opal_fadump_init_metadata(struct opal_fadump_mem_struct *fdm) 26 { 27 fdm->version = OPAL_FADUMP_VERSION; 28 fdm->region_cnt = 0; 29 fdm->registered_regions = 0; 30 fdm->fadumphdr_addr = 0; 31 } 32 33 static u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf) 34 { 35 u64 addr = fadump_conf->reserve_dump_area_start; 36 37 opal_fdm = __va(fadump_conf->kernel_metadata); 38 opal_fadump_init_metadata(opal_fdm); 39 40 opal_fdm->region_cnt = 1; 41 opal_fdm->rgn[0].src = 0; 42 opal_fdm->rgn[0].dest = addr; 43 opal_fdm->rgn[0].size = fadump_conf->boot_memory_size; 44 addr += fadump_conf->boot_memory_size; 45 46 /* 47 * Kernel metadata is passed to f/w and retrieved in capture kerenl. 48 * So, use it to save fadump header address instead of calculating it. 49 */ 50 opal_fdm->fadumphdr_addr = (opal_fdm->rgn[0].dest + 51 fadump_conf->boot_memory_size); 52 53 return addr; 54 } 55 56 static u64 opal_fadump_get_metadata_size(void) 57 { 58 return PAGE_ALIGN(sizeof(struct opal_fadump_mem_struct)); 59 } 60 61 static int opal_fadump_setup_metadata(struct fw_dump *fadump_conf) 62 { 63 int err = 0; 64 s64 ret; 65 66 /* 67 * Use the last page(s) in FADump memory reservation for 68 * kernel metadata. 69 */ 70 fadump_conf->kernel_metadata = (fadump_conf->reserve_dump_area_start + 71 fadump_conf->reserve_dump_area_size - 72 opal_fadump_get_metadata_size()); 73 pr_info("Kernel metadata addr: %llx\n", fadump_conf->kernel_metadata); 74 75 /* Initialize kernel metadata before registering the address with f/w */ 76 opal_fdm = __va(fadump_conf->kernel_metadata); 77 opal_fadump_init_metadata(opal_fdm); 78 79 /* 80 * Register metadata address with f/w. Can be retrieved in 81 * the capture kernel. 82 */ 83 ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 84 fadump_conf->kernel_metadata); 85 if (ret != OPAL_SUCCESS) { 86 pr_err("Failed to set kernel metadata tag!\n"); 87 err = -EPERM; 88 } 89 90 return err; 91 } 92 93 static int opal_fadump_register(struct fw_dump *fadump_conf) 94 { 95 return -EIO; 96 } 97 98 static int opal_fadump_unregister(struct fw_dump *fadump_conf) 99 { 100 return -EIO; 101 } 102 103 static int opal_fadump_invalidate(struct fw_dump *fadump_conf) 104 { 105 return -EIO; 106 } 107 108 static int __init opal_fadump_process(struct fw_dump *fadump_conf) 109 { 110 return -EINVAL; 111 } 112 113 static void opal_fadump_region_show(struct fw_dump *fadump_conf, 114 struct seq_file *m) 115 { 116 const struct opal_fadump_mem_struct *fdm_ptr = opal_fdm; 117 u64 dumped_bytes = 0; 118 int i; 119 120 for (i = 0; i < fdm_ptr->region_cnt; i++) { 121 seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ", 122 fdm_ptr->rgn[i].src, fdm_ptr->rgn[i].dest); 123 seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n", 124 fdm_ptr->rgn[i].size, dumped_bytes); 125 } 126 } 127 128 static void opal_fadump_trigger(struct fadump_crash_info_header *fdh, 129 const char *msg) 130 { 131 int rc; 132 133 rc = opal_cec_reboot2(OPAL_REBOOT_MPIPL, msg); 134 if (rc == OPAL_UNSUPPORTED) { 135 pr_emerg("Reboot type %d not supported.\n", 136 OPAL_REBOOT_MPIPL); 137 } else if (rc == OPAL_HARDWARE) 138 pr_emerg("No backend support for MPIPL!\n"); 139 } 140 141 static struct fadump_ops opal_fadump_ops = { 142 .fadump_init_mem_struct = opal_fadump_init_mem_struct, 143 .fadump_get_metadata_size = opal_fadump_get_metadata_size, 144 .fadump_setup_metadata = opal_fadump_setup_metadata, 145 .fadump_register = opal_fadump_register, 146 .fadump_unregister = opal_fadump_unregister, 147 .fadump_invalidate = opal_fadump_invalidate, 148 .fadump_process = opal_fadump_process, 149 .fadump_region_show = opal_fadump_region_show, 150 .fadump_trigger = opal_fadump_trigger, 151 }; 152 153 void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node) 154 { 155 unsigned long dn; 156 157 /* 158 * Check if Firmware-Assisted Dump is supported. if yes, check 159 * if dump has been initiated on last reboot. 160 */ 161 dn = of_get_flat_dt_subnode_by_name(node, "dump"); 162 if (dn == -FDT_ERR_NOTFOUND) { 163 pr_debug("FADump support is missing!\n"); 164 return; 165 } 166 167 if (!of_flat_dt_is_compatible(dn, "ibm,opal-dump")) { 168 pr_err("Support missing for this f/w version!\n"); 169 return; 170 } 171 172 fadump_conf->ops = &opal_fadump_ops; 173 fadump_conf->fadump_supported = 1; 174 } 175