/* * This function is called in the capture kernel to get configuration details * setup in the first kernel and passed to the f/w.
*/ staticvoid __init rtas_fadump_get_config(struct fw_dump *fadump_conf, conststruct rtas_fadump_mem_struct *fdm)
{ unsignedlong base, size, last_end, hole_size;
last_end = 0;
hole_size = 0;
fadump_conf->boot_memory_size = 0;
fadump_conf->boot_mem_regs_cnt = 0;
pr_debug("Boot memory regions:\n"); for (int i = 0; i < be16_to_cpu(fdm->header.dump_num_sections); i++) { int type = be16_to_cpu(fdm->rgn[i].source_data_type);
u64 addr;
switch (type) { case RTAS_FADUMP_CPU_STATE_DATA:
addr = be64_to_cpu(fdm->rgn[i].destination_address);
fadump_conf->cpu_state_dest_vaddr = (u64)__va(addr); /* * Start address of reserve dump area (permanent reservation) for * re-registering FADump after dump capture.
*/
fadump_conf->reserve_dump_area_start = addr; break; case RTAS_FADUMP_HPTE_REGION: /* Not processed currently. */ break; case RTAS_FADUMP_REAL_MODE_REGION:
base = be64_to_cpu(fdm->rgn[i].source_address);
size = be64_to_cpu(fdm->rgn[i].source_len);
pr_debug("\t[%03d] base: 0x%lx, size: 0x%lx\n", i, base, size); if (!base) {
fadump_conf->boot_mem_dest_addr =
be64_to_cpu(fdm->rgn[i].destination_address);
}
fadump_conf->boot_mem_addr[fadump_conf->boot_mem_regs_cnt] = base;
fadump_conf->boot_mem_sz[fadump_conf->boot_mem_regs_cnt] = size;
fadump_conf->boot_memory_size += size;
hole_size += (base - last_end);
last_end = base + size;
fadump_conf->boot_mem_regs_cnt++; break; case RTAS_FADUMP_PARAM_AREA:
fadump_conf->param_area = be64_to_cpu(fdm->rgn[i].destination_address); break; default:
pr_warn("Section type %d unsupported on this kernel. Ignoring!\n", type); break;
}
}
fadump_conf->boot_mem_top = fadump_conf->boot_memory_size + hole_size;
/* * Fields for disk dump option. * We are not using disk dump option, hence set these fields to 0.
*/
fdm.header.dd_block_size = 0;
fdm.header.dd_block_offset = 0;
fdm.header.dd_num_blocks = 0;
fdm.header.dd_offset_disk_path = 0;
/* set 0 to disable an automatic dump-reboot. */
fdm.header.max_time_auto = 0;
/* Kernel dump sections */ /* cpu state data section. */
fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA);
fdm.rgn[sec_cnt].source_address = 0;
fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->cpu_state_data_size);
fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
addr += fadump_conf->cpu_state_data_size;
sec_cnt++;
/* * Platform requires the exact size of the Dump Memory Structure. * Avoid including any unused rgns in the calculation, as this * could result in a parameter error (-3) from the platform.
*/
fdm_size = sizeof(struct rtas_fadump_section_header);
fdm_size += be16_to_cpu(fdm.header.dump_num_sections) * sizeof(struct rtas_fadump_section);
/* TODO: Add upper time limit for the delay */ do {
rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
NULL, FADUMP_REGISTER, &fdm, fdm_size);
wait_time = rtas_busy_delay_time(rc); if (wait_time)
mdelay(wait_time);
} while (wait_time);
switch (rc) { case 0:
pr_info("Registration is successful!\n");
fadump_conf->dump_registered = 1;
err = 0; break; case -1:
pr_err("Failed to register. Hardware Error(%d).\n", rc); break; case -3: if (!is_fadump_reserved_mem_contiguous())
pr_err("Can't have holes in reserved memory area.\n");
pr_err("Failed to register. Parameter Error(%d).\n", rc);
err = -EINVAL; break; case -9:
pr_err("Already registered!\n");
fadump_conf->dump_registered = 1;
err = -EEXIST; break; default:
pr_err("Failed to register. Unknown Error(%d).\n", rc); break;
}
return err;
}
staticint rtas_fadump_unregister(struct fw_dump *fadump_conf)
{ unsignedint wait_time; int rc;
/* TODO: Add upper time limit for the delay */ do {
rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
NULL, FADUMP_UNREGISTER, &fdm, sizeof(struct rtas_fadump_mem_struct));
wait_time = rtas_busy_delay_time(rc); if (wait_time)
mdelay(wait_time);
} while (wait_time);
if (rc) {
pr_err("Failed to un-register - unexpected error(%d).\n", rc); return -EIO;
}
fadump_conf->dump_registered = 0; return 0;
}
staticint rtas_fadump_invalidate(struct fw_dump *fadump_conf)
{ unsignedint wait_time; int rc;
/* TODO: Add upper time limit for the delay */ do {
rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
NULL, FADUMP_INVALIDATE, fdm_active, sizeof(struct rtas_fadump_mem_struct));
wait_time = rtas_busy_delay_time(rc); if (wait_time)
mdelay(wait_time);
} while (wait_time);
if (rc) {
pr_err("Failed to invalidate - unexpected error (%d).\n", rc); return -EIO;
}
#define RTAS_FADUMP_GPR_MASK 0xffffff0000000000 staticinlineint rtas_fadump_gpr_index(u64 id)
{ char str[3]; int i = -1;
if ((id & RTAS_FADUMP_GPR_MASK) == fadump_str_to_u64("GPR")) { /* get the digits at the end */
id &= ~RTAS_FADUMP_GPR_MASK;
id >>= 24;
str[2] = '\0';
str[1] = id & 0xff;
str[0] = (id >> 8) & 0xff; if (kstrtoint(str, 10, &i))
i = -EINVAL; if (i > 31)
i = -1;
} return i;
}
/* * Read CPU state dump data and convert it into ELF notes. * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be * used to access the data to allow for additional fields to be added without * affecting compatibility. Each list of registers for a CPU starts with * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes, * 8 Byte ASCII identifier and 8 Byte register value. The register entry * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part * of register value. For more details refer to PAPR document. * * Only for the crashing cpu we ignore the CPU dump data and get exact * state from fadump crash info structure populated by first kernel at the * time of crash.
*/ staticint __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf)
{ struct rtas_fadump_reg_save_area_header *reg_header; struct fadump_crash_info_header *fdh = NULL; struct rtas_fadump_reg_entry *reg_entry;
u32 num_cpus, *note_buf; int i, rc = 0, cpu = 0; struct pt_regs regs; void *vaddr;
/* * Validate and process the dump data stored by the firmware, and update * the CPU notes of elfcorehdr.
*/ staticint __init rtas_fadump_process(struct fw_dump *fadump_conf)
{ if (!fdm_active || !fadump_conf->fadumphdr_addr) return -EINVAL;
/* Check if the dump data is valid. */ for (int i = 0; i < be16_to_cpu(fdm_active->header.dump_num_sections); i++) { int type = be16_to_cpu(fdm_active->rgn[i].source_data_type); int rc = 0;
switch (type) { case RTAS_FADUMP_CPU_STATE_DATA: case RTAS_FADUMP_HPTE_REGION: case RTAS_FADUMP_REAL_MODE_REGION: if (fdm_active->rgn[i].error_flags != 0) {
pr_err("Dump taken by platform is not valid (%d)\n", i);
rc = -EINVAL;
} if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len) {
pr_err("Dump taken by platform is incomplete (%d)\n", i);
rc = -EINVAL;
} if (rc) {
pr_warn("Region type: %u src addr: 0x%llx dest addr: 0x%llx\n",
be16_to_cpu(fdm_active->rgn[i].source_data_type),
be64_to_cpu(fdm_active->rgn[i].source_address),
be64_to_cpu(fdm_active->rgn[i].destination_address)); return rc;
} break; case RTAS_FADUMP_PARAM_AREA: if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len ||
fdm_active->rgn[i].error_flags != 0) {
pr_warn("Failed to process additional parameters! Proceeding anyway..\n");
fadump_conf->param_area = 0;
} break; default: /* * If the first/crashed kernel added a new region type that the * second/fadump kernel doesn't recognize, skip it and process * assuming backward compatibility.
*/
pr_warn("Unknown region found: type: %u src addr: 0x%llx dest addr: 0x%llx\n",
be16_to_cpu(fdm_active->rgn[i].source_data_type),
be64_to_cpu(fdm_active->rgn[i].source_address),
be64_to_cpu(fdm_active->rgn[i].destination_address)); break;
}
}
/* Dump is active. Show preserved area start address. */ if (fdm_active) {
seq_printf(m, "\nMemory above %#016llx is reserved for saving crash dump\n",
fadump_conf->boot_mem_top);
}
}
/* FADUMP_MAX_MEM_REGS or lower */ staticint rtas_fadump_max_boot_mem_rgns(void)
{ /* * Version 1 of Kernel Assisted Dump Memory Structure (PAPR) supports 10 sections. * With one each section taken for CPU state data & HPTE respectively, 8 sections * can be used for boot memory regions. * * If new region(s) is(are) defined, maximum boot memory regions will decrease * proportionally.
*/ return RTAS_FADUMP_MAX_BOOT_MEM_REGS;
}
void __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
{ int i, size, num_sections; const __be32 *sections; const __be32 *token;
/* * Check if Firmware Assisted dump is supported. if yes, check * if dump has been initiated on last reboot.
*/
token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL); if (!token) return;
/* Firmware supports 64-bit value for size, align it to pagesize. */
fadump_conf->max_copy_size = ALIGN_DOWN(U64_MAX, PAGE_SIZE);
/* * The 'ibm,kernel-dump' rtas node is present only if there is * dump data waiting for us.
*/
fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL); if (fdm_active) {
pr_info("Firmware-assisted dump is active.\n");
fadump_conf->dump_active = 1;
rtas_fadump_get_config(fadump_conf, (void *)__pa(fdm_active));
}
/* Get the sizes required to store dump data for the firmware provided * dump sections. * For each dump section type supported, a 32bit cell which defines * the ID of a supported section followed by two 32 bit cells which * gives the size of the section in bytes.
*/
sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes",
&size);
if (!sections) return;
num_sections = size / (3 * sizeof(u32));
for (i = 0; i < num_sections; i++, sections += 3) {
u32 type = (u32)of_read_number(sections, 1);
switch (type) { case RTAS_FADUMP_CPU_STATE_DATA:
fadump_conf->cpu_state_data_size =
of_read_ulong(§ions[1], 2); break; case RTAS_FADUMP_HPTE_REGION:
fadump_conf->hpte_region_size =
of_read_ulong(§ions[1], 2); break;
}
}
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.27 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.