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