powerpc/fadump: Fix fadump to work with a different endian capture kernel

[ Upstream commit b74196af37 ]

Dump capture would fail if capture kernel is not of the endianess as the
production kernel, because the in-memory data structure (struct
opal_fadump_mem_struct) shared across production kernel and capture
kernel assumes the same endianess for both the kernels, which doesn't
have to be true always. Fix it by having a well-defined endianess for
struct opal_fadump_mem_struct.

Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/161902744901.86147.14719228311655123526.stgit@hbathini
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Hari Bathini 2021-04-21 23:20:52 +05:30 коммит произвёл Greg Kroah-Hartman
Родитель de8f0b173d
Коммит a0f7180a2c
2 изменённых файлов: 57 добавлений и 47 удалений

Просмотреть файл

@ -60,7 +60,7 @@ void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
addr = be64_to_cpu(addr); addr = be64_to_cpu(addr);
pr_debug("Kernel metadata addr: %llx\n", addr); pr_debug("Kernel metadata addr: %llx\n", addr);
opal_fdm_active = (void *)addr; opal_fdm_active = (void *)addr;
if (opal_fdm_active->registered_regions == 0) if (be16_to_cpu(opal_fdm_active->registered_regions) == 0)
return; return;
ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_BOOT_MEM, &addr); ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_BOOT_MEM, &addr);
@ -95,17 +95,17 @@ static int opal_fadump_unregister(struct fw_dump *fadump_conf);
static void opal_fadump_update_config(struct fw_dump *fadump_conf, static void opal_fadump_update_config(struct fw_dump *fadump_conf,
const struct opal_fadump_mem_struct *fdm) const struct opal_fadump_mem_struct *fdm)
{ {
pr_debug("Boot memory regions count: %d\n", fdm->region_cnt); pr_debug("Boot memory regions count: %d\n", be16_to_cpu(fdm->region_cnt));
/* /*
* The destination address of the first boot memory region is the * The destination address of the first boot memory region is the
* destination address of boot memory regions. * destination address of boot memory regions.
*/ */
fadump_conf->boot_mem_dest_addr = fdm->rgn[0].dest; fadump_conf->boot_mem_dest_addr = be64_to_cpu(fdm->rgn[0].dest);
pr_debug("Destination address of boot memory regions: %#016llx\n", pr_debug("Destination address of boot memory regions: %#016llx\n",
fadump_conf->boot_mem_dest_addr); fadump_conf->boot_mem_dest_addr);
fadump_conf->fadumphdr_addr = fdm->fadumphdr_addr; fadump_conf->fadumphdr_addr = be64_to_cpu(fdm->fadumphdr_addr);
} }
/* /*
@ -126,9 +126,9 @@ static void opal_fadump_get_config(struct fw_dump *fadump_conf,
fadump_conf->boot_memory_size = 0; fadump_conf->boot_memory_size = 0;
pr_debug("Boot memory regions:\n"); pr_debug("Boot memory regions:\n");
for (i = 0; i < fdm->region_cnt; i++) { for (i = 0; i < be16_to_cpu(fdm->region_cnt); i++) {
base = fdm->rgn[i].src; base = be64_to_cpu(fdm->rgn[i].src);
size = fdm->rgn[i].size; size = be64_to_cpu(fdm->rgn[i].size);
pr_debug("\t[%03d] base: 0x%lx, size: 0x%lx\n", i, base, size); pr_debug("\t[%03d] base: 0x%lx, size: 0x%lx\n", i, base, size);
fadump_conf->boot_mem_addr[i] = base; fadump_conf->boot_mem_addr[i] = base;
@ -143,7 +143,7 @@ static void opal_fadump_get_config(struct fw_dump *fadump_conf,
* Start address of reserve dump area (permanent reservation) for * Start address of reserve dump area (permanent reservation) for
* re-registering FADump after dump capture. * re-registering FADump after dump capture.
*/ */
fadump_conf->reserve_dump_area_start = fdm->rgn[0].dest; fadump_conf->reserve_dump_area_start = be64_to_cpu(fdm->rgn[0].dest);
/* /*
* Rarely, but it can so happen that system crashes before all * Rarely, but it can so happen that system crashes before all
@ -155,13 +155,14 @@ static void opal_fadump_get_config(struct fw_dump *fadump_conf,
* Hope the memory that could not be preserved only has pages * Hope the memory that could not be preserved only has pages
* that are usually filtered out while saving the vmcore. * that are usually filtered out while saving the vmcore.
*/ */
if (fdm->region_cnt > fdm->registered_regions) { if (be16_to_cpu(fdm->region_cnt) > be16_to_cpu(fdm->registered_regions)) {
pr_warn("Not all memory regions were saved!!!\n"); pr_warn("Not all memory regions were saved!!!\n");
pr_warn(" Unsaved memory regions:\n"); pr_warn(" Unsaved memory regions:\n");
i = fdm->registered_regions; i = be16_to_cpu(fdm->registered_regions);
while (i < fdm->region_cnt) { while (i < be16_to_cpu(fdm->region_cnt)) {
pr_warn("\t[%03d] base: 0x%llx, size: 0x%llx\n", pr_warn("\t[%03d] base: 0x%llx, size: 0x%llx\n",
i, fdm->rgn[i].src, fdm->rgn[i].size); i, be64_to_cpu(fdm->rgn[i].src),
be64_to_cpu(fdm->rgn[i].size));
i++; i++;
} }
@ -170,7 +171,7 @@ static void opal_fadump_get_config(struct fw_dump *fadump_conf,
} }
fadump_conf->boot_mem_top = (fadump_conf->boot_memory_size + hole_size); fadump_conf->boot_mem_top = (fadump_conf->boot_memory_size + hole_size);
fadump_conf->boot_mem_regs_cnt = fdm->region_cnt; fadump_conf->boot_mem_regs_cnt = be16_to_cpu(fdm->region_cnt);
opal_fadump_update_config(fadump_conf, fdm); opal_fadump_update_config(fadump_conf, fdm);
} }
@ -178,35 +179,38 @@ static void opal_fadump_get_config(struct fw_dump *fadump_conf,
static void opal_fadump_init_metadata(struct opal_fadump_mem_struct *fdm) static void opal_fadump_init_metadata(struct opal_fadump_mem_struct *fdm)
{ {
fdm->version = OPAL_FADUMP_VERSION; fdm->version = OPAL_FADUMP_VERSION;
fdm->region_cnt = 0; fdm->region_cnt = cpu_to_be16(0);
fdm->registered_regions = 0; fdm->registered_regions = cpu_to_be16(0);
fdm->fadumphdr_addr = 0; fdm->fadumphdr_addr = cpu_to_be64(0);
} }
static u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf) static u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf)
{ {
u64 addr = fadump_conf->reserve_dump_area_start; u64 addr = fadump_conf->reserve_dump_area_start;
u16 reg_cnt;
int i; int i;
opal_fdm = __va(fadump_conf->kernel_metadata); opal_fdm = __va(fadump_conf->kernel_metadata);
opal_fadump_init_metadata(opal_fdm); opal_fadump_init_metadata(opal_fdm);
/* Boot memory regions */ /* Boot memory regions */
reg_cnt = be16_to_cpu(opal_fdm->region_cnt);
for (i = 0; i < fadump_conf->boot_mem_regs_cnt; i++) { for (i = 0; i < fadump_conf->boot_mem_regs_cnt; i++) {
opal_fdm->rgn[i].src = fadump_conf->boot_mem_addr[i]; opal_fdm->rgn[i].src = cpu_to_be64(fadump_conf->boot_mem_addr[i]);
opal_fdm->rgn[i].dest = addr; opal_fdm->rgn[i].dest = cpu_to_be64(addr);
opal_fdm->rgn[i].size = fadump_conf->boot_mem_sz[i]; opal_fdm->rgn[i].size = cpu_to_be64(fadump_conf->boot_mem_sz[i]);
opal_fdm->region_cnt++; reg_cnt++;
addr += fadump_conf->boot_mem_sz[i]; addr += fadump_conf->boot_mem_sz[i];
} }
opal_fdm->region_cnt = cpu_to_be16(reg_cnt);
/* /*
* Kernel metadata is passed to f/w and retrieved in capture kerenl. * Kernel metadata is passed to f/w and retrieved in capture kerenl.
* So, use it to save fadump header address instead of calculating it. * So, use it to save fadump header address instead of calculating it.
*/ */
opal_fdm->fadumphdr_addr = (opal_fdm->rgn[0].dest + opal_fdm->fadumphdr_addr = cpu_to_be64(be64_to_cpu(opal_fdm->rgn[0].dest) +
fadump_conf->boot_memory_size); fadump_conf->boot_memory_size);
opal_fadump_update_config(fadump_conf, opal_fdm); opal_fadump_update_config(fadump_conf, opal_fdm);
@ -269,18 +273,21 @@ static u64 opal_fadump_get_bootmem_min(void)
static int opal_fadump_register(struct fw_dump *fadump_conf) static int opal_fadump_register(struct fw_dump *fadump_conf)
{ {
s64 rc = OPAL_PARAMETER; s64 rc = OPAL_PARAMETER;
u16 registered_regs;
int i, err = -EIO; int i, err = -EIO;
for (i = 0; i < opal_fdm->region_cnt; i++) { registered_regs = be16_to_cpu(opal_fdm->registered_regions);
for (i = 0; i < be16_to_cpu(opal_fdm->region_cnt); i++) {
rc = opal_mpipl_update(OPAL_MPIPL_ADD_RANGE, rc = opal_mpipl_update(OPAL_MPIPL_ADD_RANGE,
opal_fdm->rgn[i].src, be64_to_cpu(opal_fdm->rgn[i].src),
opal_fdm->rgn[i].dest, be64_to_cpu(opal_fdm->rgn[i].dest),
opal_fdm->rgn[i].size); be64_to_cpu(opal_fdm->rgn[i].size));
if (rc != OPAL_SUCCESS) if (rc != OPAL_SUCCESS)
break; break;
opal_fdm->registered_regions++; registered_regs++;
} }
opal_fdm->registered_regions = cpu_to_be16(registered_regs);
switch (rc) { switch (rc) {
case OPAL_SUCCESS: case OPAL_SUCCESS:
@ -291,7 +298,8 @@ static int opal_fadump_register(struct fw_dump *fadump_conf)
case OPAL_RESOURCE: case OPAL_RESOURCE:
/* If MAX regions limit in f/w is hit, warn and proceed. */ /* If MAX regions limit in f/w is hit, warn and proceed. */
pr_warn("%d regions could not be registered for MPIPL as MAX limit is reached!\n", pr_warn("%d regions could not be registered for MPIPL as MAX limit is reached!\n",
(opal_fdm->region_cnt - opal_fdm->registered_regions)); (be16_to_cpu(opal_fdm->region_cnt) -
be16_to_cpu(opal_fdm->registered_regions)));
fadump_conf->dump_registered = 1; fadump_conf->dump_registered = 1;
err = 0; err = 0;
break; break;
@ -312,7 +320,7 @@ static int opal_fadump_register(struct fw_dump *fadump_conf)
* If some regions were registered before OPAL_MPIPL_ADD_RANGE * If some regions were registered before OPAL_MPIPL_ADD_RANGE
* OPAL call failed, unregister all regions. * OPAL call failed, unregister all regions.
*/ */
if ((err < 0) && (opal_fdm->registered_regions > 0)) if ((err < 0) && (be16_to_cpu(opal_fdm->registered_regions) > 0))
opal_fadump_unregister(fadump_conf); opal_fadump_unregister(fadump_conf);
return err; return err;
@ -328,7 +336,7 @@ static int opal_fadump_unregister(struct fw_dump *fadump_conf)
return -EIO; return -EIO;
} }
opal_fdm->registered_regions = 0; opal_fdm->registered_regions = cpu_to_be16(0);
fadump_conf->dump_registered = 0; fadump_conf->dump_registered = 0;
return 0; return 0;
} }
@ -563,19 +571,20 @@ static void opal_fadump_region_show(struct fw_dump *fadump_conf,
else else
fdm_ptr = opal_fdm; fdm_ptr = opal_fdm;
for (i = 0; i < fdm_ptr->region_cnt; i++) { for (i = 0; i < be16_to_cpu(fdm_ptr->region_cnt); i++) {
/* /*
* Only regions that are registered for MPIPL * Only regions that are registered for MPIPL
* would have dump data. * would have dump data.
*/ */
if ((fadump_conf->dump_active) && if ((fadump_conf->dump_active) &&
(i < fdm_ptr->registered_regions)) (i < be16_to_cpu(fdm_ptr->registered_regions)))
dumped_bytes = fdm_ptr->rgn[i].size; dumped_bytes = be64_to_cpu(fdm_ptr->rgn[i].size);
seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ", seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
fdm_ptr->rgn[i].src, fdm_ptr->rgn[i].dest); be64_to_cpu(fdm_ptr->rgn[i].src),
be64_to_cpu(fdm_ptr->rgn[i].dest));
seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n", seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
fdm_ptr->rgn[i].size, dumped_bytes); be64_to_cpu(fdm_ptr->rgn[i].size), dumped_bytes);
} }
/* Dump is active. Show reserved area start address. */ /* Dump is active. Show reserved area start address. */
@ -624,6 +633,7 @@ void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
{ {
const __be32 *prop; const __be32 *prop;
unsigned long dn; unsigned long dn;
__be64 be_addr;
u64 addr = 0; u64 addr = 0;
int i, len; int i, len;
s64 ret; s64 ret;
@ -680,13 +690,13 @@ void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
if (!prop) if (!prop)
return; return;
ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_KERNEL, &addr); ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_KERNEL, &be_addr);
if ((ret != OPAL_SUCCESS) || !addr) { if ((ret != OPAL_SUCCESS) || !be_addr) {
pr_err("Failed to get Kernel metadata (%lld)\n", ret); pr_err("Failed to get Kernel metadata (%lld)\n", ret);
return; return;
} }
addr = be64_to_cpu(addr); addr = be64_to_cpu(be_addr);
pr_debug("Kernel metadata addr: %llx\n", addr); pr_debug("Kernel metadata addr: %llx\n", addr);
opal_fdm_active = __va(addr); opal_fdm_active = __va(addr);
@ -697,14 +707,14 @@ void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
} }
/* Kernel regions not registered with f/w for MPIPL */ /* Kernel regions not registered with f/w for MPIPL */
if (opal_fdm_active->registered_regions == 0) { if (be16_to_cpu(opal_fdm_active->registered_regions) == 0) {
opal_fdm_active = NULL; opal_fdm_active = NULL;
return; return;
} }
ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_CPU, &addr); ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_CPU, &be_addr);
if (addr) { if (be_addr) {
addr = be64_to_cpu(addr); addr = be64_to_cpu(be_addr);
pr_debug("CPU metadata addr: %llx\n", addr); pr_debug("CPU metadata addr: %llx\n", addr);
opal_cpu_metadata = __va(addr); opal_cpu_metadata = __va(addr);
} }

Просмотреть файл

@ -31,14 +31,14 @@
* OPAL FADump kernel metadata * OPAL FADump kernel metadata
* *
* The address of this structure will be registered with f/w for retrieving * The address of this structure will be registered with f/w for retrieving
* and processing during crash dump. * in the capture kernel to process the crash dump.
*/ */
struct opal_fadump_mem_struct { struct opal_fadump_mem_struct {
u8 version; u8 version;
u8 reserved[3]; u8 reserved[3];
u16 region_cnt; /* number of regions */ __be16 region_cnt; /* number of regions */
u16 registered_regions; /* Regions registered for MPIPL */ __be16 registered_regions; /* Regions registered for MPIPL */
u64 fadumphdr_addr; __be64 fadumphdr_addr;
struct opal_mpipl_region rgn[FADUMP_MAX_MEM_REGS]; struct opal_mpipl_region rgn[FADUMP_MAX_MEM_REGS];
} __packed; } __packed;
@ -135,7 +135,7 @@ static inline void opal_fadump_read_regs(char *bufp, unsigned int regs_cnt,
for (i = 0; i < regs_cnt; i++, bufp += reg_entry_size) { for (i = 0; i < regs_cnt; i++, bufp += reg_entry_size) {
reg_entry = (struct hdat_fadump_reg_entry *)bufp; reg_entry = (struct hdat_fadump_reg_entry *)bufp;
val = (cpu_endian ? be64_to_cpu(reg_entry->reg_val) : val = (cpu_endian ? be64_to_cpu(reg_entry->reg_val) :
reg_entry->reg_val); (u64)(reg_entry->reg_val));
opal_fadump_set_regval_regnum(regs, opal_fadump_set_regval_regnum(regs,
be32_to_cpu(reg_entry->reg_type), be32_to_cpu(reg_entry->reg_type),
be32_to_cpu(reg_entry->reg_num), be32_to_cpu(reg_entry->reg_num),