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 static int opal_fadump_unregister(struct fw_dump *fadump_conf); 25 26 static void opal_fadump_update_config(struct fw_dump *fadump_conf, 27 const struct opal_fadump_mem_struct *fdm) 28 { 29 /* 30 * The destination address of the first boot memory region is the 31 * destination address of boot memory regions. 32 */ 33 fadump_conf->boot_mem_dest_addr = fdm->rgn[0].dest; 34 pr_debug("Destination address of boot memory regions: %#016llx\n", 35 fadump_conf->boot_mem_dest_addr); 36 37 fadump_conf->fadumphdr_addr = fdm->fadumphdr_addr; 38 } 39 40 /* Initialize kernel metadata */ 41 static void opal_fadump_init_metadata(struct opal_fadump_mem_struct *fdm) 42 { 43 fdm->version = OPAL_FADUMP_VERSION; 44 fdm->region_cnt = 0; 45 fdm->registered_regions = 0; 46 fdm->fadumphdr_addr = 0; 47 } 48 49 static u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf) 50 { 51 u64 addr = fadump_conf->reserve_dump_area_start; 52 53 opal_fdm = __va(fadump_conf->kernel_metadata); 54 opal_fadump_init_metadata(opal_fdm); 55 56 opal_fdm->region_cnt = 1; 57 opal_fdm->rgn[0].src = 0; 58 opal_fdm->rgn[0].dest = addr; 59 opal_fdm->rgn[0].size = fadump_conf->boot_memory_size; 60 addr += fadump_conf->boot_memory_size; 61 62 /* 63 * Kernel metadata is passed to f/w and retrieved in capture kerenl. 64 * So, use it to save fadump header address instead of calculating it. 65 */ 66 opal_fdm->fadumphdr_addr = (opal_fdm->rgn[0].dest + 67 fadump_conf->boot_memory_size); 68 69 opal_fadump_update_config(fadump_conf, opal_fdm); 70 71 return addr; 72 } 73 74 static u64 opal_fadump_get_metadata_size(void) 75 { 76 return PAGE_ALIGN(sizeof(struct opal_fadump_mem_struct)); 77 } 78 79 static int opal_fadump_setup_metadata(struct fw_dump *fadump_conf) 80 { 81 int err = 0; 82 s64 ret; 83 84 /* 85 * Use the last page(s) in FADump memory reservation for 86 * kernel metadata. 87 */ 88 fadump_conf->kernel_metadata = (fadump_conf->reserve_dump_area_start + 89 fadump_conf->reserve_dump_area_size - 90 opal_fadump_get_metadata_size()); 91 pr_info("Kernel metadata addr: %llx\n", fadump_conf->kernel_metadata); 92 93 /* Initialize kernel metadata before registering the address with f/w */ 94 opal_fdm = __va(fadump_conf->kernel_metadata); 95 opal_fadump_init_metadata(opal_fdm); 96 97 /* 98 * Register metadata address with f/w. Can be retrieved in 99 * the capture kernel. 100 */ 101 ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 102 fadump_conf->kernel_metadata); 103 if (ret != OPAL_SUCCESS) { 104 pr_err("Failed to set kernel metadata tag!\n"); 105 err = -EPERM; 106 } 107 108 return err; 109 } 110 111 static int opal_fadump_register(struct fw_dump *fadump_conf) 112 { 113 s64 rc = OPAL_PARAMETER; 114 int i, err = -EIO; 115 116 for (i = 0; i < opal_fdm->region_cnt; i++) { 117 rc = opal_mpipl_update(OPAL_MPIPL_ADD_RANGE, 118 opal_fdm->rgn[i].src, 119 opal_fdm->rgn[i].dest, 120 opal_fdm->rgn[i].size); 121 if (rc != OPAL_SUCCESS) 122 break; 123 124 opal_fdm->registered_regions++; 125 } 126 127 switch (rc) { 128 case OPAL_SUCCESS: 129 pr_info("Registration is successful!\n"); 130 fadump_conf->dump_registered = 1; 131 err = 0; 132 break; 133 case OPAL_RESOURCE: 134 /* If MAX regions limit in f/w is hit, warn and proceed. */ 135 pr_warn("%d regions could not be registered for MPIPL as MAX limit is reached!\n", 136 (opal_fdm->region_cnt - opal_fdm->registered_regions)); 137 fadump_conf->dump_registered = 1; 138 err = 0; 139 break; 140 case OPAL_PARAMETER: 141 pr_err("Failed to register. Parameter Error(%lld).\n", rc); 142 break; 143 case OPAL_HARDWARE: 144 pr_err("Support not available.\n"); 145 fadump_conf->fadump_supported = 0; 146 fadump_conf->fadump_enabled = 0; 147 break; 148 default: 149 pr_err("Failed to register. Unknown Error(%lld).\n", rc); 150 break; 151 } 152 153 /* 154 * If some regions were registered before OPAL_MPIPL_ADD_RANGE 155 * OPAL call failed, unregister all regions. 156 */ 157 if ((err < 0) && (opal_fdm->registered_regions > 0)) 158 opal_fadump_unregister(fadump_conf); 159 160 return err; 161 } 162 163 static int opal_fadump_unregister(struct fw_dump *fadump_conf) 164 { 165 s64 rc; 166 167 rc = opal_mpipl_update(OPAL_MPIPL_REMOVE_ALL, 0, 0, 0); 168 if (rc) { 169 pr_err("Failed to un-register - unexpected Error(%lld).\n", rc); 170 return -EIO; 171 } 172 173 opal_fdm->registered_regions = 0; 174 fadump_conf->dump_registered = 0; 175 return 0; 176 } 177 178 static int opal_fadump_invalidate(struct fw_dump *fadump_conf) 179 { 180 return -EIO; 181 } 182 183 static void opal_fadump_cleanup(struct fw_dump *fadump_conf) 184 { 185 s64 ret; 186 187 ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 0); 188 if (ret != OPAL_SUCCESS) 189 pr_warn("Could not reset (%llu) kernel metadata tag!\n", ret); 190 } 191 192 static int __init opal_fadump_process(struct fw_dump *fadump_conf) 193 { 194 return -EINVAL; 195 } 196 197 static void opal_fadump_region_show(struct fw_dump *fadump_conf, 198 struct seq_file *m) 199 { 200 const struct opal_fadump_mem_struct *fdm_ptr = opal_fdm; 201 u64 dumped_bytes = 0; 202 int i; 203 204 for (i = 0; i < fdm_ptr->region_cnt; i++) { 205 seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ", 206 fdm_ptr->rgn[i].src, fdm_ptr->rgn[i].dest); 207 seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n", 208 fdm_ptr->rgn[i].size, dumped_bytes); 209 } 210 } 211 212 static void opal_fadump_trigger(struct fadump_crash_info_header *fdh, 213 const char *msg) 214 { 215 int rc; 216 217 rc = opal_cec_reboot2(OPAL_REBOOT_MPIPL, msg); 218 if (rc == OPAL_UNSUPPORTED) { 219 pr_emerg("Reboot type %d not supported.\n", 220 OPAL_REBOOT_MPIPL); 221 } else if (rc == OPAL_HARDWARE) 222 pr_emerg("No backend support for MPIPL!\n"); 223 } 224 225 static struct fadump_ops opal_fadump_ops = { 226 .fadump_init_mem_struct = opal_fadump_init_mem_struct, 227 .fadump_get_metadata_size = opal_fadump_get_metadata_size, 228 .fadump_setup_metadata = opal_fadump_setup_metadata, 229 .fadump_register = opal_fadump_register, 230 .fadump_unregister = opal_fadump_unregister, 231 .fadump_invalidate = opal_fadump_invalidate, 232 .fadump_cleanup = opal_fadump_cleanup, 233 .fadump_process = opal_fadump_process, 234 .fadump_region_show = opal_fadump_region_show, 235 .fadump_trigger = opal_fadump_trigger, 236 }; 237 238 void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node) 239 { 240 unsigned long dn; 241 242 /* 243 * Check if Firmware-Assisted Dump is supported. if yes, check 244 * if dump has been initiated on last reboot. 245 */ 246 dn = of_get_flat_dt_subnode_by_name(node, "dump"); 247 if (dn == -FDT_ERR_NOTFOUND) { 248 pr_debug("FADump support is missing!\n"); 249 return; 250 } 251 252 if (!of_flat_dt_is_compatible(dn, "ibm,opal-dump")) { 253 pr_err("Support missing for this f/w version!\n"); 254 return; 255 } 256 257 fadump_conf->ops = &opal_fadump_ops; 258 fadump_conf->fadump_supported = 1; 259 } 260