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 void opal_fadump_cleanup(struct fw_dump *fadump_conf) 109 { 110 s64 ret; 111 112 ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 0); 113 if (ret != OPAL_SUCCESS) 114 pr_warn("Could not reset (%llu) kernel metadata tag!\n", ret); 115 } 116 117 static int __init opal_fadump_process(struct fw_dump *fadump_conf) 118 { 119 return -EINVAL; 120 } 121 122 static void opal_fadump_region_show(struct fw_dump *fadump_conf, 123 struct seq_file *m) 124 { 125 const struct opal_fadump_mem_struct *fdm_ptr = opal_fdm; 126 u64 dumped_bytes = 0; 127 int i; 128 129 for (i = 0; i < fdm_ptr->region_cnt; i++) { 130 seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ", 131 fdm_ptr->rgn[i].src, fdm_ptr->rgn[i].dest); 132 seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n", 133 fdm_ptr->rgn[i].size, dumped_bytes); 134 } 135 } 136 137 static void opal_fadump_trigger(struct fadump_crash_info_header *fdh, 138 const char *msg) 139 { 140 int rc; 141 142 rc = opal_cec_reboot2(OPAL_REBOOT_MPIPL, msg); 143 if (rc == OPAL_UNSUPPORTED) { 144 pr_emerg("Reboot type %d not supported.\n", 145 OPAL_REBOOT_MPIPL); 146 } else if (rc == OPAL_HARDWARE) 147 pr_emerg("No backend support for MPIPL!\n"); 148 } 149 150 static struct fadump_ops opal_fadump_ops = { 151 .fadump_init_mem_struct = opal_fadump_init_mem_struct, 152 .fadump_get_metadata_size = opal_fadump_get_metadata_size, 153 .fadump_setup_metadata = opal_fadump_setup_metadata, 154 .fadump_register = opal_fadump_register, 155 .fadump_unregister = opal_fadump_unregister, 156 .fadump_invalidate = opal_fadump_invalidate, 157 .fadump_cleanup = opal_fadump_cleanup, 158 .fadump_process = opal_fadump_process, 159 .fadump_region_show = opal_fadump_region_show, 160 .fadump_trigger = opal_fadump_trigger, 161 }; 162 163 void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node) 164 { 165 unsigned long dn; 166 167 /* 168 * Check if Firmware-Assisted Dump is supported. if yes, check 169 * if dump has been initiated on last reboot. 170 */ 171 dn = of_get_flat_dt_subnode_by_name(node, "dump"); 172 if (dn == -FDT_ERR_NOTFOUND) { 173 pr_debug("FADump support is missing!\n"); 174 return; 175 } 176 177 if (!of_flat_dt_is_compatible(dn, "ibm,opal-dump")) { 178 pr_err("Support missing for this f/w version!\n"); 179 return; 180 } 181 182 fadump_conf->ops = &opal_fadump_ops; 183 fadump_conf->fadump_supported = 1; 184 } 185