@samitouri / QOSamiQemu / commits / 0991b30c31

pnv/mpipl: Preserve CPU registers after crash

Kernel expects the platform to provide CPU registers after pausing execution of the CPUs. Currently only exporting the registers, used by Linux, for generating the /proc/vmcore Reviewed-by: Hari Bathini <hbathini@linux.ibm.com> Reviewed-by: Sourabh Jain <sourabhjain@linux.ibm.com> Signed-off-by: Aditya Gupta <adityag@linux.ibm.com> Tested-by: Shivang Upadhyay <shivangu@linux.ibm.com> Link: https://lore.kernel.org/qemu-devel/20260424083837.214947-6-adityag@linux.ibm.com Signed-off-by: Harsh Prateek Bora <harshpb@linux.ibm.com>

Aditya Gupta committed Apr 24, 2026 at 14:08 UTC 0991b30c31cdf16e61412c372ac12c76189c5c7b
2 files changed +214
hw/ppc/pnv_mpipl.c
+154
@@ -8,6 +8,9 @@
8 #include "qemu/log.h"
9 #include "qemu/units.h"
10 #include "system/address-spaces.h"
11 +#include "system/cpus.h"
12 +#include "system/hw_accel.h"
13 +#include "system/memory.h"
14 #include "system/runstate.h"
15 #include "hw/ppc/pnv.h"
16 #include "hw/ppc/pnv_mpipl.h"
@@ -17,6 +20,8 @@
20 (pnv->mpipl_state.skiboot_base + MDST_TABLE_OFF)
21 #define MDDT_TABLE_RELOCATED \
22 (pnv->mpipl_state.skiboot_base + MDDT_TABLE_OFF)
23 +#define PROC_DUMP_RELOCATED \
24 + (pnv->mpipl_state.skiboot_base + PROC_DUMP_AREA_OFF)
25
26 /*
27 * Preserve the memory regions as pointed by MDST table
@@ -169,9 +174,158 @@ static bool pnv_mpipl_preserve_mem(PnvMachineState *pnv)
174 return true;
175 }
176
177 +static void do_store_cpu_regs(CPUState *cpu, MpiplPreservedCPUState *state)
178 +{
179 + CPUPPCState *env = cpu_env(cpu);
180 + MpiplRegDataHdr *regs_hdr = &state->hdr;
181 + MpiplRegEntry *reg_entries = state->reg_entries;
182 + MpiplRegEntry *curr_reg_entry;
183 + uint32_t num_saved_regs = 0;
184 +
185 + cpu_synchronize_state(cpu);
186 +
187 + regs_hdr->pir = cpu_to_be32(env->spr[SPR_PIR]);
188 +
189 + /* QEMU CPUs are not in Power Saving Mode */
190 + regs_hdr->core_state = 0xff;
191 +
192 + regs_hdr->off_regentries = 0;
193 + regs_hdr->num_regentries = cpu_to_be32(NUM_REGS_PER_CPU);
194 +
195 + regs_hdr->alloc_size = cpu_to_be32(sizeof(MpiplRegEntry));
196 + regs_hdr->act_size = cpu_to_be32(sizeof(MpiplRegEntry));
197 +
198 +#define REG_TYPE_GPR 0x1
199 +#define REG_TYPE_SPR 0x2
200 +#define REG_TYPE_TIMA 0x3
201 +
202 +/*
203 + * ID numbers used by f/w while populating certain registers
204 + *
205 + * Copied these defines from the linux kernel
206 + */
207 +#define REG_ID_NIP 0x7D0
208 +#define REG_ID_MSR 0x7D1
209 +#define REG_ID_CCR 0x7D2
210 +
211 + curr_reg_entry = reg_entries;
212 +
213 +#define REG_ENTRY(type, num, val) \
214 + do { \
215 + curr_reg_entry->reg_type = cpu_to_be32(type); \
216 + curr_reg_entry->reg_num = cpu_to_be32(num); \
217 + curr_reg_entry->reg_val = cpu_to_be64(val); \
218 + ++curr_reg_entry; \
219 + ++num_saved_regs; \
220 + } while (0)
221 +
222 + /* Save the GPRs */
223 + for (int gpr_id = 0; gpr_id < 32; ++gpr_id) {
224 + REG_ENTRY(REG_TYPE_GPR, gpr_id, env->gpr[gpr_id]);
225 + }
226 +
227 + REG_ENTRY(REG_TYPE_SPR, SPR_ACOP, env->spr[SPR_ACOP]);
228 + REG_ENTRY(REG_TYPE_SPR, SPR_AMR, env->spr[SPR_AMR]);
229 + REG_ENTRY(REG_TYPE_SPR, SPR_BESCR, env->spr[SPR_BESCR]);
230 + REG_ENTRY(REG_TYPE_SPR, SPR_CFAR, env->spr[SPR_CFAR]);
231 + REG_ENTRY(REG_TYPE_SPR, SPR_CIABR, env->spr[SPR_CIABR]);
232 +
233 + REG_ENTRY(REG_TYPE_SPR, SPR_CTR, env->spr[SPR_CTR]);
234 + REG_ENTRY(REG_TYPE_SPR, SPR_CTRL, env->spr[SPR_CTRL]);
235 + REG_ENTRY(REG_TYPE_SPR, SPR_DABR, env->spr[SPR_DABR]);
236 + REG_ENTRY(REG_TYPE_SPR, SPR_DABRX, env->spr[SPR_DABRX]);
237 + REG_ENTRY(REG_TYPE_SPR, SPR_DAR, env->spr[SPR_DAR]);
238 + REG_ENTRY(REG_TYPE_SPR, SPR_DAWR0, env->spr[SPR_DAWR0]);
239 + REG_ENTRY(REG_TYPE_SPR, SPR_DAWR1, env->spr[SPR_DAWR1]);
240 + REG_ENTRY(REG_TYPE_SPR, SPR_DAWRX0, env->spr[SPR_DAWRX0]);
241 + REG_ENTRY(REG_TYPE_SPR, SPR_DAWRX1, env->spr[SPR_DAWRX1]);
242 + REG_ENTRY(REG_TYPE_SPR, SPR_DPDES, env->spr[SPR_DPDES]);
243 + REG_ENTRY(REG_TYPE_SPR, SPR_DSCR, env->spr[SPR_DSCR]);
244 + REG_ENTRY(REG_TYPE_SPR, SPR_DSISR, env->spr[SPR_DSISR]);
245 + REG_ENTRY(REG_TYPE_SPR, SPR_EBBHR, env->spr[SPR_EBBHR]);
246 + REG_ENTRY(REG_TYPE_SPR, SPR_EBBRR, env->spr[SPR_EBBRR]);
247 +
248 + REG_ENTRY(REG_TYPE_SPR, SPR_FSCR, env->spr[SPR_FSCR]);
249 +
250 + REG_ENTRY(REG_TYPE_SPR, SPR_CTR, env->ctr);
251 + REG_ENTRY(REG_TYPE_SPR, SPR_DAR, env->spr[SPR_DAR]);
252 + REG_ENTRY(REG_TYPE_SPR, SPR_DSISR, env->spr[SPR_DSISR]);
253 + REG_ENTRY(REG_TYPE_SPR, SPR_LR, env->lr);
254 + REG_ENTRY(REG_TYPE_SPR, REG_ID_MSR, env->msr);
255 + REG_ENTRY(REG_TYPE_SPR, REG_ID_NIP, env->nip);
256 + REG_ENTRY(REG_TYPE_SPR, SPR_XER, env->xer);
257 + REG_ENTRY(REG_TYPE_SPR, SPR_SRR0, env->spr[SPR_SRR0]);
258 + REG_ENTRY(REG_TYPE_SPR, SPR_SRR1, env->spr[SPR_SRR1]);
259 + REG_ENTRY(REG_TYPE_SPR, SPR_HSRR0, env->spr[SPR_HSRR0]);
260 + REG_ENTRY(REG_TYPE_SPR, SPR_HSRR1, env->spr[SPR_HSRR1]);
261 + REG_ENTRY(REG_TYPE_SPR, SPR_CFAR, env->spr[SPR_CFAR]);
262 + REG_ENTRY(REG_TYPE_SPR, SPR_HMER, env->spr[SPR_HMER]);
263 + REG_ENTRY(REG_TYPE_SPR, SPR_HMEER, env->spr[SPR_HMEER]);
264 +
265 + /*
266 + * Ensure the number of registers saved match the number of
267 + * registers per cpu
268 + *
269 + * This will help catch an error if in future a new register entry
270 + * is added/removed while not modifying NUM_PER_CPU_REGS
271 + */
272 + assert(num_saved_regs == NUM_REGS_PER_CPU);
273 +}
274 +
275 +static bool pnv_mpipl_preserve_cpu_state(PnvMachineState *pnv)
276 +{
277 + MachineState *machine = MACHINE(pnv);
278 + uint32_t num_cpus = machine->smp.cpus;
279 + MpiplPreservedCPUState *state;
280 + CPUState *cpu;
281 + AddressSpace *default_as = &address_space_memory;
282 + MemTxResult io_result;
283 + MemTxAttrs attrs;
284 +
285 + /* Mark the memory transactions as privileged memory access */
286 + attrs.user = 0;
287 + attrs.memory = 1;
288 +
289 + if (pnv->mpipl_state.cpu_states) {
290 + /*
291 + * CPU States might have been allocated from some past crash, free the
292 + * memory to preven memory leak
293 + */
294 + g_free(pnv->mpipl_state.cpu_states);
295 + pnv->mpipl_state.num_cpu_states = 0;
296 + }
297 +
298 + pnv->mpipl_state.cpu_states = g_malloc_n(num_cpus,
299 + sizeof(MpiplPreservedCPUState));
300 + pnv->mpipl_state.num_cpu_states = num_cpus;
301 +
302 + state = pnv->mpipl_state.cpu_states;
303 +
304 + /* Preserve the Processor Dump Area */
305 + io_result = address_space_read(default_as, PROC_DUMP_RELOCATED, attrs,
306 + &pnv->mpipl_state.proc_area, sizeof(MpiplProcDumpArea));
307 + if (io_result != MEMTX_OK) {
308 + qemu_log_mask(LOG_GUEST_ERROR,
309 + "MPIPL: Failed to read Proc Dump Area at: 0x" TARGET_FMT_lx "\n",
310 + PROC_DUMP_RELOCATED);
311 +
312 + return false;
313 + }
314 +
315 + CPU_FOREACH(cpu) {
316 + do_store_cpu_regs(cpu, state);
317 + ++state;
318 + }
319 +
320 + return true;
321 +}
322 +
323 void do_mpipl_preserve(PnvMachineState *pnv)
324 {
325 + pause_all_vcpus();
326 +
327 pnv_mpipl_preserve_mem(pnv);
328 + pnv_mpipl_preserve_cpu_state(pnv);
329
330 /* Mark next boot as Memory-preserving boot */
331 pnv->mpipl_state.is_next_boot_mpipl = true;
include/hw/ppc/pnv_mpipl.h
+60
@@ -17,6 +17,10 @@
17 typedef struct MdstTableEntry MdstTableEntry;
18 typedef struct MdrtTableEntry MdrtTableEntry;
19 typedef struct MpiplPreservedState MpiplPreservedState;
20 +typedef struct MpiplRegDataHdr MpiplRegDataHdr;
21 +typedef struct MpiplRegEntry MpiplRegEntry;
22 +typedef struct MpiplProcDumpArea MpiplProcDumpArea;
23 +typedef struct MpiplPreservedCPUState MpiplPreservedCPUState;
24
25 /*
26 * Following offsets are copied from skiboot source code.
@@ -49,6 +53,8 @@ typedef struct MpiplPreservedState MpiplPreservedState;
53 /* HRMOR_BIT copied from skiboot */
54 #define HRMOR_BIT (1ull << 63)
55
56 +#define NUM_REGS_PER_CPU 66 /*(32 GPRs, 34 SPRs)*/
57 +
58 /*
59 * Memory Dump Source Table (MDST)
60 *
@@ -95,6 +101,55 @@ static_assert(MDST_MAX_ENTRIES == MDDT_MAX_ENTRIES,
101 static_assert(MDRT_MAX_ENTRIES >= MDST_MAX_ENTRIES,
102 "MDRT should support atleast having number of entries as in MDST");
103
104 +/*
105 + * Processor Dump Area
106 + *
107 + * This contains the information needed for having processor
108 + * state captured during a platform dump.
109 + *
110 + * As mentioned in HDAT, following the P9 specific format
111 + */
112 +struct MpiplProcDumpArea {
113 + uint32_t thread_size; /* Size of each thread register entry */
114 +#define PROC_DUMP_AREA_VERSION_P9 0x1 /* P9 format */
115 + uint8_t version;
116 + uint8_t reserved[11];
117 + uint64_t alloc_addr; /* Destination memory to place register data */
118 + uint32_t reserved2;
119 + uint32_t alloc_size; /* Allocated size */
120 + uint64_t dest_addr; /* Destination address */
121 + uint32_t reserved3;
122 + uint32_t act_size; /* Actual data size */
123 +} QEMU_PACKED;
124 +
125 +/*
126 + * "Architected Register Data" in the HDAT spec
127 + *
128 + * Acts as a header to the register entries for a particular thread
129 + */
130 +struct MpiplRegDataHdr {
131 + uint32_t pir; /* PIR of thread */
132 + uint8_t core_state; /* Stop state of the overall core */
133 + uint8_t reserved[3];
134 + uint32_t off_regentries; /* Offset to Register Entries Array */
135 + uint32_t num_regentries; /* Number of Register Entries in Array */
136 + uint32_t alloc_size; /* Allocated size for each Register Entry */
137 + uint32_t act_size; /* Actual size for each Register Entry */
138 +} QEMU_PACKED;
139 +
140 +struct MpiplRegEntry {
141 + uint32_t reg_type;
142 + uint32_t reg_num;
143 + uint64_t reg_val;
144 +} QEMU_PACKED;
145 +
146 +struct MpiplPreservedCPUState {
147 + MpiplRegDataHdr hdr;
148 +
149 + /* Length of 'reg_entries' is hdr.num_regentries */
150 + MpiplRegEntry reg_entries[NUM_REGS_PER_CPU];
151 +};
152 +
153 /* Preserved state to be saved in PnvMachineState */
154 struct MpiplPreservedState {
155 /* skiboot_base will be valid only after OPAL sends relocated base to SBE */
@@ -103,6 +158,11 @@ struct MpiplPreservedState {
158
159 MdrtTableEntry *mdrt_table;
160 uint32_t num_mdrt_entries;
161 +
162 + MpiplProcDumpArea proc_area;
163 +
164 + MpiplPreservedCPUState *cpu_states;
165 + uint32_t num_cpu_states;
166 };
167
168 #endif