Watchdog and kernel profiling for AMD
- a different set of MSRs and performance counters is used on AMD - when initializing NMI watchdog the test for Intel architecture performance counters feature only applies to Intel now - NMI is enabled if the CPU belongs to a family which has the performance counters that we use
This commit is contained in:
parent
274fcf8d1f
commit
1786291e32
2 changed files with 95 additions and 24 deletions
|
@ -9,27 +9,28 @@
|
||||||
|
|
||||||
#define CPUID_UNHALTED_CORE_CYCLES_AVAILABLE 0
|
#define CPUID_UNHALTED_CORE_CYCLES_AVAILABLE 0
|
||||||
|
|
||||||
#define MSR_PERFMON_CRT0 0xc1
|
#define INTEL_MSR_PERFMON_CRT0 0xc1
|
||||||
#define MSR_PERFMON_SEL0 0x186
|
#define INTEL_MSR_PERFMON_SEL0 0x186
|
||||||
|
|
||||||
#define MSR_PERFMON_SEL0_ENABLE (1 << 22)
|
#define INTEL_MSR_PERFMON_SEL0_ENABLE (1 << 22)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Intel architecture performance counters watchdog
|
* Intel architecture performance counters watchdog
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PRIVATE struct arch_watchdog intel_arch_watchdog;
|
PRIVATE struct arch_watchdog intel_arch_watchdog;
|
||||||
|
PRIVATE struct arch_watchdog amd_watchdog;
|
||||||
|
|
||||||
PRIVATE void intel_arch_watchdog_init(const unsigned cpu)
|
PRIVATE void intel_arch_watchdog_init(const unsigned cpu)
|
||||||
{
|
{
|
||||||
u64_t cpuf;
|
u64_t cpuf;
|
||||||
u32_t val;
|
u32_t val;
|
||||||
|
|
||||||
ia32_msr_write(MSR_PERFMON_CRT0, 0, 0);
|
ia32_msr_write(INTEL_MSR_PERFMON_CRT0, 0, 0);
|
||||||
|
|
||||||
/* Int, OS, USR, Core ccyles */
|
/* Int, OS, USR, Core ccyles */
|
||||||
val = 1 << 20 | 1 << 17 | 1 << 16 | 0x3c;
|
val = 1 << 20 | 1 << 17 | 1 << 16 | 0x3c;
|
||||||
ia32_msr_write(MSR_PERFMON_SEL0, 0, val);
|
ia32_msr_write(INTEL_MSR_PERFMON_SEL0, 0, val);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* should give as a tick approx. every 0.5-1s, the perf counter has only
|
* should give as a tick approx. every 0.5-1s, the perf counter has only
|
||||||
|
@ -38,11 +39,13 @@ PRIVATE void intel_arch_watchdog_init(const unsigned cpu)
|
||||||
cpuf = cpu_get_freq(cpu);
|
cpuf = cpu_get_freq(cpu);
|
||||||
while (cpuf.hi || cpuf.lo > 0x7fffffffU)
|
while (cpuf.hi || cpuf.lo > 0x7fffffffU)
|
||||||
cpuf = div64u64(cpuf, 2);
|
cpuf = div64u64(cpuf, 2);
|
||||||
watchdog->resetval = watchdog->watchdog_resetval = cpuf.lo;
|
cpuf.lo = -cpuf.lo;
|
||||||
|
watchdog->resetval = watchdog->watchdog_resetval = cpuf;
|
||||||
|
|
||||||
ia32_msr_write(MSR_PERFMON_CRT0, 0, -cpuf.lo);
|
ia32_msr_write(INTEL_MSR_PERFMON_CRT0, 0, cpuf.lo);
|
||||||
|
|
||||||
ia32_msr_write(MSR_PERFMON_SEL0, 0, val | MSR_PERFMON_SEL0_ENABLE);
|
ia32_msr_write(INTEL_MSR_PERFMON_SEL0, 0,
|
||||||
|
val | INTEL_MSR_PERFMON_SEL0_ENABLE);
|
||||||
|
|
||||||
/* unmask the performance counter interrupt */
|
/* unmask the performance counter interrupt */
|
||||||
lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
|
lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
|
||||||
|
@ -51,7 +54,7 @@ PRIVATE void intel_arch_watchdog_init(const unsigned cpu)
|
||||||
PRIVATE void intel_arch_watchdog_reinit(const unsigned cpu)
|
PRIVATE void intel_arch_watchdog_reinit(const unsigned cpu)
|
||||||
{
|
{
|
||||||
lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
|
lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
|
||||||
ia32_msr_write(MSR_PERFMON_CRT0, 0, -watchdog->resetval);
|
ia32_msr_write(INTEL_MSR_PERFMON_CRT0, 0, watchdog->resetval.lo);
|
||||||
}
|
}
|
||||||
|
|
||||||
PUBLIC int arch_watchdog_init(void)
|
PUBLIC int arch_watchdog_init(void)
|
||||||
|
@ -63,20 +66,31 @@ PUBLIC int arch_watchdog_init(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
eax = 0xA;
|
if (machine.cpu_type.vendor == CPU_VENDOR_INTEL) {
|
||||||
|
eax = 0xA;
|
||||||
|
|
||||||
_cpuid(&eax, &ebx, &ecx, &edx);
|
_cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
|
||||||
/* FIXME currently we support only watchdog based on the intel
|
/* FIXME currently we support only watchdog based on the intel
|
||||||
* architectural performance counters. Some Intel CPUs don't have this
|
* architectural performance counters. Some Intel CPUs don't have this
|
||||||
* feature
|
* feature
|
||||||
*/
|
*/
|
||||||
if (ebx & (1 << CPUID_UNHALTED_CORE_CYCLES_AVAILABLE))
|
if (ebx & (1 << CPUID_UNHALTED_CORE_CYCLES_AVAILABLE))
|
||||||
|
return -1;
|
||||||
|
if (!((((eax >> 8)) & 0xff) > 0))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
watchdog = &intel_arch_watchdog;
|
||||||
|
} else if (machine.cpu_type.vendor == CPU_VENDOR_AMD) {
|
||||||
|
if (machine.cpu_type.family != 6 &&
|
||||||
|
machine.cpu_type.family != 15 &&
|
||||||
|
machine.cpu_type.family != 16 &&
|
||||||
|
machine.cpu_type.family != 17)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
watchdog = &amd_watchdog;
|
||||||
|
} else
|
||||||
return -1;
|
return -1;
|
||||||
if (!((((eax >> 8)) & 0xff) > 0))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
watchdog = &intel_arch_watchdog;
|
|
||||||
|
|
||||||
/* Setup PC overflow as NMI for watchdog, it is masked for now */
|
/* Setup PC overflow as NMI for watchdog, it is masked for now */
|
||||||
lapic_write(LAPIC_LVTPCR, APIC_ICR_INT_MASK | APIC_ICR_DM_NMI);
|
lapic_write(LAPIC_LVTPCR, APIC_ICR_INT_MASK | APIC_ICR_DM_NMI);
|
||||||
|
@ -160,7 +174,8 @@ PRIVATE int intel_arch_watchdog_profile_init(const unsigned freq)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
watchdog->profile_resetval = cpuf.lo;
|
cpuf.lo = -cpuf.lo;
|
||||||
|
watchdog->profile_resetval = cpuf;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -170,3 +185,59 @@ PRIVATE struct arch_watchdog intel_arch_watchdog = {
|
||||||
/*.reinit = */ intel_arch_watchdog_reinit,
|
/*.reinit = */ intel_arch_watchdog_reinit,
|
||||||
/*.profile_init = */ intel_arch_watchdog_profile_init
|
/*.profile_init = */ intel_arch_watchdog_profile_init
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define AMD_MSR_EVENT_SEL0 0xc0010000
|
||||||
|
#define AMD_MSR_EVENT_CTR0 0xc0010004
|
||||||
|
#define AMD_MSR_EVENT_SEL0_ENABLE (1 << 22)
|
||||||
|
|
||||||
|
PRIVATE void amd_watchdog_init(const unsigned cpu)
|
||||||
|
{
|
||||||
|
u64_t cpuf;
|
||||||
|
u32_t val;
|
||||||
|
|
||||||
|
ia32_msr_write(AMD_MSR_EVENT_CTR0, 0, 0);
|
||||||
|
|
||||||
|
/* Int, OS, USR, Cycles cpu is running */
|
||||||
|
val = 1 << 20 | 1 << 17 | 1 << 16 | 0x76;
|
||||||
|
ia32_msr_write(AMD_MSR_EVENT_SEL0, 0, val);
|
||||||
|
|
||||||
|
cpuf = cpu_get_freq(cpu);
|
||||||
|
neg64(cpuf);
|
||||||
|
watchdog->resetval = watchdog->watchdog_resetval = cpuf;
|
||||||
|
|
||||||
|
ia32_msr_write(AMD_MSR_EVENT_CTR0,
|
||||||
|
watchdog->resetval.hi, watchdog->resetval.lo);
|
||||||
|
|
||||||
|
ia32_msr_write(AMD_MSR_EVENT_SEL0, 0,
|
||||||
|
val | AMD_MSR_EVENT_SEL0_ENABLE);
|
||||||
|
|
||||||
|
/* unmask the performance counter interrupt */
|
||||||
|
lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
|
||||||
|
}
|
||||||
|
|
||||||
|
PRIVATE void amd_watchdog_reinit(const unsigned cpu)
|
||||||
|
{
|
||||||
|
lapic_write(LAPIC_LVTPCR, APIC_ICR_DM_NMI);
|
||||||
|
ia32_msr_write(AMD_MSR_EVENT_CTR0,
|
||||||
|
watchdog->resetval.hi, watchdog->resetval.lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
PRIVATE int amd_watchdog_profile_init(const unsigned freq)
|
||||||
|
{
|
||||||
|
u64_t cpuf;
|
||||||
|
|
||||||
|
/* FIXME works only if all CPUs have the same freq */
|
||||||
|
cpuf = cpu_get_freq(cpuid);
|
||||||
|
cpuf = div64u64(cpuf, freq);
|
||||||
|
|
||||||
|
neg64(cpuf);
|
||||||
|
watchdog->profile_resetval = cpuf;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRIVATE struct arch_watchdog amd_watchdog = {
|
||||||
|
/*.init = */ amd_watchdog_init,
|
||||||
|
/*.reinit = */ amd_watchdog_reinit,
|
||||||
|
/*.profile_init = */ amd_watchdog_profile_init
|
||||||
|
};
|
||||||
|
|
|
@ -20,9 +20,9 @@ struct arch_watchdog {
|
||||||
arch_watchdog_method_t init; /* initial setup */
|
arch_watchdog_method_t init; /* initial setup */
|
||||||
arch_watchdog_method_t reinit; /* reinit after a tick */
|
arch_watchdog_method_t reinit; /* reinit after a tick */
|
||||||
arch_watchdog_profile_init_t profile_init;
|
arch_watchdog_profile_init_t profile_init;
|
||||||
unsigned resetval;
|
u64_t resetval;
|
||||||
unsigned watchdog_resetval;
|
u64_t watchdog_resetval;
|
||||||
unsigned profile_resetval;
|
u64_t profile_resetval;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct arch_watchdog *watchdog;
|
extern struct arch_watchdog *watchdog;
|
||||||
|
|
Loading…
Reference in a new issue