@samitouri / QOSamiQemu / commits / 7aff577f0e

pnv/mpipl: Write the preserved CPU and MDRT state

Logic for preserving the CPU registers and memory regions has been done in previous patches. Write those data at the relevant memory address, such as PROC_DUMP_AREA for CPU registers, and MDRT for preserved memory regions. Also export "mpipl-boot" device tree node, for kernel to know that it's a 'dump active' boot 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-8-adityag@linux.ibm.com Signed-off-by: Harsh Prateek Bora <harshpb@linux.ibm.com>

Aditya Gupta committed Apr 24, 2026 at 14:08 UTC 7aff577f0ee07831cf8f441f66a2149f39dfe3c3
3 files changed +179 -1
hw/ppc/pnv.c
+38 -1
@@ -750,10 +750,47 @@ static void pnv_reset(MachineState *machine, ResetType type)
750 {
751 PnvMachineState *pnv = PNV_MACHINE(machine);
752 void *fdt;
753 + int node_offset;
754 + bool mpipl_write_succeeded = false;
755
756 qemu_devices_reset(type);
757
756 - if (!pnv->mpipl_state.is_next_boot_mpipl) {
758 + /*
759 + * Only on success of writing MPIPL data will the next boot be provided
760 + * "mpipl-boot" property in device tree
761 + * Otherwise boot like a normal non-MPIPL boot
762 + */
763 + if (pnv->mpipl_state.is_next_boot_mpipl) {
764 + /* Write the preserved MDRT and CPU State Data */
765 + mpipl_write_succeeded = do_mpipl_write(pnv);
766 + }
767 +
768 + /*
769 + * If it's a MPIPL boot, add the "mpipl-boot" property, and reset the
770 + * boolean for MPIPL boot for next boot
771 + */
772 + if (mpipl_write_succeeded) {
773 + void *fdt_copy = g_malloc0(FDT_MAX_SIZE);
774 +
775 + /* Create a writable copy of the fdt */
776 + _FDT((fdt_open_into(fdt, fdt_copy, FDT_MAX_SIZE)));
777 +
778 + node_offset = fdt_path_offset(fdt_copy, "/ibm,opal/dump");
779 + _FDT((fdt_appendprop_u64(fdt_copy, node_offset, "mpipl-boot", 1)));
780 +
781 + /* Update the fdt, and free the original fdt */
782 + if (fdt != machine->fdt) {
783 + /*
784 + * Only free the fdt if it's not machine->fdt, to prevent
785 + * double free, since we already free machine->fdt later
786 + */
787 + g_free(fdt);
788 + }
789 + fdt = fdt_copy;
790 +
791 + /* This boot is an MPIPL, reset the boolean for next boot */
792 + pnv->mpipl_state.is_next_boot_mpipl = false;
793 + } else {
794 /*
795 * Set the "Thread Register State Entry Size", so that firmware can
796 * allocate enough memory to capture CPU state in the event of a
hw/ppc/pnv_mpipl.c
+140
@@ -20,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 MDRT_TABLE_RELOCATED \
24 + (pnv->mpipl_state.skiboot_base + MDRT_TABLE_OFF)
25 #define PROC_DUMP_RELOCATED \
26 (pnv->mpipl_state.skiboot_base + PROC_DUMP_AREA_OFF)
27
@@ -320,6 +322,139 @@ static bool pnv_mpipl_preserve_cpu_state(PnvMachineState *pnv)
322 return true;
323 }
324
325 +/*
326 + * Write the preserved CPU state data in Processor Dump Area (PROC_DUMP_AREA)
327 + *
328 + * Returns true if everything went fine, else false for any error
329 + */
330 +static bool pnv_mpipl_write_cpu_state(PnvMachineState *pnv)
331 +{
332 + MpiplProcDumpArea *proc_area = &pnv->mpipl_state.proc_area;
333 + MpiplPreservedCPUState *cpu_state = pnv->mpipl_state.cpu_states;
334 + const uint32_t num_cpu_states = pnv->mpipl_state.num_cpu_states;
335 + hwaddr next_regentries_hdr;
336 + AddressSpace *default_as = &address_space_memory;
337 + MemTxResult io_result;
338 + MemTxAttrs attrs;
339 +
340 + /* Mark the memory transactions as privileged memory access */
341 + attrs.user = 0;
342 + attrs.memory = 1;
343 +
344 + if (be32_to_cpu(proc_area->alloc_size) <
345 + (num_cpu_states * sizeof(MpiplPreservedCPUState))) {
346 + qemu_log_mask(LOG_GUEST_ERROR,
347 + "MPIPL: Size of buffer allocate by skiboot (%u bytes) is not"
348 + "enough to save all CPUs registers needed (%zu bytes)",
349 + be32_to_cpu(proc_area->alloc_size),
350 + num_cpu_states * sizeof(MpiplPreservedCPUState));
351 +
352 + return false;
353 + }
354 +
355 + proc_area->version = PROC_DUMP_AREA_VERSION_P9;
356 +
357 + /*
358 + * This is the stride kernel/firmware should use to jump from a
359 + * register entries header to next CPU's header
360 + */
361 + proc_area->thread_size = cpu_to_be32(sizeof(MpiplPreservedCPUState));
362 +
363 + /* Write the header and register entries for each CPU */
364 + next_regentries_hdr = be64_to_cpu(proc_area->alloc_addr) & (~HRMOR_BIT);
365 + for (int i = 0; i < num_cpu_states; ++i) {
366 + io_result = address_space_write(default_as, next_regentries_hdr, attrs,
367 + &cpu_state->hdr, sizeof(MpiplRegDataHdr));
368 + if (io_result != MEMTX_OK) {
369 + qemu_log_mask(LOG_GUEST_ERROR,
370 + "MPIPL: Failed to write RegEntries Header\n");
371 + return false;
372 + }
373 +
374 + io_result = address_space_write(default_as,
375 + next_regentries_hdr + sizeof(MpiplRegDataHdr), attrs,
376 + &cpu_state->reg_entries,
377 + NUM_REGS_PER_CPU * (sizeof(MpiplRegEntry)));
378 + if (io_result != MEMTX_OK) {
379 + qemu_log_mask(LOG_GUEST_ERROR,
380 + "MPIPL: Failed to write Register Entries\n");
381 + return false;
382 + }
383 +
384 + /*
385 + * According to HDAT section:
386 + * "15.3.1.5 Architected Register Data content":
387 + *
388 + * The next register entries header will be at current header +
389 + * "Thread Register State Entry size"
390 + *
391 + * Note: proc_area.thread_size == sizeof(MpiplPreservedCPUState)
392 + */
393 + next_regentries_hdr += sizeof(MpiplPreservedCPUState);
394 + ++cpu_state;
395 + }
396 +
397 + /* Point the destination address to the preserved memory region */
398 + proc_area->dest_addr = proc_area->alloc_addr;
399 + proc_area->act_size = cpu_to_be32(num_cpu_states *
400 + sizeof(MpiplPreservedCPUState));
401 +
402 + io_result = address_space_write(default_as, PROC_DUMP_AREA_OFF, attrs,
403 + proc_area, sizeof(MpiplProcDumpArea));
404 + if (io_result != MEMTX_OK) {
405 + qemu_log_mask(LOG_GUEST_ERROR,
406 + "MPIPL: Failed to write Register Entries\n");
407 + return false;
408 + }
409 +
410 + return true;
411 +}
412 +
413 +/*
414 + * Write the preserved MDRT table, representing preserved memory regions
415 + *
416 + * Returns true if everything went fine, else false for any error
417 + */
418 +static bool pnv_mpipl_write_mdrt(PnvMachineState *pnv)
419 +{
420 + MpiplPreservedState *state = &pnv->mpipl_state;
421 + AddressSpace *default_as = &address_space_memory;
422 + MemTxResult io_result;
423 + MemTxAttrs attrs;
424 +
425 + /* Mark the memory transactions as privileged memory access */
426 + attrs.user = 0;
427 + attrs.memory = 1;
428 +
429 + /*
430 + * Generally writes from platform during MPIPL don't go to a relocated
431 + * skiboot address
432 + *
433 + * Though for MDRT we are doing so, as this is the address skiboot
434 + * considers by default for MDRT
435 + *
436 + * MDRT/MDST/MDDT base addresses are actually meant to be shared by
437 + * platform in SPIRA structures.
438 + *
439 + * Not implementing SPIRA as it increases complexity for no gains.
440 + * Using the default address skiboot expects for MDRT, which is the
441 + * relocated MDRT, hence writing to it
442 + *
443 + * Other tables like MDST/MDDT should not be written to relocated
444 + * addresses, as skiboot will overwrite anything from SKIBOOT_BASE till
445 + * SKIBOOT_BASE+SKIBOOT_SIZE (which is 0x30000000-0x31c00000 by default)
446 + */
447 + io_result = address_space_write(default_as, MDRT_TABLE_RELOCATED, attrs,
448 + state->mdrt_table,
449 + state->num_mdrt_entries * sizeof(MdrtTableEntry));
450 + if (io_result != MEMTX_OK) {
451 + qemu_log_mask(LOG_GUEST_ERROR, "MPIPL: Failed to write MDRT table\n");
452 + return false;
453 + }
454 +
455 + return true;
456 +}
457 +
458 void do_mpipl_preserve(PnvMachineState *pnv)
459 {
460 pause_all_vcpus();
@@ -340,3 +475,8 @@ void do_mpipl_preserve(PnvMachineState *pnv)
475 */
476 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
477 }
478 +
479 +bool do_mpipl_write(PnvMachineState *pnv)
480 +{
481 + return pnv_mpipl_write_mdrt(pnv) && pnv_mpipl_write_cpu_state(pnv);
482 +}
include/hw/ppc/pnv.h
+1
@@ -296,5 +296,6 @@ void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor);
296
297 /* MPIPL helpers */
298 void do_mpipl_preserve(PnvMachineState *pnv);
299 +bool do_mpipl_write(PnvMachineState *pnv);
300
301 #endif /* PPC_PNV_H */