@@ -53,6 +53,11 @@
#define FADUMP_HPTE_REGION 0x0002
#define FADUMP_REAL_MODE_REGION 0x0011
+/* User define dump sections.
+ * Per PAPR: 0x0100 - 0xFFFF OS defined source types
+ */
+#define FADUMP_METADATA_REGION 0x0100
+
/* Dump request flag */
#define FADUMP_REQUEST_FLAG 0x00000001
@@ -61,6 +66,9 @@
#define FADUMP_UNREGISTER 2
#define FADUMP_INVALIDATE 3
+/* Number of dump sections requested by kernel */
+#define FADUMP_NUM_SECTIONS 4
+
/* Dump status flag */
#define FADUMP_ERROR_FLAG 0x2000
@@ -119,6 +127,7 @@ struct fadump_mem_struct {
struct fadump_section cpu_state_data;
struct fadump_section hpte_region;
struct fadump_section rmr_region;
+ struct fadump_section metadata_region;
};
/* Firmware-assisted dump configuration details. */
@@ -188,17 +188,40 @@ static void fadump_show_config(void)
pr_debug("Boot memory size : %lx\n", fw_dump.boot_memory_size);
}
+static unsigned long get_fadump_metadata_size(void)
+{
+ unsigned long size = 0;
+
+ size += sizeof(struct fadump_crash_info_header);
+ size += sizeof(struct elfhdr); /* ELF core header.*/
+ size += sizeof(struct elf_phdr); /* place holder for cpu notes */
+ /* Program headers for crash memory regions. */
+ size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2);
+
+ size = PAGE_ALIGN(size);
+ pr_debug("fadump Metadata size is %ld\n", size);
+ return size;
+}
+
static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
unsigned long addr)
{
+ uint16_t num_sections = 0;
+ unsigned long metadata_base = 0;
+ unsigned long metadata_size = 0;
+
if (!fdm)
return 0;
+ /* Skip the fadump metadata area. */
+ metadata_base = addr;
+ metadata_size = get_fadump_metadata_size();
+ addr += metadata_size;
+
memset(fdm, 0, sizeof(struct fadump_mem_struct));
addr = addr & PAGE_MASK;
fdm->header.dump_format_version = cpu_to_be32(0x00000001);
- fdm->header.dump_num_sections = cpu_to_be16(3);
fdm->header.dump_status_flag = 0;
fdm->header.offset_first_dump_section =
cpu_to_be32((u32)offsetof(struct fadump_mem_struct, cpu_state_data));
@@ -222,6 +245,7 @@ static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
fdm->cpu_state_data.source_address = 0;
fdm->cpu_state_data.source_len = cpu_to_be64(fw_dump.cpu_state_data_size);
fdm->cpu_state_data.destination_address = cpu_to_be64(addr);
+ num_sections++;
addr += fw_dump.cpu_state_data_size;
/* hpte region section */
@@ -230,6 +254,7 @@ static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
fdm->hpte_region.source_address = 0;
fdm->hpte_region.source_len = cpu_to_be64(fw_dump.hpte_region_size);
fdm->hpte_region.destination_address = cpu_to_be64(addr);
+ num_sections++;
addr += fw_dump.hpte_region_size;
/* RMA region section */
@@ -238,8 +263,27 @@ static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
fdm->rmr_region.source_address = cpu_to_be64(RMA_START);
fdm->rmr_region.source_len = cpu_to_be64(fw_dump.boot_memory_size);
fdm->rmr_region.destination_address = cpu_to_be64(addr);
+ num_sections++;
addr += fw_dump.boot_memory_size;
+ /*
+ * fadump metadata section.
+ * This section will help 2nd kernel to locate the metadata base
+ * looking at source_address. This entry will also copy the metadata
+ * content after the RMR region dump data, so that old kernels that
+ * does not support this section can still find the metadata to
+ * process fadump.
+ */
+ fdm->metadata_region.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
+ fdm->metadata_region.source_data_type =
+ cpu_to_be16(FADUMP_METADATA_REGION);
+ fdm->metadata_region.source_address = cpu_to_be64(metadata_base);
+ fdm->metadata_region.source_len = cpu_to_be64(metadata_size);
+ fdm->metadata_region.destination_address = cpu_to_be64(addr);
+ num_sections++;
+
+ fdm->header.dump_num_sections = cpu_to_be16(num_sections);
+ BUILD_BUG_ON(num_sections != FADUMP_NUM_SECTIONS);
return addr;
}
@@ -325,12 +369,12 @@ static unsigned long get_fadump_area_size(void)
size += fw_dump.cpu_state_data_size;
size += fw_dump.hpte_region_size;
size += fw_dump.boot_memory_size;
- size += sizeof(struct fadump_crash_info_header);
- size += sizeof(struct elfhdr); /* ELF core header.*/
- size += sizeof(struct elf_phdr); /* place holder for cpu notes */
- /* Program headers for crash memory regions. */
- size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2);
-
+ size += get_fadump_metadata_size();
+ /* Add it twice. We will ask platform to move metadata contents
+ * at rmr_region.destination_address + source_len, so that old
+ * kernels also can get metadata to process fadump successfully.
+ */
+ size += get_fadump_metadata_size();
size = PAGE_ALIGN(size);
return size;
}
@@ -355,6 +399,35 @@ static void __init fadump_reserve_crash_area(unsigned long base,
}
}
+static inline unsigned long get_fadump_metadata_base(
+ const struct fadump_mem_struct *fdm)
+{
+ const struct fadump_section *dump_section;
+ uint16_t num_sections;
+
+ /* Scan through all dump sections to find out metadata section. */
+ dump_section = &fdm->cpu_state_data;
+ num_sections = be16_to_cpu(fdm->header.dump_num_sections);
+
+ while (num_sections--) {
+ uint16_t type = be16_to_cpu(dump_section->source_data_type);
+ if (type == FADUMP_METADATA_REGION)
+ return be64_to_cpu(dump_section->source_address);
+
+ dump_section++;
+ }
+
+ /*
+ * Support the older kernel which does not contain metadata
+ * dump section. We end up here only when fadump is active and
+ * the previously crashed kernel is old kernel which had metadata
+ * section at the top of the reserved memory.
+ */
+ BUG_ON(fw_dump.dump_active == 0);
+ return be64_to_cpu(fdm->rmr_region.destination_address) +
+ be64_to_cpu(fdm->rmr_region.source_len);
+}
+
int __init fadump_reserve_mem(void)
{
unsigned long base, size, memory_boundary;
@@ -420,9 +493,9 @@ int __init fadump_reserve_mem(void)
size = memory_boundary - base;
fadump_reserve_crash_area(base, size);
- fw_dump.fadumphdr_addr =
- be64_to_cpu(fdm_active->rmr_region.destination_address) +
- be64_to_cpu(fdm_active->rmr_region.source_len);
+ pr_info("Number of kernel Dump sections: %d\n",
+ be16_to_cpu(fdm_active->header.dump_num_sections));
+ fw_dump.fadumphdr_addr = get_fadump_metadata_base(fdm_active);
pr_debug("fadumphdr_addr = %p\n",
(void *) fw_dump.fadumphdr_addr);
} else {
@@ -821,6 +894,38 @@ static int __init fadump_build_cpu_notes(const struct fadump_mem_struct *fdm)
}
+static int validate_dump(const struct fadump_mem_struct *fdm_active)
+{
+ const struct fadump_section *dump_section;
+ uint16_t num_sections = 0;
+ int rc = 0;
+
+ num_sections = be16_to_cpu(fdm_active->header.dump_num_sections);
+ dump_section = &fdm_active->cpu_state_data;
+
+ if ((be16_to_cpu(fdm_active->header.dump_status_flag)
+ == FADUMP_ERROR_FLAG)) {
+ pr_err("Error occured while taking a dump by platform\n");
+ rc = -EINVAL;
+ }
+
+ /* Print Section type and error flags for failed dump section. */
+ while (num_sections--) {
+ if (dump_section->error_flags != 0 ||
+ (dump_section->source_len != dump_section->bytes_dumped)) {
+ pr_err("Section: %04x, Error flag: %04x, "
+ "Bytes Req: %llx, Bytes Dumped: %llx\n",
+ be16_to_cpu(dump_section->source_data_type),
+ be16_to_cpu(dump_section->error_flags),
+ be64_to_cpu(dump_section->source_len),
+ be64_to_cpu(dump_section->bytes_dumped));
+ rc = -EINVAL;
+ }
+ dump_section++;
+ }
+ return rc;
+}
+
/*
* Validate and process the dump data stored by firmware before exporting
* it through '/proc/vmcore'.
@@ -834,17 +939,10 @@ static int __init process_fadump(const struct fadump_mem_struct *fdm_active)
return -EINVAL;
/* Check if the dump data is valid. */
- if ((be16_to_cpu(fdm_active->header.dump_status_flag) == FADUMP_ERROR_FLAG) ||
- (fdm_active->cpu_state_data.error_flags != 0) ||
- (fdm_active->rmr_region.error_flags != 0)) {
+ rc = validate_dump(fdm_active);
+ if (rc) {
printk(KERN_ERR "Dump taken by platform is not valid\n");
- return -EINVAL;
- }
- if ((fdm_active->rmr_region.bytes_dumped !=
- fdm_active->rmr_region.source_len) ||
- !fdm_active->cpu_state_data.bytes_dumped) {
- printk(KERN_ERR "Dump taken by platform is incomplete\n");
- return -EINVAL;
+ return rc;
}
/* Validate the fadump crash info header */
@@ -1107,7 +1205,7 @@ static int register_fadump(void)
fadump_setup_crash_memory_ranges();
- addr = be64_to_cpu(fdm.rmr_region.destination_address) + be64_to_cpu(fdm.rmr_region.source_len);
+ addr = be64_to_cpu(fdm.metadata_region.source_address);
/* Initialize fadump crash info header. */
addr = init_fadump_header(addr);
vaddr = __va(addr);
@@ -1146,7 +1244,7 @@ static int fadump_unregister_dump(struct fadump_mem_struct *fdm)
return 0;
}
-static int fadump_invalidate_dump(struct fadump_mem_struct *fdm)
+static int fadump_invalidate_dump(const struct fadump_mem_struct *fdm)
{
int rc = 0;
unsigned int wait_time;
@@ -1177,9 +1275,7 @@ void fadump_cleanup(void)
{
/* Invalidate the registration only if dump is active. */
if (fw_dump.dump_active) {
- init_fadump_mem_struct(&fdm,
- be64_to_cpu(fdm_active->cpu_state_data.destination_address));
- fadump_invalidate_dump(&fdm);
+ fadump_invalidate_dump(fdm_active);
}
}
@@ -1250,6 +1346,35 @@ static void fadump_release_memory(unsigned long begin, unsigned long end)
fadump_release_reserved_area(begin, end);
}
+static inline unsigned long get_fadump_prev_mstart(
+ const struct fadump_mem_struct *fdm)
+{
+ const struct fadump_section *dump_section;
+ uint16_t num_sections;
+
+ /* This function is called only when fadump is active. */
+ BUG_ON(fw_dump.dump_active == 0);
+
+ /* Scan through all dump sections to find out metadata section. */
+ dump_section = &fdm->cpu_state_data;
+ num_sections = be16_to_cpu(fdm->header.dump_num_sections);
+
+ while (num_sections--) {
+ uint16_t type = be16_to_cpu(dump_section->source_data_type);
+ if (type == FADUMP_METADATA_REGION)
+ return be64_to_cpu(dump_section->source_address);
+
+ dump_section++;
+ }
+
+ /*
+ * Support the older kernel which does not contain metadata
+ * dump section. For older kernel reserved memory start is
+ * at cpu_state_data.destination_address.
+ */
+ return be64_to_cpu(fdm->cpu_state_data.destination_address);
+}
+
static void fadump_invalidate_release_mem(void)
{
unsigned long reserved_area_start, reserved_area_end;
@@ -1261,7 +1386,7 @@ static void fadump_invalidate_release_mem(void)
return;
}
- destination_address = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
+ destination_address = get_fadump_prev_mstart(fdm_active);
fadump_cleanup();
mutex_unlock(&fadump_mutex);