hmp: added local apic dump state
Added the hmp command to query local apic registers state, may be usefull after guest crashes to understand IRQ routing in guest. (qemu) info lapic dumping local APIC state for CPU 0 LVT0 0x00010700 active-hi edge masked ExtINT (vec 0) LVT1 0x00000400 active-hi edge NMI LVTPC 0x00010000 active-hi edge masked Fixed (vec 0) LVTERR 0x000000fe active-hi edge Fixed (vec 254) LVTTHMR 0x00010000 active-hi edge masked Fixed (vec 0) LVTT 0x000000ef active-hi edge one-shot Fixed (vec 239) Timer DCR=0x3 (divide by 16) initial_count = 61360 SPIV 0x000001ff APIC enabled, focus=off, spurious vec 255 ICR 0x000000fd physical edge de-assert no-shorthand ICR2 0x00000001 cpu 1 (X2APIC ID) ESR 0x00000000 ISR (none) IRR 239 APR 0x00 TPR 0x00 DFR 0x0f LDR 0x00 PPR 0x00 Signed-off-by: Pavel Butsykin <pbutsykin@virtuozzo.com> Signed-off-by: Denis V. Lunev <den@openvz.org> CC: Paolo Bonzini <pbonzini@redhat.com> CC: Andreas Färber <afaerber@suse.de> Message-Id: <1442927901-1084-7-git-send-email-den@openvz.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
caf15319e8
commit
1f871d49e3
@ -110,6 +110,22 @@ STEXI
|
|||||||
@item info registers
|
@item info registers
|
||||||
@findex registers
|
@findex registers
|
||||||
Show the cpu registers.
|
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
|
ETEXI
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -42,5 +42,6 @@ CPUState *mon_get_cpu(void);
|
|||||||
void hmp_info_mem(Monitor *mon, const QDict *qdict);
|
void hmp_info_mem(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_info_tlb(Monitor *mon, const QDict *qdict);
|
void hmp_info_tlb(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_mce(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 */
|
#endif /* MONITOR_COMMON */
|
||||||
|
@ -1353,4 +1353,7 @@ void enable_compat_apic_id_mode(void);
|
|||||||
#define APIC_DEFAULT_ADDRESS 0xfee00000
|
#define APIC_DEFAULT_ADDRESS 0xfee00000
|
||||||
#define APIC_SPACE_SIZE 0x100000
|
#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 */
|
#endif /* CPU_I386_H */
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
|
#include "hw/i386/apic_internal.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void cpu_x86_version(CPUX86State *env, int *family, int *model)
|
static void cpu_x86_version(CPUX86State *env, int *family, int *model)
|
||||||
@ -177,6 +178,196 @@ done:
|
|||||||
cpu_fprintf(f, "\n");
|
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_TOTAL 50
|
||||||
#define DUMP_CODE_BYTES_BACKWARD 20
|
#define DUMP_CODE_BYTES_BACKWARD 20
|
||||||
|
|
||||||
|
@ -492,3 +492,9 @@ const MonitorDef *target_monitor_defs(void)
|
|||||||
{
|
{
|
||||||
return monitor_defs;
|
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);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user