Commit | Line | Data |
---|---|---|
d3833a70 HB |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * Firmware-Assisted Dump support on POWERVM platform. | |
4 | * | |
5 | * Copyright 2011, Mahesh Salgaonkar, IBM Corporation. | |
6 | * Copyright 2019, Hari Bathini, IBM Corporation. | |
7 | */ | |
8 | ||
9 | #define pr_fmt(fmt) "rtas fadump: " fmt | |
10 | ||
11 | #include <linux/string.h> | |
12 | #include <linux/memblock.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/seq_file.h> | |
15 | #include <linux/crash_dump.h> | |
e6f6390a CL |
16 | #include <linux/of.h> |
17 | #include <linux/of_fdt.h> | |
d3833a70 HB |
18 | |
19 | #include <asm/page.h> | |
d3833a70 | 20 | #include <asm/rtas.h> |
683eab94 | 21 | #include <asm/setup.h> |
d3833a70 HB |
22 | #include <asm/fadump.h> |
23 | #include <asm/fadump-internal.h> | |
24 | ||
25 | #include "rtas-fadump.h" | |
26 | ||
41a65d16 | 27 | static struct rtas_fadump_mem_struct fdm; |
f3512011 | 28 | static const struct rtas_fadump_mem_struct *fdm_active; |
41a65d16 HB |
29 | |
30 | static void rtas_fadump_update_config(struct fw_dump *fadump_conf, | |
31 | const struct rtas_fadump_mem_struct *fdm) | |
32 | { | |
41a65d16 HB |
33 | fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr + |
34 | fadump_conf->boot_memory_size); | |
35 | } | |
36 | ||
f3512011 HB |
37 | /* |
38 | * This function is called in the capture kernel to get configuration details | |
39 | * setup in the first kernel and passed to the f/w. | |
40 | */ | |
e14ff96d | 41 | static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf, |
f3512011 HB |
42 | const struct rtas_fadump_mem_struct *fdm) |
43 | { | |
78d5cc15 HB |
44 | unsigned long base, size, last_end, hole_size; |
45 | ||
46 | last_end = 0; | |
47 | hole_size = 0; | |
48 | fadump_conf->boot_memory_size = 0; | |
49 | fadump_conf->boot_mem_regs_cnt = 0; | |
50 | pr_debug("Boot memory regions:\n"); | |
51 | for (int i = 0; i < be16_to_cpu(fdm->header.dump_num_sections); i++) { | |
52 | int type = be16_to_cpu(fdm->rgn[i].source_data_type); | |
53 | u64 addr; | |
54 | ||
55 | switch (type) { | |
56 | case RTAS_FADUMP_CPU_STATE_DATA: | |
57 | addr = be64_to_cpu(fdm->rgn[i].destination_address); | |
58 | ||
59 | fadump_conf->cpu_state_dest_vaddr = (u64)__va(addr); | |
60 | /* | |
61 | * Start address of reserve dump area (permanent reservation) for | |
62 | * re-registering FADump after dump capture. | |
63 | */ | |
64 | fadump_conf->reserve_dump_area_start = addr; | |
65 | break; | |
66 | case RTAS_FADUMP_HPTE_REGION: | |
67 | /* Not processed currently. */ | |
68 | break; | |
69 | case RTAS_FADUMP_REAL_MODE_REGION: | |
70 | base = be64_to_cpu(fdm->rgn[i].source_address); | |
71 | size = be64_to_cpu(fdm->rgn[i].source_len); | |
72 | pr_debug("\t[%03d] base: 0x%lx, size: 0x%lx\n", i, base, size); | |
73 | if (!base) { | |
74 | fadump_conf->boot_mem_dest_addr = | |
75 | be64_to_cpu(fdm->rgn[i].destination_address); | |
76 | } | |
77 | ||
78 | fadump_conf->boot_mem_addr[fadump_conf->boot_mem_regs_cnt] = base; | |
79 | fadump_conf->boot_mem_sz[fadump_conf->boot_mem_regs_cnt] = size; | |
80 | fadump_conf->boot_memory_size += size; | |
81 | hole_size += (base - last_end); | |
82 | last_end = base + size; | |
83 | fadump_conf->boot_mem_regs_cnt++; | |
84 | break; | |
683eab94 HB |
85 | case RTAS_FADUMP_PARAM_AREA: |
86 | fadump_conf->param_area = be64_to_cpu(fdm->rgn[i].destination_address); | |
87 | break; | |
78d5cc15 HB |
88 | default: |
89 | pr_warn("Section type %d unsupported on this kernel. Ignoring!\n", type); | |
90 | break; | |
91 | } | |
92 | } | |
93 | fadump_conf->boot_mem_top = fadump_conf->boot_memory_size + hole_size; | |
f3512011 HB |
94 | |
95 | rtas_fadump_update_config(fadump_conf, fdm); | |
96 | } | |
97 | ||
d3833a70 HB |
98 | static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf) |
99 | { | |
41a65d16 | 100 | u64 addr = fadump_conf->reserve_dump_area_start; |
78d5cc15 | 101 | u16 sec_cnt = 0; |
41a65d16 HB |
102 | |
103 | memset(&fdm, 0, sizeof(struct rtas_fadump_mem_struct)); | |
104 | addr = addr & PAGE_MASK; | |
105 | ||
106 | fdm.header.dump_format_version = cpu_to_be32(0x00000001); | |
41a65d16 HB |
107 | fdm.header.dump_status_flag = 0; |
108 | fdm.header.offset_first_dump_section = | |
78d5cc15 | 109 | cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct, rgn)); |
41a65d16 HB |
110 | |
111 | /* | |
112 | * Fields for disk dump option. | |
113 | * We are not using disk dump option, hence set these fields to 0. | |
114 | */ | |
115 | fdm.header.dd_block_size = 0; | |
116 | fdm.header.dd_block_offset = 0; | |
117 | fdm.header.dd_num_blocks = 0; | |
118 | fdm.header.dd_offset_disk_path = 0; | |
119 | ||
120 | /* set 0 to disable an automatic dump-reboot. */ | |
121 | fdm.header.max_time_auto = 0; | |
122 | ||
123 | /* Kernel dump sections */ | |
124 | /* cpu state data section. */ | |
78d5cc15 HB |
125 | fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); |
126 | fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA); | |
127 | fdm.rgn[sec_cnt].source_address = 0; | |
128 | fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->cpu_state_data_size); | |
129 | fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr); | |
41a65d16 | 130 | addr += fadump_conf->cpu_state_data_size; |
78d5cc15 | 131 | sec_cnt++; |
41a65d16 HB |
132 | |
133 | /* hpte region section */ | |
78d5cc15 HB |
134 | fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); |
135 | fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_HPTE_REGION); | |
136 | fdm.rgn[sec_cnt].source_address = 0; | |
137 | fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->hpte_region_size); | |
138 | fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr); | |
41a65d16 | 139 | addr += fadump_conf->hpte_region_size; |
78d5cc15 | 140 | sec_cnt++; |
41a65d16 | 141 | |
9cf3b3a3 HB |
142 | /* |
143 | * Align boot memory area destination address to page boundary to | |
144 | * be able to mmap read this area in the vmcore. | |
145 | */ | |
146 | addr = PAGE_ALIGN(addr); | |
147 | ||
78d5cc15 HB |
148 | /* First boot memory region destination address */ |
149 | fadump_conf->boot_mem_dest_addr = addr; | |
150 | for (int i = 0; i < fadump_conf->boot_mem_regs_cnt; i++) { | |
151 | /* Boot memory regions */ | |
152 | fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); | |
153 | fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION); | |
154 | fdm.rgn[sec_cnt].source_address = cpu_to_be64(fadump_conf->boot_mem_addr[i]); | |
155 | fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->boot_mem_sz[i]); | |
156 | fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr); | |
157 | addr += fadump_conf->boot_mem_sz[i]; | |
158 | sec_cnt++; | |
159 | } | |
41a65d16 | 160 | |
683eab94 HB |
161 | /* Parameters area */ |
162 | if (fadump_conf->param_area) { | |
163 | fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); | |
164 | fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_PARAM_AREA); | |
165 | fdm.rgn[sec_cnt].source_address = cpu_to_be64(fadump_conf->param_area); | |
166 | fdm.rgn[sec_cnt].source_len = cpu_to_be64(COMMAND_LINE_SIZE); | |
167 | fdm.rgn[sec_cnt].destination_address = cpu_to_be64(fadump_conf->param_area); | |
168 | sec_cnt++; | |
169 | } | |
78d5cc15 | 170 | fdm.header.dump_num_sections = cpu_to_be16(sec_cnt); |
683eab94 | 171 | |
41a65d16 HB |
172 | rtas_fadump_update_config(fadump_conf, &fdm); |
173 | ||
174 | return addr; | |
d3833a70 HB |
175 | } |
176 | ||
7b1b3b48 HB |
177 | static u64 rtas_fadump_get_bootmem_min(void) |
178 | { | |
179 | return RTAS_FADUMP_MIN_BOOT_MEM; | |
180 | } | |
181 | ||
d3833a70 HB |
182 | static int rtas_fadump_register(struct fw_dump *fadump_conf) |
183 | { | |
78d5cc15 | 184 | unsigned int wait_time, fdm_size; |
41a65d16 HB |
185 | int rc, err = -EIO; |
186 | ||
78d5cc15 HB |
187 | /* |
188 | * Platform requires the exact size of the Dump Memory Structure. | |
189 | * Avoid including any unused rgns in the calculation, as this | |
190 | * could result in a parameter error (-3) from the platform. | |
191 | */ | |
192 | fdm_size = sizeof(struct rtas_fadump_section_header); | |
193 | fdm_size += be16_to_cpu(fdm.header.dump_num_sections) * sizeof(struct rtas_fadump_section); | |
194 | ||
41a65d16 HB |
195 | /* TODO: Add upper time limit for the delay */ |
196 | do { | |
197 | rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, | |
78d5cc15 | 198 | NULL, FADUMP_REGISTER, &fdm, fdm_size); |
41a65d16 HB |
199 | |
200 | wait_time = rtas_busy_delay_time(rc); | |
201 | if (wait_time) | |
202 | mdelay(wait_time); | |
203 | ||
204 | } while (wait_time); | |
205 | ||
206 | switch (rc) { | |
207 | case 0: | |
208 | pr_info("Registration is successful!\n"); | |
209 | fadump_conf->dump_registered = 1; | |
210 | err = 0; | |
211 | break; | |
212 | case -1: | |
213 | pr_err("Failed to register. Hardware Error(%d).\n", rc); | |
214 | break; | |
215 | case -3: | |
78d5cc15 | 216 | if (!is_fadump_reserved_mem_contiguous()) |
41a65d16 HB |
217 | pr_err("Can't have holes in reserved memory area.\n"); |
218 | ||
219 | pr_err("Failed to register. Parameter Error(%d).\n", rc); | |
220 | err = -EINVAL; | |
221 | break; | |
222 | case -9: | |
223 | pr_err("Already registered!\n"); | |
224 | fadump_conf->dump_registered = 1; | |
225 | err = -EEXIST; | |
226 | break; | |
227 | default: | |
228 | pr_err("Failed to register. Unknown Error(%d).\n", rc); | |
229 | break; | |
230 | } | |
231 | ||
232 | return err; | |
d3833a70 HB |
233 | } |
234 | ||
235 | static int rtas_fadump_unregister(struct fw_dump *fadump_conf) | |
236 | { | |
41a65d16 HB |
237 | unsigned int wait_time; |
238 | int rc; | |
239 | ||
240 | /* TODO: Add upper time limit for the delay */ | |
241 | do { | |
242 | rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, | |
243 | NULL, FADUMP_UNREGISTER, &fdm, | |
244 | sizeof(struct rtas_fadump_mem_struct)); | |
245 | ||
246 | wait_time = rtas_busy_delay_time(rc); | |
247 | if (wait_time) | |
248 | mdelay(wait_time); | |
249 | } while (wait_time); | |
250 | ||
251 | if (rc) { | |
252 | pr_err("Failed to un-register - unexpected error(%d).\n", rc); | |
253 | return -EIO; | |
254 | } | |
255 | ||
256 | fadump_conf->dump_registered = 0; | |
257 | return 0; | |
d3833a70 HB |
258 | } |
259 | ||
260 | static int rtas_fadump_invalidate(struct fw_dump *fadump_conf) | |
261 | { | |
f3512011 HB |
262 | unsigned int wait_time; |
263 | int rc; | |
264 | ||
265 | /* TODO: Add upper time limit for the delay */ | |
266 | do { | |
267 | rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, | |
268 | NULL, FADUMP_INVALIDATE, fdm_active, | |
269 | sizeof(struct rtas_fadump_mem_struct)); | |
270 | ||
271 | wait_time = rtas_busy_delay_time(rc); | |
272 | if (wait_time) | |
273 | mdelay(wait_time); | |
274 | } while (wait_time); | |
275 | ||
276 | if (rc) { | |
277 | pr_err("Failed to invalidate - unexpected error (%d).\n", rc); | |
278 | return -EIO; | |
279 | } | |
280 | ||
281 | fadump_conf->dump_active = 0; | |
282 | fdm_active = NULL; | |
283 | return 0; | |
284 | } | |
285 | ||
286 | #define RTAS_FADUMP_GPR_MASK 0xffffff0000000000 | |
287 | static inline int rtas_fadump_gpr_index(u64 id) | |
288 | { | |
289 | char str[3]; | |
290 | int i = -1; | |
291 | ||
292 | if ((id & RTAS_FADUMP_GPR_MASK) == fadump_str_to_u64("GPR")) { | |
293 | /* get the digits at the end */ | |
294 | id &= ~RTAS_FADUMP_GPR_MASK; | |
295 | id >>= 24; | |
296 | str[2] = '\0'; | |
297 | str[1] = id & 0xff; | |
298 | str[0] = (id >> 8) & 0xff; | |
299 | if (kstrtoint(str, 10, &i)) | |
300 | i = -EINVAL; | |
301 | if (i > 31) | |
302 | i = -1; | |
303 | } | |
304 | return i; | |
305 | } | |
306 | ||
e14ff96d | 307 | static void __init rtas_fadump_set_regval(struct pt_regs *regs, u64 reg_id, u64 reg_val) |
f3512011 HB |
308 | { |
309 | int i; | |
310 | ||
311 | i = rtas_fadump_gpr_index(reg_id); | |
312 | if (i >= 0) | |
313 | regs->gpr[i] = (unsigned long)reg_val; | |
314 | else if (reg_id == fadump_str_to_u64("NIA")) | |
315 | regs->nip = (unsigned long)reg_val; | |
316 | else if (reg_id == fadump_str_to_u64("MSR")) | |
317 | regs->msr = (unsigned long)reg_val; | |
318 | else if (reg_id == fadump_str_to_u64("CTR")) | |
319 | regs->ctr = (unsigned long)reg_val; | |
320 | else if (reg_id == fadump_str_to_u64("LR")) | |
321 | regs->link = (unsigned long)reg_val; | |
322 | else if (reg_id == fadump_str_to_u64("XER")) | |
323 | regs->xer = (unsigned long)reg_val; | |
324 | else if (reg_id == fadump_str_to_u64("CR")) | |
325 | regs->ccr = (unsigned long)reg_val; | |
326 | else if (reg_id == fadump_str_to_u64("DAR")) | |
327 | regs->dar = (unsigned long)reg_val; | |
328 | else if (reg_id == fadump_str_to_u64("DSISR")) | |
329 | regs->dsisr = (unsigned long)reg_val; | |
330 | } | |
331 | ||
e14ff96d | 332 | static struct rtas_fadump_reg_entry* __init |
f3512011 HB |
333 | rtas_fadump_read_regs(struct rtas_fadump_reg_entry *reg_entry, |
334 | struct pt_regs *regs) | |
335 | { | |
336 | memset(regs, 0, sizeof(struct pt_regs)); | |
337 | ||
338 | while (be64_to_cpu(reg_entry->reg_id) != fadump_str_to_u64("CPUEND")) { | |
339 | rtas_fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id), | |
340 | be64_to_cpu(reg_entry->reg_value)); | |
341 | reg_entry++; | |
342 | } | |
343 | reg_entry++; | |
344 | return reg_entry; | |
345 | } | |
346 | ||
347 | /* | |
348 | * Read CPU state dump data and convert it into ELF notes. | |
349 | * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be | |
350 | * used to access the data to allow for additional fields to be added without | |
351 | * affecting compatibility. Each list of registers for a CPU starts with | |
352 | * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes, | |
353 | * 8 Byte ASCII identifier and 8 Byte register value. The register entry | |
354 | * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part | |
355 | * of register value. For more details refer to PAPR document. | |
356 | * | |
357 | * Only for the crashing cpu we ignore the CPU dump data and get exact | |
358 | * state from fadump crash info structure populated by first kernel at the | |
359 | * time of crash. | |
360 | */ | |
361 | static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf) | |
362 | { | |
363 | struct rtas_fadump_reg_save_area_header *reg_header; | |
364 | struct fadump_crash_info_header *fdh = NULL; | |
365 | struct rtas_fadump_reg_entry *reg_entry; | |
366 | u32 num_cpus, *note_buf; | |
367 | int i, rc = 0, cpu = 0; | |
368 | struct pt_regs regs; | |
f3512011 HB |
369 | void *vaddr; |
370 | ||
78d5cc15 | 371 | vaddr = (void *)fadump_conf->cpu_state_dest_vaddr; |
f3512011 HB |
372 | |
373 | reg_header = vaddr; | |
374 | if (be64_to_cpu(reg_header->magic_number) != | |
375 | fadump_str_to_u64("REGSAVE")) { | |
376 | pr_err("Unable to read register save area.\n"); | |
377 | return -ENOENT; | |
378 | } | |
379 | ||
380 | pr_debug("--------CPU State Data------------\n"); | |
381 | pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number)); | |
382 | pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset)); | |
383 | ||
384 | vaddr += be32_to_cpu(reg_header->num_cpu_offset); | |
385 | num_cpus = be32_to_cpu(*((__be32 *)(vaddr))); | |
386 | pr_debug("NumCpus : %u\n", num_cpus); | |
387 | vaddr += sizeof(u32); | |
388 | reg_entry = (struct rtas_fadump_reg_entry *)vaddr; | |
389 | ||
390 | rc = fadump_setup_cpu_notes_buf(num_cpus); | |
391 | if (rc != 0) | |
392 | return rc; | |
393 | ||
394 | note_buf = (u32 *)fadump_conf->cpu_notes_buf_vaddr; | |
395 | ||
396 | if (fadump_conf->fadumphdr_addr) | |
397 | fdh = __va(fadump_conf->fadumphdr_addr); | |
398 | ||
399 | for (i = 0; i < num_cpus; i++) { | |
400 | if (be64_to_cpu(reg_entry->reg_id) != | |
401 | fadump_str_to_u64("CPUSTRT")) { | |
402 | pr_err("Unable to read CPU state data\n"); | |
403 | rc = -ENOENT; | |
404 | goto error_out; | |
405 | } | |
406 | /* Lower 4 bytes of reg_value contains logical cpu id */ | |
407 | cpu = (be64_to_cpu(reg_entry->reg_value) & | |
408 | RTAS_FADUMP_CPU_ID_MASK); | |
6584cec0 | 409 | if (fdh && !cpumask_test_cpu(cpu, &fdh->cpu_mask)) { |
f3512011 HB |
410 | RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry); |
411 | continue; | |
412 | } | |
413 | pr_debug("Reading register data for cpu %d...\n", cpu); | |
414 | if (fdh && fdh->crashing_cpu == cpu) { | |
415 | regs = fdh->regs; | |
416 | note_buf = fadump_regs_to_elf_notes(note_buf, ®s); | |
417 | RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry); | |
418 | } else { | |
419 | reg_entry++; | |
420 | reg_entry = rtas_fadump_read_regs(reg_entry, ®s); | |
421 | note_buf = fadump_regs_to_elf_notes(note_buf, ®s); | |
422 | } | |
423 | } | |
424 | final_note(note_buf); | |
425 | ||
c6c5b14d SJ |
426 | pr_debug("Updating elfcore header (%llx) with cpu notes\n", fadump_conf->elfcorehdr_addr); |
427 | fadump_update_elfcore_header((char *)fadump_conf->elfcorehdr_addr); | |
f3512011 HB |
428 | return 0; |
429 | ||
430 | error_out: | |
431 | fadump_free_cpu_notes_buf(); | |
432 | return rc; | |
433 | ||
d3833a70 HB |
434 | } |
435 | ||
436 | /* | |
c6c5b14d SJ |
437 | * Validate and process the dump data stored by the firmware, and update |
438 | * the CPU notes of elfcorehdr. | |
d3833a70 HB |
439 | */ |
440 | static int __init rtas_fadump_process(struct fw_dump *fadump_conf) | |
441 | { | |
f3512011 HB |
442 | if (!fdm_active || !fadump_conf->fadumphdr_addr) |
443 | return -EINVAL; | |
444 | ||
445 | /* Check if the dump data is valid. */ | |
78d5cc15 HB |
446 | for (int i = 0; i < be16_to_cpu(fdm_active->header.dump_num_sections); i++) { |
447 | int type = be16_to_cpu(fdm_active->rgn[i].source_data_type); | |
448 | int rc = 0; | |
449 | ||
450 | switch (type) { | |
451 | case RTAS_FADUMP_CPU_STATE_DATA: | |
452 | case RTAS_FADUMP_HPTE_REGION: | |
453 | case RTAS_FADUMP_REAL_MODE_REGION: | |
454 | if (fdm_active->rgn[i].error_flags != 0) { | |
455 | pr_err("Dump taken by platform is not valid (%d)\n", i); | |
456 | rc = -EINVAL; | |
457 | } | |
458 | if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len) { | |
459 | pr_err("Dump taken by platform is incomplete (%d)\n", i); | |
460 | rc = -EINVAL; | |
461 | } | |
462 | if (rc) { | |
463 | pr_warn("Region type: %u src addr: 0x%llx dest addr: 0x%llx\n", | |
464 | be16_to_cpu(fdm_active->rgn[i].source_data_type), | |
465 | be64_to_cpu(fdm_active->rgn[i].source_address), | |
466 | be64_to_cpu(fdm_active->rgn[i].destination_address)); | |
467 | return rc; | |
468 | } | |
469 | break; | |
683eab94 HB |
470 | case RTAS_FADUMP_PARAM_AREA: |
471 | if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len || | |
472 | fdm_active->rgn[i].error_flags != 0) { | |
473 | pr_warn("Failed to process additional parameters! Proceeding anyway..\n"); | |
474 | fadump_conf->param_area = 0; | |
475 | } | |
476 | break; | |
78d5cc15 HB |
477 | default: |
478 | /* | |
479 | * If the first/crashed kernel added a new region type that the | |
480 | * second/fadump kernel doesn't recognize, skip it and process | |
481 | * assuming backward compatibility. | |
482 | */ | |
483 | pr_warn("Unknown region found: type: %u src addr: 0x%llx dest addr: 0x%llx\n", | |
484 | be16_to_cpu(fdm_active->rgn[i].source_data_type), | |
485 | be64_to_cpu(fdm_active->rgn[i].source_address), | |
486 | be64_to_cpu(fdm_active->rgn[i].destination_address)); | |
487 | break; | |
488 | } | |
f3512011 HB |
489 | } |
490 | ||
c6c5b14d | 491 | return rtas_fadump_build_cpu_notes(fadump_conf); |
d3833a70 HB |
492 | } |
493 | ||
494 | static void rtas_fadump_region_show(struct fw_dump *fadump_conf, | |
495 | struct seq_file *m) | |
496 | { | |
f3512011 HB |
497 | const struct rtas_fadump_mem_struct *fdm_ptr; |
498 | ||
499 | if (fdm_active) | |
500 | fdm_ptr = fdm_active; | |
501 | else | |
502 | fdm_ptr = &fdm; | |
41a65d16 | 503 | |
78d5cc15 HB |
504 | |
505 | for (int i = 0; i < be16_to_cpu(fdm_ptr->header.dump_num_sections); i++) { | |
506 | int type = be16_to_cpu(fdm_ptr->rgn[i].source_data_type); | |
507 | ||
508 | switch (type) { | |
509 | case RTAS_FADUMP_CPU_STATE_DATA: | |
510 | seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n", | |
511 | be64_to_cpu(fdm_ptr->rgn[i].destination_address), | |
512 | be64_to_cpu(fdm_ptr->rgn[i].destination_address) + | |
513 | be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1, | |
514 | be64_to_cpu(fdm_ptr->rgn[i].source_len), | |
515 | be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped)); | |
516 | break; | |
517 | case RTAS_FADUMP_HPTE_REGION: | |
518 | seq_printf(m, "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n", | |
519 | be64_to_cpu(fdm_ptr->rgn[i].destination_address), | |
520 | be64_to_cpu(fdm_ptr->rgn[i].destination_address) + | |
521 | be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1, | |
522 | be64_to_cpu(fdm_ptr->rgn[i].source_len), | |
523 | be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped)); | |
524 | break; | |
525 | case RTAS_FADUMP_REAL_MODE_REGION: | |
526 | seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ", | |
527 | be64_to_cpu(fdm_ptr->rgn[i].source_address), | |
528 | be64_to_cpu(fdm_ptr->rgn[i].destination_address)); | |
529 | seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n", | |
530 | be64_to_cpu(fdm_ptr->rgn[i].source_len), | |
531 | be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped)); | |
532 | break; | |
683eab94 HB |
533 | case RTAS_FADUMP_PARAM_AREA: |
534 | seq_printf(m, "\n[%#016llx-%#016llx]: cmdline append: '%s'\n", | |
535 | be64_to_cpu(fdm_ptr->rgn[i].destination_address), | |
536 | be64_to_cpu(fdm_ptr->rgn[i].destination_address) + | |
537 | be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1, | |
538 | (char *)__va(be64_to_cpu(fdm_ptr->rgn[i].destination_address))); | |
539 | break; | |
78d5cc15 HB |
540 | default: |
541 | seq_printf(m, "Unknown region type %d : Src: %#016llx, Dest: %#016llx, ", | |
542 | type, be64_to_cpu(fdm_ptr->rgn[i].source_address), | |
543 | be64_to_cpu(fdm_ptr->rgn[i].destination_address)); | |
544 | break; | |
545 | } | |
546 | } | |
f3512011 | 547 | |
a3ceb588 | 548 | /* Dump is active. Show preserved area start address. */ |
f3512011 | 549 | if (fdm_active) { |
a3ceb588 HB |
550 | seq_printf(m, "\nMemory above %#016llx is reserved for saving crash dump\n", |
551 | fadump_conf->boot_mem_top); | |
f3512011 | 552 | } |
d3833a70 HB |
553 | } |
554 | ||
555 | static void rtas_fadump_trigger(struct fadump_crash_info_header *fdh, | |
556 | const char *msg) | |
557 | { | |
558 | /* Call ibm,os-term rtas call to trigger firmware assisted dump */ | |
559 | rtas_os_term((char *)msg); | |
560 | } | |
561 | ||
78d5cc15 HB |
562 | /* FADUMP_MAX_MEM_REGS or lower */ |
563 | static int rtas_fadump_max_boot_mem_rgns(void) | |
564 | { | |
565 | /* | |
566 | * Version 1 of Kernel Assisted Dump Memory Structure (PAPR) supports 10 sections. | |
567 | * With one each section taken for CPU state data & HPTE respectively, 8 sections | |
568 | * can be used for boot memory regions. | |
569 | * | |
570 | * If new region(s) is(are) defined, maximum boot memory regions will decrease | |
571 | * proportionally. | |
572 | */ | |
573 | return RTAS_FADUMP_MAX_BOOT_MEM_REGS; | |
574 | } | |
575 | ||
d3833a70 HB |
576 | static struct fadump_ops rtas_fadump_ops = { |
577 | .fadump_init_mem_struct = rtas_fadump_init_mem_struct, | |
7b1b3b48 | 578 | .fadump_get_bootmem_min = rtas_fadump_get_bootmem_min, |
d3833a70 HB |
579 | .fadump_register = rtas_fadump_register, |
580 | .fadump_unregister = rtas_fadump_unregister, | |
581 | .fadump_invalidate = rtas_fadump_invalidate, | |
582 | .fadump_process = rtas_fadump_process, | |
583 | .fadump_region_show = rtas_fadump_region_show, | |
584 | .fadump_trigger = rtas_fadump_trigger, | |
78d5cc15 | 585 | .fadump_max_boot_mem_rgns = rtas_fadump_max_boot_mem_rgns, |
d3833a70 HB |
586 | }; |
587 | ||
588 | void __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node) | |
589 | { | |
590 | int i, size, num_sections; | |
591 | const __be32 *sections; | |
592 | const __be32 *token; | |
593 | ||
594 | /* | |
595 | * Check if Firmware Assisted dump is supported. if yes, check | |
596 | * if dump has been initiated on last reboot. | |
597 | */ | |
598 | token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL); | |
599 | if (!token) | |
600 | return; | |
601 | ||
683eab94 HB |
602 | fadump_conf->ibm_configure_kernel_dump = be32_to_cpu(*token); |
603 | fadump_conf->ops = &rtas_fadump_ops; | |
604 | fadump_conf->fadump_supported = 1; | |
605 | fadump_conf->param_area_supported = 1; | |
d3833a70 | 606 | |
7dee93a9 | 607 | /* Firmware supports 64-bit value for size, align it to pagesize. */ |
e96d904e | 608 | fadump_conf->max_copy_size = ALIGN_DOWN(U64_MAX, PAGE_SIZE); |
7dee93a9 | 609 | |
f3512011 HB |
610 | /* |
611 | * The 'ibm,kernel-dump' rtas node is present only if there is | |
612 | * dump data waiting for us. | |
613 | */ | |
614 | fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL); | |
615 | if (fdm_active) { | |
616 | pr_info("Firmware-assisted dump is active.\n"); | |
617 | fadump_conf->dump_active = 1; | |
618 | rtas_fadump_get_config(fadump_conf, (void *)__pa(fdm_active)); | |
619 | } | |
620 | ||
d3833a70 HB |
621 | /* Get the sizes required to store dump data for the firmware provided |
622 | * dump sections. | |
623 | * For each dump section type supported, a 32bit cell which defines | |
624 | * the ID of a supported section followed by two 32 bit cells which | |
625 | * gives the size of the section in bytes. | |
626 | */ | |
627 | sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes", | |
628 | &size); | |
629 | ||
630 | if (!sections) | |
631 | return; | |
632 | ||
633 | num_sections = size / (3 * sizeof(u32)); | |
634 | ||
635 | for (i = 0; i < num_sections; i++, sections += 3) { | |
636 | u32 type = (u32)of_read_number(sections, 1); | |
637 | ||
638 | switch (type) { | |
639 | case RTAS_FADUMP_CPU_STATE_DATA: | |
640 | fadump_conf->cpu_state_data_size = | |
641 | of_read_ulong(§ions[1], 2); | |
642 | break; | |
643 | case RTAS_FADUMP_HPTE_REGION: | |
644 | fadump_conf->hpte_region_size = | |
645 | of_read_ulong(§ions[1], 2); | |
646 | break; | |
647 | } | |
648 | } | |
649 | } |