问题描述:
- ctyunos系统,鲲鹏机器
- 执行reboot时,100%必现crash
堆栈如下:
[ 1396.877810] reboot: Restarting system |
EFI runtime service是一种由固件提供的功能,可以在任何CPU模式和任何时间使用,即使在退出UEFI boot service之后也可以。EFI runtime service可以为操作系统提供一些有用的功能,例如重启/关闭系统,获取/设置当前时间,管理变量等。
34 void efi_reboot(enum reboot_mode reboot_mode, const char *__unused) 33 { 32 |---const char *str[] = { "cold", "warm", "shutdown", "platform" }; 31 |---int efi_mode, cap_reset_mode; 30 29 |---if (!efi_enabled(EFI_RUNTIME_SERVICES)) 28 |---|---return; 27 26 |---switch (reboot_mode) { 25 |---case REBOOT_WARM: 24 |---case REBOOT_SOFT: 23 |---|---efi_mode = EFI_RESET_WARM; 22 |---|---break; 21 |---default: 20 |---|---efi_mode = EFI_RESET_COLD; 19 |---|---break; 18 |---} 17 16 |---/* 15 |--- * If a quirk forced an EFI reset mode, always use that. 14 |--- */ 13 |---if (efi_reboot_quirk_mode != -1) 12 |---|---efi_mode = efi_reboot_quirk_mode; 11 10 |---if (efi_capsule_pending(&cap_reset_mode)) { 9 |---|---if (efi_mode != cap_reset_mode) 8 |---|---|---printk(KERN_CRIT "efi: %s reset requested but pending " 7 |---|---|--- "capsule update requires %s reset... Performing " 6 |---|---|--- "%s reset.\n", str[efi_mode], str[cap_reset_mode], 5 |---|---|--- str[cap_reset_mode]); 4 |---|---efi_mode = cap_reset_mode; 3 |---} 2 1 |---efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL); 47 } 14 static void virt_efi_reset_system(int reset_type, 13 |---|---|---|--- efi_status_t status, 12 |---|---|---|--- unsigned long data_size, 11 |---|---|---|--- efi_char16_t *data) 10 { 9 |---if (down_interruptible(&efi_runtime_lock)) { 8 |---|---pr_warn("failed to invoke the reset_system() runtime service:\n" 7 |---|---|---"could not get exclusive access to the firmware\n"); 6 |---|---return; 5 |---} 4 |---__efi_call_virt(reset_system, reset_type, status, data_size, data); 3 |---up(&efi_runtime_lock); 2 } 45 #define __efi_call_virt(f, args...) \ 1 |---__efi_call_virt_pointer(efi.systab->runtime, f, args) 20 #define __efi_call_virt_pointer(p, f, args...)|-|---|---|---\ 19 ({|-|---|---|---|---|---|---|---|---\ 18 |---unsigned long __flags;|-|---|---|---|---|---\ 17 |---|---|---|---|---|---|---|---|---\ 16 |---arch_efi_call_virt_setup();||---|---|---|---\ 15 |---|---|---|---|---|---|---|---|---\ 14 |---__flags = efi_call_virt_save_flags();|--|---|---|---\ 13 |---arch_efi_call_virt(p, f, args);||---|---|---|---\ 12 |---efi_call_virt_check_flags(__flags, __stringify(f));||---\ 11 |---|---|---|---|---|---|---|---|---\ 10 |---arch_efi_call_virt_teardown();|-|---|---|---|---\ 9 }) 10 #define arch_efi_call_virt(p, f, args...)|--|---|---|---\ 9 ({|-|---|---|---|---|---|---|---|---\ 8 |---efi_##f##_t *__f;|--|---|---|---|---|---\ 7 |---__f = p->f;||---|---|---|---|---|---\ 6 |---__efi_rt_asm_wrapper(__f, #f, args);|---|---|---|---\ 5 } efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...); ENTRY(__efi_rt_asm_wrapper) stp x29, x30, [sp, #-32]! mov x29, sp /* * Register x18 is designated as the 'platform' register by the AAPCS, * which means firmware running at the same exception level as the OS * (such as UEFI) should never touch it. */ stp x1, x18, [sp, #16] /* * We are lucky enough that no EFI runtime services take more than * 5 arguments, so all are passed in registers rather than via the * stack. */ mov x8, x0 mov x0, x2 mov x1, x3 mov x2, x4 mov x3, x5 mov x4, x6 blr x8 ldp x1, x2, [sp, #16] cmp x2, x18 ldp x29, x30, [sp], #32 b.ne 0f ret 0: b efi_handle_corrupted_x18 // tail call ENDPROC(__efi_rt_asm_wrapper) |
验证测试,
1.常用centos7/8 、ubuntu 18.04-22.10 都会出现crash
2. openeuler全系列crash
3. debian 最新版,6.10内核正常
结论:
1. 6.* 内核增加了对固件运行时的异常处理,如果固件存在缺陷,会调用其他方式处理
2. 其他系统没有考虑到固件运行错误,均会crash
3. efi=noruntime配置后不会crash
4. 根因:固件问题,升级固件后问题也解决。若无法规避则修改内核参数