target/ppc/cpu.h | 3 + target/ppc/cpu_init.c | 1 + target/ppc/excp_helper.c | 116 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+)
PPC CPU has TYPE_INTERRUPT_STATS_PROVIDER interface but it does not
implement the print_info function. This causes 'info pic' to print
a line like:
Interrupt controller information not available for
power10_v2.0-powerpc64-cpu.
Add a print_info panel for CPUs with irq delivery status.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
target/ppc/cpu.h | 3 +
target/ppc/cpu_init.c | 1 +
target/ppc/excp_helper.c | 116 +++++++++++++++++++++++++++++++++++++++
3 files changed, 120 insertions(+)
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index ff14f5b8a7f..dca84ca23cd 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -27,6 +27,7 @@
#include "cpu-qom.h"
#include "qom/object.h"
#include "hw/registerfields.h"
+#include "hw/intc/intc.h"
#define CPU_RESOLVING_TYPE TYPE_POWERPC_CPU
@@ -1604,6 +1605,7 @@ int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
void ppc_maybe_interrupt(CPUPPCState *env);
void ppc_cpu_do_interrupt(CPUState *cpu);
bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
+void ppc_cpu_irq_print_info(InterruptStatsProvider *obj, GString *buf);
void ppc_cpu_do_system_reset(CPUState *cs);
void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector);
extern const VMStateDescription vmstate_ppc_cpu;
@@ -2686,6 +2688,7 @@ enum {
#endif
/* Hardware exceptions definitions */
+/* Keep powerpc_intr_name in sync */
enum {
/* External hardware exception sources */
PPC_INTERRUPT_RESET = 0x00001, /* Reset exception */
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index b3d6599abd2..36742136309 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -7527,6 +7527,7 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data)
#ifndef CONFIG_USER_ONLY
cc->sysemu_ops = &ppc_sysemu_ops;
INTERRUPT_STATS_PROVIDER_CLASS(oc)->get_statistics = ppc_get_irq_stats;
+ INTERRUPT_STATS_PROVIDER_CLASS(oc)->print_info = ppc_cpu_irq_print_info;
/* check_prot_access_type relies on MMU access and PAGE bits relations */
qemu_build_assert(MMU_DATA_LOAD == 0 && MMU_DATA_STORE == 1 &&
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 1890ec9ccb6..7ea5798e95b 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -104,6 +104,49 @@ static const char *powerpc_excp_name(int excp)
}
}
+static const char *powerpc_intr_name(uint32_t intr)
+{
+ switch (intr) {
+ case PPC_INTERRUPT_RESET: return "RSET";
+ case PPC_INTERRUPT_WAKEUP: return "WAKE";
+ case PPC_INTERRUPT_MCK: return "MCHK";
+ case PPC_INTERRUPT_EXT: return "EXTN";
+ case PPC_INTERRUPT_SMI: return "SMI";
+ case PPC_INTERRUPT_CEXT: return "CEXT";
+ case PPC_INTERRUPT_DEBUG: return "DEBG";
+ case PPC_INTERRUPT_THERM: return "THRM";
+ case PPC_INTERRUPT_DECR: return "DECR";
+ case PPC_INTERRUPT_HDECR: return "HDEC";
+ case PPC_INTERRUPT_PIT: return "PIT";
+ case PPC_INTERRUPT_FIT: return "FIT";
+ case PPC_INTERRUPT_WDT: return "WDT";
+ case PPC_INTERRUPT_CDOORBELL: return "CDBL";
+ case PPC_INTERRUPT_DOORBELL: return "DBL";
+ case PPC_INTERRUPT_PERFM: return "PMU";
+ case PPC_INTERRUPT_HMI: return "HMI";
+ case PPC_INTERRUPT_HDOORBELL: return "HDBL";
+ case PPC_INTERRUPT_HVIRT: return "HVRT";
+ case PPC_INTERRUPT_EBB: return "EBB";
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static bool powerpc_is_hv_intr(uint32_t intr)
+{
+ switch (intr) {
+ case PPC_INTERRUPT_RESET:
+ case PPC_INTERRUPT_MCK:
+ case PPC_INTERRUPT_HDECR:
+ case PPC_INTERRUPT_HMI:
+ case PPC_INTERRUPT_HDOORBELL:
+ case PPC_INTERRUPT_HVIRT:
+ return true;
+ default:
+ return false;
+ }
+}
+
static void dump_syscall(CPUPPCState *env)
{
qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64
@@ -2438,6 +2481,79 @@ static void ppc_deliver_interrupt(CPUPPCState *env, int interrupt)
}
}
+void ppc_cpu_irq_print_info(InterruptStatsProvider *obj, GString *buf)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(obj);
+ CPUPPCState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
+ const char *priv1 = "";
+ const char *priv2;
+ bool none;
+ int i;
+
+ g_string_append_printf(buf, "CPU[%x] interrupt info\n", cs->cpu_index);
+
+ g_string_append_printf(buf, " state:%s",
+ cs->halted ? "stopped" : "running");
+ if (env->resume_as_sreset) {
+ g_string_append_printf(buf, "(wake with sreset)");
+ }
+
+ if (FIELD_EX64(env->msr, MSR, PR)) {
+ priv2 = "user";
+ } else {
+ priv2 = "privileged";
+ }
+ if (env->has_hv_mode) {
+ if (FIELD_EX64_HV(env->msr)) {
+ priv1 = "host-";
+ } else {
+ priv1 = "guest-";
+ }
+ }
+ g_string_append_printf(buf, " mode:%s%s\n", priv1, priv2);
+
+ if (env->has_hv_mode) {
+ g_string_append_printf(buf, " hypervisor irqs:%s\n",
+ !FIELD_EX64_HV(env->msr) || FIELD_EX64(env->msr, MSR, EE) ?
+ "enabled" : "disabled");
+ }
+ g_string_append_printf(buf, " supervisor irqs:%s\n",
+ FIELD_EX64(env->msr, MSR, EE) ? "enabled" : "disabled");
+
+ if (env->has_hv_mode) {
+ none = true;
+ g_string_append_printf(buf, " pending hypervisor interrupts: ");
+ for (i = 0; i < 32; i++) {
+ uint32_t intr = (1U << i);
+ if (powerpc_is_hv_intr(intr) && (env->pending_interrupts & intr)) {
+ none = false;
+ g_string_append_printf(buf, "%s ", powerpc_intr_name(intr));
+ }
+ }
+ if (none) {
+ g_string_append_printf(buf, "none\n");
+ } else {
+ g_string_append_printf(buf, "\n");
+ }
+ }
+
+ none = true;
+ g_string_append_printf(buf, " pending supervisor interrupts: ");
+ for (i = 0; i < 32; i++) {
+ uint32_t intr = (1U << i);
+ if (!powerpc_is_hv_intr(intr) && (env->pending_interrupts & intr)) {
+ none = false;
+ g_string_append_printf(buf, "%s ", powerpc_intr_name(intr));
+ }
+ }
+ if (none) {
+ g_string_append_printf(buf, "none\n");
+ } else {
+ g_string_append_printf(buf, "\n");
+ }
+}
+
/*
* system reset is not delivered via normal irq method, so have to set
* halted = 0 to resume CPU running if it was halted. Possibly we should
--
2.47.1
© 2016 - 2025 Red Hat, Inc.