x86: optionally show last exception from/to register contents (v2)
PATCH 1/1 x86: optionally show last exception from/to register contents (v2)
.. when dumping register state. This is particularly useful when gcc managed to tail-call optimize an indirect call which happens to hit a NULL (or otherwise invalid) pointer.
Documentation/kernel-parameters.txt | 3 +++ arch/i386/kernel/cpu/amd.c | 4 ++++ arch/i386/kernel/cpu/common.c | 2 ++ arch/i386/kernel/cpu/intel.c | 20 ++++++++++++++------ arch/i386/kernel/traps.c | 35 +++++++++++++++++++++++++++++++++++ arch/x86_64/kernel/setup.c | 23 ++++++++++++++++++----- arch/x86_64/kernel/traps.c | 34 ++++++++++++++++++++++++++++++++++ include/asm-i386/msr-index.h | 3 +++ include/asm-i386/processor.h | 4 ++++ include/asm-x86_64/msr.h | 6 ++++++ include/asm-x86_64/processor.h | 3 +++ 11 files changed, 126 insertions(+), 11 deletions(-)
Documentation/kernel-parameters.txt +3 -0
@@ -1155,6 +1155,9 @@ and is between 256 and 4096 characters. nolapic_timer [X86-32,APIC] Do not use the local APIC timer. + noler [X86-32/X86-64] Do not print last exception records + with kernel register dumps. + noltlbs [PPC] Do not use large page/tlb entries for kernel lowmem mapping on PPC40x.
arch/i386/kernel/cpu/amd.c +4 -0
@@ -238,9 +238,13 @@ static void __cpuinit init_amd(struct cpcase 0x10 :case 0x11 :set_bit (X86_FEATURE_K8 , c->x86_capability); +if (ler_enabled) +__get_cpu_var (ler_msr) =MSR_IA32_LASTINTFROMIP ;break ;case 6 :set_bit (X86_FEATURE_K7 , c->x86_capability); +if (ler_enabled) +__get_cpu_var (ler_msr) =MSR_IA32_LASTINTFROMIP ;break ; }if (c->x86 >=6 )
arch/i386/kernel/cpu/common.c +2 -0
@@ -503,6 +503,8 @@ static void __cpuinit identify_cpu(struc/* Init Machine Check Exception if available. */ mcheck_init (c); + +ler_enable (); }void __init identify_boot_cpu (void )
arch/i386/kernel/cpu/intel.c +14 -6
@@ -188,15 +188,23 @@ static void __cpuinit init_intel(struct }#endif -if (c->x86 ==15 ) { +switch (c->x86) { +case 15 :set_bit (X86_FEATURE_P4 , c->x86_capability);set_bit (X86_FEATURE_SYNC_RDTSC , c->x86_capability); - } -if (c->x86 ==6 ) +if (c->x86_model >=0x03 ) +set_bit (X86_FEATURE_CONSTANT_TSC , c->x86_capability); +if (ler_enabled) +__get_cpu_var (ler_msr) =MSR_P4_LER_FROM_LIP ; +break ; +case 6 :set_bit (X86_FEATURE_P3 , c->x86_capability); -if ((c->x86 ==0xf && c->x86_model >=0x03 ) || - (c->x86 ==0x6 && c->x86_model >=0x0e )) -set_bit (X86_FEATURE_CONSTANT_TSC , c->x86_capability); +if (c->x86_model >=0x0e ) +set_bit (X86_FEATURE_CONSTANT_TSC , c->x86_capability); +if (ler_enabled) +__get_cpu_var (ler_msr) =MSR_IA32_LASTINTFROMIP ; +break ; + }if (cpu_has_ds) {unsigned int l1;
arch/i386/kernel/traps.c +35 -0
@@ -321,6 +321,20 @@ void show_registers(struct pt_regs *regsunsigned int code_len = code_bytes;unsigned char c; +if (oops_in_progress &&__get_cpu_var (ler_msr)) { +u32 from, to, hi; + +if (rdmsr_safe (__get_cpu_var (ler_msr), &from, &hi) ==0 + &&rdmsr_safe (__get_cpu_var (ler_msr) +1 , &to, &hi) ==0 ) { +printk ("\n" KERN_EMERG +"last branch before last exception/interrupt\n" ); +printk (KERN_EMERG " from %08x" , from); +print_symbol (" (%s)\n" , from); +printk (KERN_EMERG " to %08x" , to); +print_symbol (" (%s)" , to); + }else +__get_cpu_var (ler_msr) =0 ; + }printk ("\n" KERN_EMERG "Stack: " );show_stack_log_lvl (NULL , regs, (unsigned long *)esp,KERN_EMERG );
@@ -360,6 +374,19 @@ int is_valid_bugaddr(unsigned long eip)return ud2 ==0x0b0f ; } +DEFINE_PER_CPU (u32, ler_msr); +int ler_enabled =1 ; + +void ler_enable (void ) { +if (__get_cpu_var (ler_msr)) { +u32 lo, hi; + +if (rdmsr_safe (MSR_IA32_DEBUGCTLMSR , &lo, &hi) <0 + ||wrmsr_safe (MSR_IA32_DEBUGCTLMSR , lo |1 , hi) <0 ) +__get_cpu_var (ler_msr) =0 ; + } +} +/* * This is gone through when something in the kernel has done something bad and * is about to be terminated.
@@ -846,6 +873,7 @@ fastcall void __kprobes do_debug(structstruct task_struct *tsk = current;get_debugreg (condition,6 ); +ler_enable ();if (notify_die (DIE_DEBUG ,"debug" , regs, condition, error_code,SIGTRAP ) ==NOTIFY_STOP )
@@ -1239,3 +1267,10 @@ static int __init code_bytes_setup(charreturn 1 ; }__setup ("code_bytes=" , code_bytes_setup); + +static int __init ler_setup (char *s) +{ + ler_enabled =0 ; +return 1 ; +} +__setup ("noler" , ler_setup);
arch/x86_64/kernel/setup.c +18 -5
@@ -617,6 +617,9 @@ static void __cpuinit init_amd(struct cp/* Family 10 doesn't support C states in MWAIT so don't use it */ if (c->x86 ==0x10 && !force_mwait)clear_bit (X86_FEATURE_MWAIT , &c->x86_capability); + +if (ler_enabled && c->x86 <=17 ) +__get_cpu_var (ler_msr) =MSR_IA32_LASTINTFROMIP ; }static void __cpuinit detect_ht (struct cpuinfo_x86 *c)
@@ -736,13 +739,22 @@ static void __cpuinit init_intel(struct c->x86_phys_bits =36 ; } -if (c->x86 ==15 ) +switch (c->x86) { +case 15 : c->x86_cache_alignment = c->x86_clflush_size *2 ; -if ((c->x86 ==0xf && c->x86_model >=0x03 ) || - (c->x86 ==0x6 && c->x86_model >=0x0e )) -set_bit (X86_FEATURE_CONSTANT_TSC , &c->x86_capability); -if (c->x86 ==6 ) +if (c->x86_model >=0x03 ) +set_bit (X86_FEATURE_CONSTANT_TSC , &c->x86_capability); +if (ler_enabled) +__get_cpu_var (ler_msr) =MSR_P4_LER_FROM_LIP ; +break ; +case 6 : +if (c->x86_model >=0x0e ) +set_bit (X86_FEATURE_CONSTANT_TSC , &c->x86_capability);set_bit (X86_FEATURE_REP_GOOD , &c->x86_capability); +if (ler_enabled) +__get_cpu_var (ler_msr) =MSR_IA32_LASTINTFROMIP ; +break ; + }if (c->x86 ==15 )set_bit (X86_FEATURE_SYNC_RDTSC , &c->x86_capability);else
@@ -906,6 +918,7 @@ void __cpuinit identify_cpu(struct cpuin#ifdef CONFIG_NUMA numa_add_cpu (smp_processor_id ());#endif +ler_enable (); }
arch/x86_64/kernel/traps.c +34 -0
@@ -426,6 +426,19 @@ void show_registers(struct pt_regs *regs* time of the fault.. */ if (in_kernel) { +if (oops_in_progress &&__get_cpu_var (ler_msr)) { +u64 from, to; + +if (checking_rdmsrl (__get_cpu_var (ler_msr), from) ==0 + &&checking_rdmsrl (__get_cpu_var (ler_msr) +1 , to) ==0 ) { +printk ("last branch before last exception/interrupt\n" ); +printk (" from " ); +printk_address (from); +printk (" to " ); +printk_address (to); + }else +__get_cpu_var (ler_msr) =0 ; + }printk ("Stack: " );_show_stack (NULL , regs, (unsigned long *)rsp);
@@ -464,6 +477,19 @@ void out_of_line_bug(void)EXPORT_SYMBOL (out_of_line_bug);#endif +DEFINE_PER_CPU (u32, ler_msr); +int ler_enabled =1 ; + +void ler_enable (void ) { +if (__get_cpu_var (ler_msr)) { +u32 lo, hi; + +if (rdmsr_safe (MSR_IA32_DEBUGCTLMSR , &lo, &hi) <0 + ||wrmsr_safe (MSR_IA32_DEBUGCTLMSR , lo |1 , hi) <0 ) +__get_cpu_var (ler_msr) =0 ; + } +} +static DEFINE_SPINLOCK (die_lock);static int die_owner =-1 ;static unsigned int die_nest_count;
@@ -849,6 +875,7 @@ asmlinkage void __kprobes do_debug(strucsiginfo_t info;get_debugreg (condition,6 ); +ler_enable ();if (notify_die (DIE_DEBUG ,"debug" , regs, condition, error_code,SIGTRAP ) ==NOTIFY_STOP )
@@ -1136,3 +1163,10 @@ static int __init kstack_setup(char *s)return 0 ; }early_param ("kstack" , kstack_setup); + +static int __init ler_setup (char *s) +{ + ler_enabled =0 ; +return 1 ; +} +__setup ("noler" , ler_setup);
include/asm-i386/msr-index.h +3 -0
@@ -63,6 +63,9 @@#define MSR_IA32_LASTINTFROMIP 0x000001dd#define MSR_IA32_LASTINTTOIP 0x000001de +#define MSR_P4_LER_FROM_LIP 0x000001d7 +#define MSR_P4_LER_TO_LIP 0x000001d8 +#define MSR_IA32_MC0_CTL 0x00000400#define MSR_IA32_MC0_STATUS 0x00000401#define MSR_IA32_MC0_ADDR 0x00000402
include/asm-i386/processor.h +4 -0
@@ -643,6 +643,10 @@ static inline unsigned int cpuid_edx(unsreturn edx; } +DECLARE_PER_CPU (u32, ler_msr); +extern int ler_enabled; +void ler_enable (void ); +/* generic versions from gas */ #define GENERIC_NOP1 ".byte 0x90\n"#define GENERIC_NOP2 ".byte 0x89,0xf6\n"
include/asm-x86_64/msr.h +6 -0
@@ -63,6 +63,12 @@ :"c"(msr), "i"(-EIO), "0"(0)); \ ret__; }) +#define checking_rdmsrl (msr,val) ({ \ + u32 lo__, hi__; \ + int rc__ = rdmsr_safe(msr, &lo__, &hi__); \ + val = lo__ | ((u64)hi__ << 32); \ + rc__; }) +#define rdtsc (low,high) \ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
include/asm-x86_64/processor.h +3 -0
@@ -333,6 +333,9 @@ struct extended_sigtable {struct extended_signature sigs[0 ]; }; +DECLARE_PER_CPU (u32, ler_msr); +extern int ler_enabled; +void ler_enable (void );#define ASM_NOP1 K8_NOP1#define ASM_NOP2 K8_NOP2
Quoted 3 lines · On Tuesday 28 August 2007 12:44:31 Jan Beulich wrote:
.. when dumping register state. This is particularly useful when gcc managed to tail-call optimize an indirect call which happens to hit a NULL (or otherwise invalid) pointer.
I added it, with a warning in the documentation and changelog
Also all the other patches except for the #DF one. Thanks.
BTW I think there is a reasonable cheap way to avoid the interrupt race:
If pda->irqcount was split into two 16bit words and irq entry code increases the upper 16bit word (that should be nearly free because because both are in L1 cache; with some luck the CPU can even dual-issue). Then the traps code could save the irq count early and then in the no_context case disable interrupts, compare and only print LER if there were no interrupts inbetween.
-Andi