arm64 linux bad mode Serror 系統異常
最近在linux4.9 arm64遇到了bad mode的kernel oops
oops的內容大概如下,而且oops過後看起來系統並沒有異常,oops出現的概率比較小,而且每次oops列印的資訊中task 都是不同的,oops之後,看起來大部分時候系統還是可以正常執行的
[ 1259.654597] Bad mode in Error handler detected, code 0xbf000002 -- SError [ 1259.661357] CPU: 12 PID: 2293 Comm: mate-settings-d Not tainted 4.1.15-1.el7.aarch64 #2 [ 1259.669320] Hardware name: xxxx [ 1259.675209] task: ffffffc8c9bd1700 ti: ffffffc8c9e4c000 task.ti: ffffffc8c9e4c000 [ 1259.682665] PC is at 0x7f9c51abd8 [ 1259.685961] LR is at 0x7f942f9828 [ 1259.689259] pc : [<0000007f9c51abd8>] lr : [<0000007f942f9828>] pstate: 80000000 [ 1259.696616] sp : ffffffc8c9e4fff0
首先先定位出現這段LOG的程式碼位置在traps.c,arm64的異常向量表中才有對這個函式的呼叫,如下圖
bad_mode 的列印資訊SError 和 code 都是從register esr_el1中獲取的,能得到SError的只有el1 和el0的invalid_error列印的
/arch/arm64/kernel/traps.c /* * bad_mode handles the impossible case in the exception vector. This is always * fatal. */ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr) { console_verbose(); pr_crit("Bad mode in %s handler detected on CPU%d, code 0x%08x -- %s\n", handler[reason], smp_processor_id(), esr, esr_get_class_string(esr)); die("Oops - bad mode", regs, 0); local_irq_disable(); panic("bad mode"); } /arch/arm64/kernel/entry.s /* * Bad Abort numbers *----------------- */ #define BAD_SYNC 0 #define BAD_IRQ 1 #define BAD_FIQ 2 #define BAD_ERROR 3 //el1_sync 函式 el1_sync: el1_inv: // TODO: add support for undefined instructions in kernel mode enable_dbg mov x0, sp mov x2, x1 mov x1, #BAD_SYNC b bad_mode ENDPROC(el1_sync) //inv_entry 函式 /* * Invalid mode handlers */ .macro inv_entry, el, reason, regsize = 64 kernel_entry \el, \regsize mov x0, sp mov x1, #\reason mrs x2, esr_el1//bad_mode 函式的引數傳遞 b bad_mode .endm el0_sync_invalid: inv_entry 0, BAD_SYNC ENDPROC(el0_sync_invalid) el0_irq_invalid: inv_entry 0, BAD_IRQ ENDPROC(el0_irq_invalid) el0_fiq_invalid: inv_entry 0, BAD_FIQ ENDPROC(el0_fiq_invalid) el0_error_invalid: inv_entry 0, BAD_ERROR ENDPROC(el0_error_invalid) #ifdef CONFIG_COMPAT el0_fiq_invalid_compat: inv_entry 0, BAD_FIQ, 32 ENDPROC(el0_fiq_invalid_compat) el0_error_invalid_compat: inv_entry 0, BAD_ERROR, 32 ENDPROC(el0_error_invalid_compat) #endif el1_sync_invalid: inv_entry 1, BAD_SYNC ENDPROC(el1_sync_invalid) el1_irq_invalid: inv_entry 1, BAD_IRQ ENDPROC(el1_irq_invalid) el1_fiq_invalid: inv_entry 1, BAD_FIQ ENDPROC(el1_fiq_invalid) el1_error_invalid: inv_entry 1, BAD_ERROR ENDPROC(el1_error_invalid)
到底SError是ARM的一種什麼異常,AArch64(ARM64)架構中,主要包括如下4中型別的異常:
- Synchronous exception(同步異常),“同步”可以理解為:發生異常的指令為導致異常的指令,即當導致異常發生的指令執行時能立即觸發異常。 包括ARM架構中定義的所有Aborts異常,如:指令異常、資料異常、對齊異常等。
- SError,System Error,是一種非同步異常,後面再仔細說明。
- IRQ,普通的中斷,是非同步異常。
- FIQ,高優先順序的中斷,是非同步異常。
SError本質上是一種非同步外部abort(asynchronous external abort)。所謂非同步,就說是發生異常時硬體(相關的暫存器)不能提供有效資訊用於分析定位,異常發生時的指令,並不是導致異常的指令。外部意味著異常來自於外部儲存系統(相較於CPU來說,MMU是內部的)。通常是硬體觸發的問題,比如硬體的clock出問題或者硬體本身的問題導致的bus訪問硬體時出現問題。
Linux核心中,對SError進行了捕獲,設定了相應的中斷向量,當並未做實際的處理,只是上報異常,並終止程序會核心,因為對於核心來說,SError是致命的,核心自身無法做相應的修復操作,核心不知道具體原因,也不知道如何修復。
分析錯誤列印: Bad mode in Error handler detected, code 0xbf000002 – SError
可以知道,發生了SError(也就是System Error異常),錯誤碼(ESR暫存器內容)為:0xbf000002
從ARM developer上查到的ESR暫存器的描述
The ESR_EL1 holds syndrome information for an exception taken to EL1.
ESR_EL1 is a 32-bit register, and is part of the Exception and fault handling registers functional group.
Figure B2-34 ESR_EL1 bit assignments
EC, [31:26]
Exception Class. Indicates the reason for the exception that this register holds information about.
IL, [25]
Instruction Length for synchronous exceptions. The possible values are:
|
16-bit. |
1 |
32-bit. |
This field is 1
for the SError interrupt, instruction aborts, misaligned PC, Stack pointer misalignment, data aborts for which the ISV bit is 0
, exceptions caused by an illegal instruction set state, and exceptions using the 0x00
Exception Class.
ISS, [24:0]
Syndrome information.
由以上資訊得出前六位bit[31:26]為101111,對應具體的異常型別,檢視ArmV8手冊:
101111 SError interrupt即系統異常中斷。
ISV, bit [24] Instruction syndrome valid. Indicates whether the rest of the syndrome information in this register is valid. 0 No valid instruction syndrome. ISS[23:0] are RES0. 1 ISS[23:0] hold a valid instruction syndrome.
本code中,該位為1,說明bit[23:0]中存放了instruction syndrome(出錯指令的具體資訊)
IS, bits [23:0] IMPLEMENTATION DEFINED syndrome information that can be used to provide additional information about the SError interrupt. Only valid if bit[24] of this register is 1. If bit[24] is 0, this field is RES0.
ELR_ELn n代表不同的執行優先順序,這個暫存器儲存著觸發異常時候的指令
如何dump 該register嘗試改了下entry.s,只有inv_entry 這裡會有BAD_ERROR的異常會走到,所以做了如下修改,發現show_regs列印的異常發生的時候的PC就是從elr_el1的copy。
所以bad_mode dump的資訊就是all kernel have了,沒有想到有效的方法,只能壓測看下error附近的LOG試著夾下code
/*
* Invalid mode handlers
*/
.macro inv_entry, el, reason, regsize = 64
kernel_entry \el, \regsize
mov x0, sp
mov x1, #\reason
mrs x2, esr_el1
mrs x3, elr_el1
//add this line to pass para to bad_mode function
//SError always call this function
b bad_mode
.endm
asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr, u64 elr_regs)
//add a para
{
siginfo_t info;
void __user *pc = (void __user *)instruction_pointer(regs);
console_verbose();
pr_crit("Bad mode in %s handler detected on CPU%d, code 0x%08x -- %s\n",
handler[reason], smp_processor_id(), esr,
esr_get_class_string(esr));
pr_cirt("dump elr %llx\n",elr_regs);
//add printk to dump this register
__show_regs(regs);