hw/ppc: Implement fadump register command

Add skeleton to handle "ibm,configure-kernel-dump" rtas call in QEMU,
including register, unregister and invalidate commands.

The register just verifies the structure of the fadump memory structure
passed by kernel, and set fadump_registered in spapr state to true.

Verify basic details mandated by the PAPR, such as number of
inputs/output, and add handling for the three fadump commands:
regiser/unregister/invalidate.

The checks are based on the table in following requirement in PAPR v2.13:
    "R1–7.3.30–1. For the Configure Platform Assisted Kernel Dump option ..."

Relevant section for the register command in PAPR is:
    Section 7.3.30: "ibm,configure-kernel-dump RTAS call" (PAPR v2.13)

Note: Any modifications made by the kernel to the fadump memory
structure after the 'ibm,configure-kernel-dump' RTAS call returns will
not be reflected in QEMU, as QEMU retains the fadump memory structure
that was provided during fadump registration.

The kernel must unregister and re-register fadump to apply any changes
to the fadump memory structure.

Signed-off-by: Aditya Gupta <adityag@linux.ibm.com>
Reviewed-by: Sourabh Jain <sourabhjain@linux.ibm.com>
Tested-by: Shivang Upadhyay <shivangu@linux.ibm.com>
Link: https://lore.kernel.org/qemu-devel/20251021134823.1861675-2-adityag@linux.ibm.com
Signed-off-by: Harsh Prateek Bora <harshpb@linux.ibm.com>
This commit is contained in:
Aditya Gupta
2025-10-23 17:37:39 +05:30
committed by Harsh Prateek Bora
parent 3c21f9dfcf
commit a728c87e96
5 changed files with 274 additions and 1 deletions
+1
View File
@@ -26,6 +26,7 @@ ppc_ss.add(when: 'CONFIG_PSERIES', if_true: files(
'spapr_nvdimm.c',
'spapr_rtas_ddw.c',
'spapr_numa.c',
'spapr_fadump.c',
'pef.c',
))
ppc_ss.add(when: ['CONFIG_PSERIES', 'CONFIG_TCG'], if_true: files(
+123
View File
@@ -0,0 +1,123 @@
/*
* Firmware Assisted Dump in PSeries
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "hw/ppc/spapr.h"
/*
* Handle the "FADUMP_CMD_REGISTER" command in 'ibm,configure-kernel-dump'
*
* Note: Any changes made by the kernel to the fadump memory struct won't
* reflect in QEMU after the 'ibm,configure-kernel-dump' RTAS call has returned,
* as we store the passed fadump memory structure passed during fadump
* registration.
* Kernel has to invalidate & re-register fadump, if it intends to make any
* changes to the fadump memory structure
*
* Returns:
* * RTAS_OUT_SUCCESS: On successful registration
* * RTAS_OUT_PARAM_ERROR: If parameters are not correct, eg. too many
* sections, invalid memory addresses that we are
* unable to read, etc
* * RTAS_OUT_DUMP_ALREADY_REGISTERED: Dump already registered
* * RTAS_OUT_HW_ERROR: Misc issue such as memory access failures
*/
uint32_t do_fadump_register(SpaprMachineState *spapr, target_ulong args)
{
FadumpSectionHeader header;
FadumpSection regions[FADUMP_MAX_SECTIONS] = {0};
target_ulong fdm_addr = rtas_ld(args, 1);
target_ulong fdm_size = rtas_ld(args, 2);
AddressSpace *default_as = &address_space_memory;
MemTxResult io_result;
MemTxAttrs attrs;
uint64_t next_section_addr;
uint16_t dump_num_sections;
/* Mark the memory transaction as privileged memory access */
attrs.user = 0;
attrs.memory = 1;
if (spapr->fadump_registered) {
/* FADump already registered */
return RTAS_OUT_DUMP_ALREADY_REGISTERED;
}
if (spapr->fadump_dump_active) {
return RTAS_OUT_DUMP_ACTIVE;
}
if (fdm_size < sizeof(FadumpSectionHeader)) {
qemu_log_mask(LOG_GUEST_ERROR,
"FADump: Header size is invalid: " TARGET_FMT_lu "\n", fdm_size);
return RTAS_OUT_PARAM_ERROR;
}
/* Ensure fdm_addr points to a valid RMR-memory/RMA-memory buffer */
if ((fdm_addr <= 0) || ((fdm_addr + fdm_size) > spapr->rma_size)) {
qemu_log_mask(LOG_GUEST_ERROR,
"FADump: Invalid fdm address: " TARGET_FMT_lu "\n", fdm_addr);
return RTAS_OUT_PARAM_ERROR;
}
/* Try to read the passed fadump header */
io_result = address_space_read(default_as, fdm_addr, attrs,
&header, sizeof(header));
if (io_result != MEMTX_OK) {
qemu_log_mask(LOG_GUEST_ERROR,
"FADump: Unable to read fdm: " TARGET_FMT_lu "\n", fdm_addr);
return RTAS_OUT_HW_ERROR;
}
/* Verify that we understand the fadump header version */
if (header.dump_format_version != cpu_to_be32(FADUMP_VERSION)) {
qemu_log_mask(LOG_GUEST_ERROR,
"FADump: Unknown fadump header version: 0x%x\n",
header.dump_format_version);
return RTAS_OUT_PARAM_ERROR;
}
/* Reset dump status flags */
header.dump_status_flag = 0;
dump_num_sections = be16_to_cpu(header.dump_num_sections);
if (dump_num_sections > FADUMP_MAX_SECTIONS) {
qemu_log_mask(LOG_GUEST_ERROR,
"FADump: Too many sections: %d sections\n", dump_num_sections);
return RTAS_OUT_PARAM_ERROR;
}
next_section_addr =
fdm_addr +
be32_to_cpu(header.offset_first_dump_section);
for (int i = 0; i < dump_num_sections; ++i) {
/* Read the fadump section from memory */
io_result = address_space_read(default_as, next_section_addr, attrs,
&regions[i], sizeof(regions[i]));
if (io_result != MEMTX_OK) {
qemu_log_mask(LOG_UNIMP,
"FADump: Unable to read fadump %dth section\n", i);
return RTAS_OUT_PARAM_ERROR;
}
next_section_addr += sizeof(regions[i]);
}
spapr->fadump_registered = true;
spapr->fadump_dump_active = false;
/* Store the registered fadump memory struct */
spapr->registered_fdm.header = header;
for (int i = 0; i < dump_num_sections; ++i) {
spapr->registered_fdm.rgn[i] = regions[i];
}
return RTAS_OUT_SUCCESS;
}
+71
View File
@@ -344,6 +344,73 @@ static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu,
rtas_st(rets, 0, ret);
}
/* Papr Section 7.4.9 ibm,configure-kernel-dump RTAS call */
static void rtas_configure_kernel_dump(PowerPCCPU *cpu,
SpaprMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
{
target_ulong cmd = rtas_ld(args, 0);
uint32_t ret_val;
/* Number of outputs has to be 1 */
if (nret != 1) {
qemu_log_mask(LOG_GUEST_ERROR,
"FADump: ibm,configure-kernel-dump called with nret != 1.\n");
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
/* Number of inputs has to be 3 */
if (nargs != 3) {
qemu_log_mask(LOG_GUEST_ERROR,
"FADump: ibm,configure-kernel-dump called with nargs != 3.\n");
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
switch (cmd) {
case FADUMP_CMD_REGISTER:
ret_val = do_fadump_register(spapr, args);
if (ret_val != RTAS_OUT_SUCCESS) {
rtas_st(rets, 0, ret_val);
return;
}
break;
case FADUMP_CMD_UNREGISTER:
if (spapr->fadump_dump_active) {
rtas_st(rets, 0, RTAS_OUT_DUMP_ACTIVE);
return;
}
spapr->fadump_registered = false;
spapr->fadump_dump_active = false;
memset(&spapr->registered_fdm, 0, sizeof(spapr->registered_fdm));
break;
case FADUMP_CMD_INVALIDATE:
if (!spapr->fadump_dump_active) {
qemu_log_mask(LOG_GUEST_ERROR,
"FADump: Nothing to invalidate, no dump active\n");
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
}
spapr->fadump_registered = false;
spapr->fadump_dump_active = false;
memset(&spapr->registered_fdm, 0, sizeof(spapr->registered_fdm));
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"FADump: Unknown command: " TARGET_FMT_lu "\n", cmd);
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
static void rtas_ibm_os_term(PowerPCCPU *cpu,
SpaprMachineState *spapr,
uint32_t token, uint32_t nargs,
@@ -659,6 +726,10 @@ static void core_rtas_register_types(void)
spapr_rtas_register(RTAS_IBM_NMI_INTERLOCK, "ibm,nmi-interlock",
rtas_ibm_nmi_interlock);
/* Register fadump rtas call */
spapr_rtas_register(RTAS_CONFIGURE_KERNEL_DUMP, "ibm,configure-kernel-dump",
rtas_configure_kernel_dump);
qtest_set_command_cb(spapr_qtest_callback);
}
+10 -1
View File
@@ -13,6 +13,7 @@
#include "hw/ppc/xics.h" /* For ICSState */
#include "hw/ppc/spapr_tpm_proxy.h"
#include "hw/ppc/spapr_nested.h" /* For SpaprMachineStateNested */
#include "hw/ppc/spapr_fadump.h" /* For FadumpMemStruct */
struct SpaprVioBus;
struct SpaprPhbState;
@@ -267,6 +268,11 @@ struct SpaprMachineState {
Error *fwnmi_migration_blocker;
SpaprWatchdog wds[WDT_MAX_WATCHDOGS];
/* Fadump State */
bool fadump_registered;
bool fadump_dump_active;
FadumpMemStruct registered_fdm;
};
#define H_SUCCESS 0
@@ -692,6 +698,8 @@ void push_sregs_to_kvm_pr(SpaprMachineState *spapr);
#define RTAS_OUT_PARAM_ERROR -3
#define RTAS_OUT_NOT_SUPPORTED -3
#define RTAS_OUT_NO_SUCH_INDICATOR -3
#define RTAS_OUT_DUMP_ALREADY_REGISTERED -9
#define RTAS_OUT_DUMP_ACTIVE -10
#define RTAS_OUT_NOT_AUTHORIZED -9002
#define RTAS_OUT_SYSPARM_PARAM_ERROR -9999
@@ -754,8 +762,9 @@ void push_sregs_to_kvm_pr(SpaprMachineState *spapr);
#define RTAS_IBM_SUSPEND_ME (RTAS_TOKEN_BASE + 0x2A)
#define RTAS_IBM_NMI_REGISTER (RTAS_TOKEN_BASE + 0x2B)
#define RTAS_IBM_NMI_INTERLOCK (RTAS_TOKEN_BASE + 0x2C)
#define RTAS_CONFIGURE_KERNEL_DUMP (RTAS_TOKEN_BASE + 0x2D)
#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x2D)
#define RTAS_TOKEN_MAX (RTAS_TOKEN_BASE + 0x2E)
/* RTAS ibm,get-system-parameter token values */
#define RTAS_SYSPARM_SPLPAR_CHARACTERISTICS 20
+69
View File
@@ -0,0 +1,69 @@
/*
* Firmware Assisted Dump in PSeries
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PPC_SPAPR_FADUMP_H
#define PPC_SPAPR_FADUMP_H
#include "qemu/osdep.h"
#include "cpu.h"
/* Fadump commands */
#define FADUMP_CMD_REGISTER 1
#define FADUMP_CMD_UNREGISTER 2
#define FADUMP_CMD_INVALIDATE 3
#define FADUMP_VERSION 1
/*
* The Firmware Assisted Dump Memory structure supports a maximum of 10 sections
* in the dump memory structure. Presently, three sections are used for
* CPU state data, HPTE & Parameters area, while the remaining seven sections
* can be used for boot memory regions.
*/
#define FADUMP_MAX_SECTIONS 10
typedef struct FadumpSection FadumpSection;
typedef struct FadumpSectionHeader FadumpSectionHeader;
typedef struct FadumpMemStruct FadumpMemStruct;
struct SpaprMachineState;
/* Kernel Dump section info */
/* All fields are in big-endian */
struct FadumpSection {
uint32_t request_flag;
uint16_t source_data_type;
uint16_t error_flags;
uint64_t source_address;
uint64_t source_len;
uint64_t bytes_dumped;
uint64_t destination_address;
};
/* ibm,configure-kernel-dump header. */
struct FadumpSectionHeader {
uint32_t dump_format_version;
uint16_t dump_num_sections;
uint16_t dump_status_flag;
uint32_t offset_first_dump_section;
/* Fields for disk dump option. */
uint32_t dd_block_size;
uint64_t dd_block_offset;
uint64_t dd_num_blocks;
uint32_t dd_offset_disk_path;
/* Maximum time allowed to prevent an automatic dump-reboot. */
uint32_t max_time_auto;
};
/* Note: All the data in these structures is in big-endian */
struct FadumpMemStruct {
FadumpSectionHeader header;
FadumpSection rgn[FADUMP_MAX_SECTIONS];
};
uint32_t do_fadump_register(struct SpaprMachineState *, target_ulong);
#endif /* PPC_SPAPR_FADUMP_H */