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 #include <linux/crash_dump.h> 16 17 #include <asm/page.h> 18 #include <asm/opal.h> 19 #include <asm/fadump-internal.h> 20 21 #include "opal-fadump.h" 22 23 static const struct opal_fadump_mem_struct *opal_fdm_active; 24 static struct opal_fadump_mem_struct *opal_fdm; 25 26 static int opal_fadump_unregister(struct fw_dump *fadump_conf); 27 28 static void opal_fadump_update_config(struct fw_dump *fadump_conf, 29 const struct opal_fadump_mem_struct *fdm) 30 { 31 pr_debug("Boot memory regions count: %d\n", fdm->region_cnt); 32 33 /* 34 * The destination address of the first boot memory region is the 35 * destination address of boot memory regions. 36 */ 37 fadump_conf->boot_mem_dest_addr = fdm->rgn[0].dest; 38 pr_debug("Destination address of boot memory regions: %#016llx\n", 39 fadump_conf->boot_mem_dest_addr); 40 41 fadump_conf->fadumphdr_addr = fdm->fadumphdr_addr; 42 } 43 44 /* 45 * This function is called in the capture kernel to get configuration details 46 * from metadata setup by the first kernel. 47 */ 48 static void opal_fadump_get_config(struct fw_dump *fadump_conf, 49 const struct opal_fadump_mem_struct *fdm) 50 { 51 int i; 52 53 if (!fadump_conf->dump_active) 54 return; 55 56 fadump_conf->boot_memory_size = 0; 57 58 pr_debug("Boot memory regions:\n"); 59 for (i = 0; i < fdm->region_cnt; i++) { 60 pr_debug("\t%d. base: 0x%llx, size: 0x%llx\n", 61 (i + 1), fdm->rgn[i].src, fdm->rgn[i].size); 62 63 fadump_conf->boot_memory_size += fdm->rgn[i].size; 64 } 65 66 /* 67 * Start address of reserve dump area (permanent reservation) for 68 * re-registering FADump after dump capture. 69 */ 70 fadump_conf->reserve_dump_area_start = fdm->rgn[0].dest; 71 72 opal_fadump_update_config(fadump_conf, fdm); 73 } 74 75 /* Initialize kernel metadata */ 76 static void opal_fadump_init_metadata(struct opal_fadump_mem_struct *fdm) 77 { 78 fdm->version = OPAL_FADUMP_VERSION; 79 fdm->region_cnt = 0; 80 fdm->registered_regions = 0; 81 fdm->fadumphdr_addr = 0; 82 } 83 84 static u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf) 85 { 86 int max_copy_size, cur_size, size; 87 u64 src_addr, dest_addr; 88 89 opal_fdm = __va(fadump_conf->kernel_metadata); 90 opal_fadump_init_metadata(opal_fdm); 91 92 /* 93 * Firmware supports 32-bit field for size. Align it to PAGE_SIZE 94 * and request firmware to copy multiple kernel boot memory regions. 95 */ 96 max_copy_size = _ALIGN_DOWN(U32_MAX, PAGE_SIZE); 97 98 /* Boot memory regions */ 99 src_addr = 0; 100 dest_addr = fadump_conf->reserve_dump_area_start; 101 size = fadump_conf->boot_memory_size; 102 while (size) { 103 cur_size = size > max_copy_size ? max_copy_size : size; 104 105 opal_fdm->rgn[opal_fdm->region_cnt].src = src_addr; 106 opal_fdm->rgn[opal_fdm->region_cnt].dest = dest_addr; 107 opal_fdm->rgn[opal_fdm->region_cnt].size = cur_size; 108 109 opal_fdm->region_cnt++; 110 dest_addr += cur_size; 111 src_addr += cur_size; 112 size -= cur_size; 113 } 114 115 /* 116 * Kernel metadata is passed to f/w and retrieved in capture kerenl. 117 * So, use it to save fadump header address instead of calculating it. 118 */ 119 opal_fdm->fadumphdr_addr = (opal_fdm->rgn[0].dest + 120 fadump_conf->boot_memory_size); 121 122 opal_fadump_update_config(fadump_conf, opal_fdm); 123 124 return dest_addr; 125 } 126 127 static u64 opal_fadump_get_metadata_size(void) 128 { 129 return PAGE_ALIGN(sizeof(struct opal_fadump_mem_struct)); 130 } 131 132 static int opal_fadump_setup_metadata(struct fw_dump *fadump_conf) 133 { 134 int err = 0; 135 s64 ret; 136 137 /* 138 * Use the last page(s) in FADump memory reservation for 139 * kernel metadata. 140 */ 141 fadump_conf->kernel_metadata = (fadump_conf->reserve_dump_area_start + 142 fadump_conf->reserve_dump_area_size - 143 opal_fadump_get_metadata_size()); 144 pr_info("Kernel metadata addr: %llx\n", fadump_conf->kernel_metadata); 145 146 /* Initialize kernel metadata before registering the address with f/w */ 147 opal_fdm = __va(fadump_conf->kernel_metadata); 148 opal_fadump_init_metadata(opal_fdm); 149 150 /* 151 * Register metadata address with f/w. Can be retrieved in 152 * the capture kernel. 153 */ 154 ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 155 fadump_conf->kernel_metadata); 156 if (ret != OPAL_SUCCESS) { 157 pr_err("Failed to set kernel metadata tag!\n"); 158 err = -EPERM; 159 } 160 161 return err; 162 } 163 164 static int opal_fadump_register(struct fw_dump *fadump_conf) 165 { 166 s64 rc = OPAL_PARAMETER; 167 int i, err = -EIO; 168 169 for (i = 0; i < opal_fdm->region_cnt; i++) { 170 rc = opal_mpipl_update(OPAL_MPIPL_ADD_RANGE, 171 opal_fdm->rgn[i].src, 172 opal_fdm->rgn[i].dest, 173 opal_fdm->rgn[i].size); 174 if (rc != OPAL_SUCCESS) 175 break; 176 177 opal_fdm->registered_regions++; 178 } 179 180 switch (rc) { 181 case OPAL_SUCCESS: 182 pr_info("Registration is successful!\n"); 183 fadump_conf->dump_registered = 1; 184 err = 0; 185 break; 186 case OPAL_RESOURCE: 187 /* If MAX regions limit in f/w is hit, warn and proceed. */ 188 pr_warn("%d regions could not be registered for MPIPL as MAX limit is reached!\n", 189 (opal_fdm->region_cnt - opal_fdm->registered_regions)); 190 fadump_conf->dump_registered = 1; 191 err = 0; 192 break; 193 case OPAL_PARAMETER: 194 pr_err("Failed to register. Parameter Error(%lld).\n", rc); 195 break; 196 case OPAL_HARDWARE: 197 pr_err("Support not available.\n"); 198 fadump_conf->fadump_supported = 0; 199 fadump_conf->fadump_enabled = 0; 200 break; 201 default: 202 pr_err("Failed to register. Unknown Error(%lld).\n", rc); 203 break; 204 } 205 206 /* 207 * If some regions were registered before OPAL_MPIPL_ADD_RANGE 208 * OPAL call failed, unregister all regions. 209 */ 210 if ((err < 0) && (opal_fdm->registered_regions > 0)) 211 opal_fadump_unregister(fadump_conf); 212 213 return err; 214 } 215 216 static int opal_fadump_unregister(struct fw_dump *fadump_conf) 217 { 218 s64 rc; 219 220 rc = opal_mpipl_update(OPAL_MPIPL_REMOVE_ALL, 0, 0, 0); 221 if (rc) { 222 pr_err("Failed to un-register - unexpected Error(%lld).\n", rc); 223 return -EIO; 224 } 225 226 opal_fdm->registered_regions = 0; 227 fadump_conf->dump_registered = 0; 228 return 0; 229 } 230 231 static int opal_fadump_invalidate(struct fw_dump *fadump_conf) 232 { 233 return -EIO; 234 } 235 236 static void opal_fadump_cleanup(struct fw_dump *fadump_conf) 237 { 238 s64 ret; 239 240 ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 0); 241 if (ret != OPAL_SUCCESS) 242 pr_warn("Could not reset (%llu) kernel metadata tag!\n", ret); 243 } 244 245 /* 246 * Convert CPU state data saved at the time of crash into ELF notes. 247 * 248 * Append crashing CPU's register data saved by the kernel in the PT_NOTE. 249 */ 250 static int __init 251 opal_fadump_build_cpu_notes(struct fw_dump *fadump_conf, 252 struct fadump_crash_info_header *fdh) 253 { 254 u32 num_cpus = 1, *note_buf; 255 int rc; 256 257 if (fdh->crashing_cpu == FADUMP_CPU_UNKNOWN) 258 return -ENODEV; 259 260 /* Allocate CPU notes buffer to hold crashing cpu notes. */ 261 rc = fadump_setup_cpu_notes_buf(num_cpus); 262 if (rc != 0) 263 return rc; 264 265 note_buf = (u32 *)fadump_conf->cpu_notes_buf_vaddr; 266 note_buf = fadump_regs_to_elf_notes(note_buf, &(fdh->regs)); 267 final_note(note_buf); 268 269 pr_debug("Updating elfcore header (%llx) with cpu notes\n", 270 fdh->elfcorehdr_addr); 271 fadump_update_elfcore_header(__va(fdh->elfcorehdr_addr)); 272 return 0; 273 } 274 275 static int __init opal_fadump_process(struct fw_dump *fadump_conf) 276 { 277 struct fadump_crash_info_header *fdh; 278 int rc = -EINVAL; 279 280 if (!opal_fdm_active || !fadump_conf->fadumphdr_addr) 281 return rc; 282 283 /* Validate the fadump crash info header */ 284 fdh = __va(fadump_conf->fadumphdr_addr); 285 if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) { 286 pr_err("Crash info header is not valid.\n"); 287 return rc; 288 } 289 290 rc = opal_fadump_build_cpu_notes(fadump_conf, fdh); 291 if (rc) 292 return rc; 293 294 /* 295 * We are done validating dump info and elfcore header is now ready 296 * to be exported. set elfcorehdr_addr so that vmcore module will 297 * export the elfcore header through '/proc/vmcore'. 298 */ 299 elfcorehdr_addr = fdh->elfcorehdr_addr; 300 301 return rc; 302 } 303 304 static void opal_fadump_region_show(struct fw_dump *fadump_conf, 305 struct seq_file *m) 306 { 307 const struct opal_fadump_mem_struct *fdm_ptr; 308 u64 dumped_bytes = 0; 309 int i; 310 311 if (fadump_conf->dump_active) 312 fdm_ptr = opal_fdm_active; 313 else 314 fdm_ptr = opal_fdm; 315 316 for (i = 0; i < fdm_ptr->region_cnt; i++) { 317 /* 318 * Only regions that are registered for MPIPL 319 * would have dump data. 320 */ 321 if ((fadump_conf->dump_active) && 322 (i < fdm_ptr->registered_regions)) 323 dumped_bytes = fdm_ptr->rgn[i].size; 324 325 seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ", 326 fdm_ptr->rgn[i].src, fdm_ptr->rgn[i].dest); 327 seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n", 328 fdm_ptr->rgn[i].size, dumped_bytes); 329 } 330 331 /* Dump is active. Show reserved area start address. */ 332 if (fadump_conf->dump_active) { 333 seq_printf(m, "\nMemory above %#016lx is reserved for saving crash dump\n", 334 fadump_conf->reserve_dump_area_start); 335 } 336 } 337 338 static void opal_fadump_trigger(struct fadump_crash_info_header *fdh, 339 const char *msg) 340 { 341 int rc; 342 343 rc = opal_cec_reboot2(OPAL_REBOOT_MPIPL, msg); 344 if (rc == OPAL_UNSUPPORTED) { 345 pr_emerg("Reboot type %d not supported.\n", 346 OPAL_REBOOT_MPIPL); 347 } else if (rc == OPAL_HARDWARE) 348 pr_emerg("No backend support for MPIPL!\n"); 349 } 350 351 static struct fadump_ops opal_fadump_ops = { 352 .fadump_init_mem_struct = opal_fadump_init_mem_struct, 353 .fadump_get_metadata_size = opal_fadump_get_metadata_size, 354 .fadump_setup_metadata = opal_fadump_setup_metadata, 355 .fadump_register = opal_fadump_register, 356 .fadump_unregister = opal_fadump_unregister, 357 .fadump_invalidate = opal_fadump_invalidate, 358 .fadump_cleanup = opal_fadump_cleanup, 359 .fadump_process = opal_fadump_process, 360 .fadump_region_show = opal_fadump_region_show, 361 .fadump_trigger = opal_fadump_trigger, 362 }; 363 364 void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node) 365 { 366 const __be32 *prop; 367 unsigned long dn; 368 u64 addr = 0; 369 s64 ret; 370 371 372 /* 373 * Check if Firmware-Assisted Dump is supported. if yes, check 374 * if dump has been initiated on last reboot. 375 */ 376 dn = of_get_flat_dt_subnode_by_name(node, "dump"); 377 if (dn == -FDT_ERR_NOTFOUND) { 378 pr_debug("FADump support is missing!\n"); 379 return; 380 } 381 382 if (!of_flat_dt_is_compatible(dn, "ibm,opal-dump")) { 383 pr_err("Support missing for this f/w version!\n"); 384 return; 385 } 386 387 fadump_conf->ops = &opal_fadump_ops; 388 fadump_conf->fadump_supported = 1; 389 390 /* 391 * Check if dump has been initiated on last reboot. 392 */ 393 prop = of_get_flat_dt_prop(dn, "mpipl-boot", NULL); 394 if (!prop) 395 return; 396 397 ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_KERNEL, &addr); 398 if ((ret != OPAL_SUCCESS) || !addr) { 399 pr_err("Failed to get Kernel metadata (%lld)\n", ret); 400 return; 401 } 402 403 addr = be64_to_cpu(addr); 404 pr_debug("Kernel metadata addr: %llx\n", addr); 405 406 opal_fdm_active = __va(addr); 407 if (opal_fdm_active->version != OPAL_FADUMP_VERSION) { 408 pr_warn("Supported kernel metadata version: %u, found: %d!\n", 409 OPAL_FADUMP_VERSION, opal_fdm_active->version); 410 pr_warn("WARNING: Kernel metadata format mismatch identified! Core file maybe corrupted..\n"); 411 } 412 413 /* Kernel regions not registered with f/w for MPIPL */ 414 if (opal_fdm_active->registered_regions == 0) { 415 opal_fdm_active = NULL; 416 return; 417 } 418 419 pr_info("Firmware-assisted dump is active.\n"); 420 fadump_conf->dump_active = 1; 421 opal_fadump_get_config(fadump_conf, opal_fdm_active); 422 } 423