summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hmp-commands-info.hx16
-rw-r--r--include/monitor/hmp-target.h1
-rw-r--r--target-i386/cpu.h3
-rw-r--r--target-i386/helper.c191
-rw-r--r--target-i386/monitor.c6
5 files changed, 217 insertions, 0 deletions
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index 94a2afefd5..b02876c83c 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -112,6 +112,22 @@ STEXI
Show the cpu registers.
ETEXI
+#if defined(TARGET_I386)
+ {
+ .name = "lapic",
+ .args_type = "",
+ .params = "",
+ .help = "show local apic state",
+ .mhandler.cmd = hmp_info_local_apic,
+ },
+#endif
+
+STEXI
+@item info lapic
+@findex lapic
+Show local APIC state
+ETEXI
+
{
.name = "cpus",
.args_type = "",
diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h
index c64f523eb5..be50577b37 100644
--- a/include/monitor/hmp-target.h
+++ b/include/monitor/hmp-target.h
@@ -42,5 +42,6 @@ CPUState *mon_get_cpu(void);
void hmp_info_mem(Monitor *mon, const QDict *qdict);
void hmp_info_tlb(Monitor *mon, const QDict *qdict);
void hmp_mce(Monitor *mon, const QDict *qdict);
+void hmp_info_local_apic(Monitor *mon, const QDict *qdict);
#endif /* MONITOR_COMMON */
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 5231e8c300..527eb99632 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -1353,4 +1353,7 @@ void enable_compat_apic_id_mode(void);
#define APIC_DEFAULT_ADDRESS 0xfee00000
#define APIC_SPACE_SIZE 0x100000
+void x86_cpu_dump_local_apic_state(CPUState *cs, FILE *f,
+ fprintf_function cpu_fprintf, int flags);
+
#endif /* CPU_I386_H */
diff --git a/target-i386/helper.c b/target-i386/helper.c
index 5480a96a0f..9364d96f96 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -23,6 +23,7 @@
#ifndef CONFIG_USER_ONLY
#include "sysemu/sysemu.h"
#include "monitor/monitor.h"
+#include "hw/i386/apic_internal.h"
#endif
static void cpu_x86_version(CPUX86State *env, int *family, int *model)
@@ -177,6 +178,196 @@ done:
cpu_fprintf(f, "\n");
}
+#ifndef CONFIG_USER_ONLY
+
+/* ARRAY_SIZE check is not required because
+ * DeliveryMode(dm) has a size of 3 bit.
+ */
+static inline const char *dm2str(uint32_t dm)
+{
+ static const char *str[] = {
+ "Fixed",
+ "...",
+ "SMI",
+ "...",
+ "NMI",
+ "INIT",
+ "...",
+ "ExtINT"
+ };
+ return str[dm];
+}
+
+static void dump_apic_lvt(FILE *f, fprintf_function cpu_fprintf,
+ const char *name, uint32_t lvt, bool is_timer)
+{
+ uint32_t dm = (lvt & APIC_LVT_DELIV_MOD) >> APIC_LVT_DELIV_MOD_SHIFT;
+ cpu_fprintf(f,
+ "%s\t 0x%08x %s %-5s %-6s %-7s %-12s %-6s",
+ name, lvt,
+ lvt & APIC_LVT_INT_POLARITY ? "active-lo" : "active-hi",
+ lvt & APIC_LVT_LEVEL_TRIGGER ? "level" : "edge",
+ lvt & APIC_LVT_MASKED ? "masked" : "",
+ lvt & APIC_LVT_DELIV_STS ? "pending" : "",
+ !is_timer ?
+ "" : lvt & APIC_LVT_TIMER_PERIODIC ?
+ "periodic" : lvt & APIC_LVT_TIMER_TSCDEADLINE ?
+ "tsc-deadline" : "one-shot",
+ dm2str(dm));
+ if (dm != APIC_DM_NMI) {
+ cpu_fprintf(f, " (vec %u)\n", lvt & APIC_VECTOR_MASK);
+ } else {
+ cpu_fprintf(f, "\n");
+ }
+}
+
+/* ARRAY_SIZE check is not required because
+ * destination shorthand has a size of 2 bit.
+ */
+static inline const char *shorthand2str(uint32_t shorthand)
+{
+ const char *str[] = {
+ "no-shorthand", "self", "all-self", "all"
+ };
+ return str[shorthand];
+}
+
+static inline uint8_t divider_conf(uint32_t divide_conf)
+{
+ uint8_t divide_val = ((divide_conf & 0x8) >> 1) | (divide_conf & 0x3);
+
+ return divide_val == 7 ? 1 : 2 << divide_val;
+}
+
+static inline void mask2str(char *str, uint32_t val, uint8_t size)
+{
+ while (size--) {
+ *str++ = (val >> size) & 1 ? '1' : '0';
+ }
+ *str = 0;
+}
+
+#define MAX_LOGICAL_APIC_ID_MASK_SIZE 16
+
+static void dump_apic_icr(FILE *f, fprintf_function cpu_fprintf,
+ APICCommonState *s, CPUX86State *env)
+{
+ uint32_t icr = s->icr[0], icr2 = s->icr[1];
+ uint8_t dest_shorthand = \
+ (icr & APIC_ICR_DEST_SHORT) >> APIC_ICR_DEST_SHORT_SHIFT;
+ bool logical_mod = icr & APIC_ICR_DEST_MOD;
+ char apic_id_str[MAX_LOGICAL_APIC_ID_MASK_SIZE + 1];
+ uint32_t dest_field;
+ bool x2apic;
+
+ cpu_fprintf(f, "ICR\t 0x%08x %s %s %s %s\n",
+ icr,
+ logical_mod ? "logical" : "physical",
+ icr & APIC_ICR_TRIGGER_MOD ? "level" : "edge",
+ icr & APIC_ICR_LEVEL ? "assert" : "de-assert",
+ shorthand2str(dest_shorthand));
+
+ cpu_fprintf(f, "ICR2\t 0x%08x", icr2);
+ if (dest_shorthand != 0) {
+ cpu_fprintf(f, "\n");
+ return;
+ }
+ x2apic = env->features[FEAT_1_ECX] & CPUID_EXT_X2APIC;
+ dest_field = x2apic ? icr2 : icr2 >> APIC_ICR_DEST_SHIFT;
+
+ if (!logical_mod) {
+ if (x2apic) {
+ cpu_fprintf(f, " cpu %u (X2APIC ID)\n", dest_field);
+ } else {
+ cpu_fprintf(f, " cpu %u (APIC ID)\n",
+ dest_field & APIC_LOGDEST_XAPIC_ID);
+ }
+ return;
+ }
+
+ if (s->dest_mode == 0xf) { /* flat mode */
+ mask2str(apic_id_str, icr2 >> APIC_ICR_DEST_SHIFT, 8);
+ cpu_fprintf(f, " mask %s (APIC ID)\n", apic_id_str);
+ } else if (s->dest_mode == 0) { /* cluster mode */
+ if (x2apic) {
+ mask2str(apic_id_str, dest_field & APIC_LOGDEST_X2APIC_ID, 16);
+ cpu_fprintf(f, " cluster %u mask %s (X2APIC ID)\n",
+ dest_field >> APIC_LOGDEST_X2APIC_SHIFT, apic_id_str);
+ } else {
+ mask2str(apic_id_str, dest_field & APIC_LOGDEST_XAPIC_ID, 4);
+ cpu_fprintf(f, " cluster %u mask %s (APIC ID)\n",
+ dest_field >> APIC_LOGDEST_XAPIC_SHIFT, apic_id_str);
+ }
+ }
+}
+
+static void dump_apic_interrupt(FILE *f, fprintf_function cpu_fprintf,
+ const char *name, uint32_t *ireg_tab,
+ uint32_t *tmr_tab)
+{
+ int i, empty = true;
+
+ cpu_fprintf(f, "%s\t ", name);
+ for (i = 0; i < 256; i++) {
+ if (apic_get_bit(ireg_tab, i)) {
+ cpu_fprintf(f, "%u%s ", i,
+ apic_get_bit(tmr_tab, i) ? "(level)" : "");
+ empty = false;
+ }
+ }
+ cpu_fprintf(f, "%s\n", empty ? "(none)" : "");
+}
+
+void x86_cpu_dump_local_apic_state(CPUState *cs, FILE *f,
+ fprintf_function cpu_fprintf, int flags)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ APICCommonState *s = APIC_COMMON(cpu->apic_state);
+ uint32_t *lvt = s->lvt;
+
+ cpu_fprintf(f, "dumping local APIC state for CPU %-2u\n\n",
+ CPU(cpu)->cpu_index);
+ dump_apic_lvt(f, cpu_fprintf, "LVT0", lvt[APIC_LVT_LINT0], false);
+ dump_apic_lvt(f, cpu_fprintf, "LVT1", lvt[APIC_LVT_LINT1], false);
+ dump_apic_lvt(f, cpu_fprintf, "LVTPC", lvt[APIC_LVT_PERFORM], false);
+ dump_apic_lvt(f, cpu_fprintf, "LVTERR", lvt[APIC_LVT_ERROR], false);
+ dump_apic_lvt(f, cpu_fprintf, "LVTTHMR", lvt[APIC_LVT_THERMAL], false);
+ dump_apic_lvt(f, cpu_fprintf, "LVTT", lvt[APIC_LVT_TIMER], true);
+
+ cpu_fprintf(f, "Timer\t DCR=0x%x (divide by %u) initial_count = %u\n",
+ s->divide_conf & APIC_DCR_MASK,
+ divider_conf(s->divide_conf),
+ s->initial_count);
+
+ cpu_fprintf(f, "SPIV\t 0x%08x APIC %s, focus=%s, spurious vec %u\n",
+ s->spurious_vec,
+ s->spurious_vec & APIC_SPURIO_ENABLED ? "enabled" : "disabled",
+ s->spurious_vec & APIC_SPURIO_FOCUS ? "on" : "off",
+ s->spurious_vec & APIC_VECTOR_MASK);
+
+ dump_apic_icr(f, cpu_fprintf, s, &cpu->env);
+
+ cpu_fprintf(f, "ESR\t 0x%08x\n", s->esr);
+
+ dump_apic_interrupt(f, cpu_fprintf, "ISR", s->isr, s->tmr);
+ dump_apic_interrupt(f, cpu_fprintf, "IRR", s->irr, s->tmr);
+
+ cpu_fprintf(f, "\nAPR 0x%02x TPR 0x%02x DFR 0x%02x LDR 0x%02x",
+ s->arb_id, s->tpr, s->dest_mode, s->log_dest);
+ if (s->dest_mode == 0) {
+ cpu_fprintf(f, "(cluster %u: id %u)",
+ s->log_dest >> APIC_LOGDEST_XAPIC_SHIFT,
+ s->log_dest & APIC_LOGDEST_XAPIC_ID);
+ }
+ cpu_fprintf(f, " PPR 0x%02x\n", apic_get_ppr(s));
+}
+#else
+void x86_cpu_dump_local_apic_state(CPUState *cs, FILE *f,
+ fprintf_function cpu_fprintf, int flags)
+{
+}
+#endif /* !CONFIG_USER_ONLY */
+
#define DUMP_CODE_BYTES_TOTAL 50
#define DUMP_CODE_BYTES_BACKWARD 20
diff --git a/target-i386/monitor.c b/target-i386/monitor.c
index 6ac86360e9..9479a770fa 100644
--- a/target-i386/monitor.c
+++ b/target-i386/monitor.c
@@ -492,3 +492,9 @@ const MonitorDef *target_monitor_defs(void)
{
return monitor_defs;
}
+
+void hmp_info_local_apic(Monitor *mon, const QDict *qdict)
+{
+ x86_cpu_dump_local_apic_state(mon_get_cpu(), (FILE *)mon, monitor_fprintf,
+ CPU_DUMP_FPU);
+}