소개
이번 시간에는 커널 패닉을 디버깅하는 과정을 소개합니다.
1차 분석
콜스택을 TRACE32로 잡아 보니 Data abort로 커널 패닉이 발생했습니다.
-000|el1_da(asm)
-->|exception
-001|usb_copy_descriptors(src = 0xFFFFFFF50D806080)
-002|configfs_composite_bind(gadget = 0xFFFFFFF4C4F16298, gdriver = 0xFFFFFFF5B2943B08)
-003|udc_bind_to_driver(udc = 0xFFFFFFF5B67BD000, driver = 0xFFFFFFF5B2943B08)
-004|usb_udc_attach_driver(name = 0xFFFFFFF57B6C3F80, driver = 0xFFFFFFF5B2943B08)
-005|schedule_work(inline)
-005|gadget_dev_desc_UDC_store(item = 0xFFFFFFF5B2943800, ?, len = 12)
-006|configfs_write_file(file = 0xFFFFFFF4D93A7F00, buf = 0x0000007F961D31D1, ?, ppos = 0xFFFFFFF5B9363EB
-007|__vfs_write(file = 0xFFFFFFF4D93A7F00, ?, ?, pos = 0xFFFFFFF5B9363EB0)
-008|vfs_write(file = 0xFFFFFFF4D93A7F00, buf = 0x0000007F961D31D1, ?, pos = 0xFFFFFFF5B9363EB0)
-009|sys_write(?, ?, ?)
-010|el0_svc_naked(asm)
-->|exception
-011|NUX:0x7F9741D688(asm)
---|end of frame
Data Abort가 발생한 인스트럭션은 'ldrb w2,[x2]' 인데 x2는 0x736563697665642F입니다.
'0x736563697665642F'이라? 주소의 형태만 봐도 MMU가 처리할 수 없는 주소로 보입니다.
______________addr/line|code___________|label____|mnemonic________________|comment
NSX:FFFFFF84CDC306B8|91000421 add x1,x1,#0x1 ; x1,x1,#1
NSX:FFFFFF84CDC306BC|8B010EA2 add x2,x21,x1,lsl #0x3 ; x2,src,x1,lsl #3
NSX:FFFFFF84CDC306C0|F85F8042 ldur x2,[x2,#-0x8] ; x2,[x2,#-8]
NSX:FFFFFF84CDC306C4|B4000082 cbz x2,0xFFFFFF84CDC306D4
___NSX:FFFFFF84CDC306C8|39400042__________________ldrb____w2,[x2]
Data Abort가 발생한 소스 코드의 위치는 어딜까요?
y.l.line 0xFFFFFF84CDC306C8
linux-next\drivers\usb\gadget\config.c|\136--136
이번에는 'v.l %l %t' 명령어로 지역 변수를 어느 레지스터가 저장했는지 확인해볼까요?
usb_copy_descriptors(
(register struct usb_descriptor_header * *) [X21] src = 0xFFFFFFF50D806080)
(register unsigned int) [X19] n_desc = 0
x21은 0xFFFFFFF50D806080입니다.
0xFFFFFFF50D806080의 출처는 0xFFFFFFF5551DD800입니다.
(struct usb_function *) [-] (struct usb_function*)0xFFFFFFF5551DD800 = 0xFFFFFFF5551DD800 -> (
(char *) [D:0xFFFFFFF5551DD800] name = 0xFFFFFF84CEF20533 -> "mtp",
(int) [D:0xFFFFFFF5551DD808] intf_id = 0,
(struct usb_gadget_strings * *) [D:0xFFFFFFF5551DD810] strings = 0xFFFFFF84CFEE3138,
(struct usb_descriptor_header * *) [D:0xFFFFFFF5551DD818] fs_descriptors = 0xFFFFFFF50D806080 -> 0x1 -> ( //<<--
(__u8) [D:0x1] bLength = 0,
(__u8) [D:0x2] bDescriptorType = 0),
(struct usb_descriptor_header * *) [D:0xFFFFFFF5551DD820] hs_descriptors = 0xFFFFFFF50D806280 -> 0x1 -> ( //<<--
(__u8) [D:0x1] bLength = 0,
(__u8) [D:0x2] bDescriptorType = 0),
(struct usb_descriptor_header * *) [D:0xFFFFFFF5551DD828] ss_descriptors = 0xFFFFFFF50D806F00 -> 0x0 -> NULL //<<--
그런데 코어 덤프에서는 otg_desc[0]이 NULL입니다.
(static struct usb_descriptor_header * [2]) otg_desc = (
[0] = 0x0 = -> NULL,
[1] = 0x0 = -> NULL)
(where)
1764static void configfs_composite_unbind(struct usb_gadget *gadget)
1765{
1766 struct usb_composite_dev *cdev;
1767 struct gadget_info *gi;
1768
1769 /* the gi->lock is hold by the caller */
1770
1771 cdev = get_gadget_data(gadget);
1772 gi = container_of(cdev, struct gadget_info, cdev);
1773
1774 kfree(otg_desc[0]);
1775 otg_desc[0] = NULL; //<<--
Solution Patch
아래와 같이 코드를 수정하면 Data Abort를 방지할 수 있습니다.
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index ec94fbb..be06b39 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1570,7 +1570,7 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
goto err_purge_funcs;
}
- if (f->multi_config_support) {
+ if (f->multi_config_support && !otg_desc[0]) {
if (f->fs_descriptors)
f->fs_descriptors =
usb_copy_descriptors(f->fs_descriptors);
'Core BSP 분석 > 커널 트러블슈팅' 카테고리의 다른 글
QEMU - Aarch64 (0) | 2023.05.07 |
---|---|
[커널크래시] 인터럽트 컨텍스트에서 wake_up_interruptible 대신 wake_up_locked 사용 (0) | 2023.05.07 |
[리눅스커널] 익명 페이지 메모리 누수(Memory Leak) 디버깅 - TRACE32 (0) | 2023.05.07 |
[tegra4i] UI freeze due to RT class 프로세스 (0) | 2023.05.07 |
[리눅스커널] Crash: 비트 플립 @profile_tick() (0) | 2023.05.07 |