mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
®ions[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;
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user