6월 14일 9주차 스터디 후기
1. Study history
- arch/arm/boot/compressed/head.S 진행 중
- linux stable 3.14.4 진행 중
2. 이슈사항
-1. 리틀-엔디안 방식으로 인한 로드 방법 차이
해당 코드에서 로드하는 r10은 LC0에서 받아온 값으로 압축 풀린 커널의 크기가 들어있는 메모리의 주소이다. 해당 사이즈를 받아와서 레지스터에 저장해둡니다. 리틀 엔디안의 경우 메모리에서 레지스터로 값을 로드할 경우 상위 주소와 하위 주소가 다른 문제가 생긴다.
예를 들어 커널 사이즈 0x12345678가 리틀 엔디안 방식에서 메모리에 저장된다면 하위주소 <- 0x78563412 -> 상위주소로 저장됩니다. 이 상태에서 빅엔디안에서 해당 메모리에 있는 값을 레지스터로 r9로 불러온다면, 상위주소 <- 0x78563412 -> 하위주소 그대로 불러와지게 되기 때문에 아래와 같은 루틴으로 원하는 값을 레지스터에 적재한다.
1 2 3 4 5 6 7 | ldrb r9, [r10, #0] @ r9 = 0x00000078 ldrb lr, [r10, #1] @ lr = 0x00000056 orr r9, r9, lr, lsl #8 @ r9 = 0x00005678 ldrb lr, [r10, #2] @ lr = 0x00000034 ldrb r10, [r10, #3] @ r10 = 0x00000012 orr r9, r9, lr, lsl #16 @ r9 = 0x00345678 orr r9, r9, r10, lsl #24 @ r9 = 0x12345678 |
참고: http://www.iamroot.org/xe/Kernel_8_ARM/60066#comment_60161
-2. DTB 관련 이슈
아래 코드를 보면 lr에 r6(_edata)에서 데이터를 불러오고, 해당 부분을 어떤 sig와 비교해서 dtb_check_done으로 넘어갈지 결정합니다. 그럼 _edata[0]에는 dtb와 관련된 무언가가 있는 것 같습니다.
1 | ldr lr, [r6, #0] /*! r6 = _edata */ ldr r1, =0xedfe0dd0 @ sig is 0xd00dfeed big endian cmp lr, r1 bne dtb_check_done |
그 무언가는 다음의 파라미터에서 유추해볼 수 있을것 같습니다. 두번째 파라미터에 r6(_edata)를 넣어줍니다. void* fdt로 fdt와 관련이 있는것 같습니다. 스터디 마지막에 잠깐 해당 함수를 보았을 때, for_each_tag를 통해 atag에 저장된 tag를 atag_list에 받아와서 fdt 관련 초기화를 해 주는것 같았습니다.
이 다음부터는 이제 다음 스터디에서 이어나갈 부분이니 여기서 끊어가겠습니다.
1 2 3 4 5 | int atags_to_fdt(void *atag_list, void *fdt, int total_space) { ... for_each_tag(atag, atag_list) { ... }} | cs |
다음 스터디 시작이 atags_to_fdt 분석부터 시작입니다. 스터디 atags_to_fdt로 분기하는 부분을 건너 뛰고 바로 dtb_check_done 라벨로 넘어갔습니다. atag, fdt 등 디바이스 트리에 대해 좀 알아봐야 할 것 같습니다. 관련 링크 아래에 남겨둡니다.
- 디바이스 트리 참고 홈페이지
- (PDF)Device Tree for Dummies - eLinux.org
- http://linuxfactory.or.kr/archives/116
- http://devicetree.org/Device_Tree_Usage
- https://www.kernel.org/doc/Documentation/devicetree/usage-model.txt
- http://en.wikipedia.org/wiki/Device_tree
- http://elinux.org/Device_Tree
- 질문 2-1
_edata(.bss section)에는 무엇이 저장되어있나? DTB sig와 함께 DTB와 관련된 정보를 담고 있는것 같아 보이는데 정확히 무엇인지 잘 모르겠습니다. |
- 2-1답변(문준영 님)
디바이스 트리를 커널에 넘겨 줄 때, 커널 이미지 바로 뒤(_edata)에 붙여서 넘겨주는 방법이 있습니다. 근데 권장되는 방법은 아니라더군요. 이게 아마 APPENDED_DTB 컨피그랑 관련 되어 있을텐데, Kconfig에서 찾아서 읽어보시면 될 것 같습니다.-3. 재배치 이슈 |
이제 dtb_check_done 라벨 시작입니다. dtb 체크가 끝났고, 재배치를 하기 위한 작업을 진행합니다. 현재의 메모리 상태를 보면 다음과 같습니다.
dtb_check_done에 도달했을 때의 메모리 --------------------------------+-----------------------+ <--- r10 (malloc_end) | | malloc space | | +-----------------------+ (malloc_start) | | .stack (_end) | | (DTB information) +-----------------------+ <--- sp(r13) = _end | DTB sig (_edata[0]) | .bss ( _edata) | +-------------------------------+-----------------------+ <--- r6 = _edata | | | <--- vmlinux의 기타 섹션(piggy, got, ...) +-------------------------------+-----------------------+ | 크기를 저장한 레지스터 | | | +-- wont_overwrite ---+ | | ... | | +--- restart ----+ <--- .text | +-----------------------+ 0x50c0_0000 <--- zImage가 로드될 위치 | | 압축풀린 | | | 커널 이미지 | | +-----------------------+ 0x5000_8000 <---r4 = TEXT_OFFSET | r9 = 압축풀린 커널의 크기 | | |-------------------------------+-----------------------+ 0x5000_0000 |
위의 코드에서 재배치가 필요한 경우인지 검사합니다. 첫번째 단계에서 페이지 디렉토리 크기까지 더해준 r10 (sp + malloc space)과 r4를 비교합니다.
- r4 = 압축풀린 커널이 위치할 주소
- r10 = sp + malloc space(0x10000) + page diretory
첫번째 비교할 조건을 만족할 경우의 메모리 |-------------------------------+-----------------------+ | | 압축풀린 | | | 커널 이미지 | |-------------------------------+-----------------------+ <---r4 = TEXT_OFFSET | +-----------------------+ (malloc_end) <--- r10 | | malloc space | | +-----------------------+ (malloc_start) +-------------------------------+ .stack | | (DTB information) +-----------------------+ <--- sp(r13) = _end | DTB sig (_edata[0] ) | .bss | +-------------------------------+-----------------------+ <--- r6 = _edata | | | <--- vmlinux의 기타 섹션들(piggy, got, ...) +-------------------------------+-----------------------+ | 크기를 저장한 레지스터 | | | +--- wont_overwrite ---+ | | ... | | +--- restart ---+ <--- .text |-------------------------------+-----------------------+ 0x50c0_0000 <--- zImage가 로드될 위치 | cs |
두번째 비교할 때 메모리 +-------------------------------+-----------------------+ (malloc_end) | | malloc space | | +-----------------------+ (malloc_start) | | .stack | | (DTB information) +-----------------------+ <--- sp(r13) = _end | DTB sig (_edata[0] ) | .bss | +-------------------------------+-----------------------+ <--- r6 = _edata | | | <--- vmlinux의 기타 섹션들(piggy, got, ...) +-------------------------------+-----------------------+ | 크기를 저장한 레지스터 | | <--- r9( 재배치 필요 ) | +--- wont_overwrite ----+ <--- r10 | | ... | <--- r9( 재배치 필요없음) | +----- restart -----+ <--- .text | +-----------------------+ 0x50c0_0000 <--- zImage가 로드될 위치 | | 압축풀린 | | | 커널 이미지 | | +-----------------------+ 0x5000_8000 <---r4 = TEXT_OFFSET | | | |-------------------------------+-----------------------+ 0x5000_0000 | cs |
1 2 3 4 5 6 7 8 9 10 11 | add r10, r10, #((reloc_code_end - restart + 256) & ~255) /*! r10 = r10 + (restart 이 후 코드 크기 + 0x100 ) & 0xffff_ff00 */ /*! 주 목적은 얼라인 맞추기 */ bic r10, r10, #255 /*! r10 = r10 & !(0xff) */ /* Get start of code we want to copy and align it down. */ adr r5, restart /*! r5 = &restart */ bic r5, r5, #31 /*! 하위 5비트 클리어 */ /*! 8byte 얼라인을 위한 비트 클리어 */ | cs |
재배치를 위한 레지스터 조절 전 메모리 상태 +---------------------------------+---------------------+ (malloc_end) <--- r10 | | malloc space| | +-----------------------+ (malloc_start) | | .stack| | (DTB information) +-----------------------+ <--- sp(r13) = _end | DTB sig (_edata[0] ) | .bss| +-------------------------------+-----------------------+ <--- r6 = _edata | || <--- vmlinux의 기타 섹션들(piggy, got, ...) +-------------------------------+-----------------------+ | 크기를 저장한 레지스터 | | | +--- wont_overwrite ---+ | | ... | | +----- restart ------+ <--- r5(얼라인 된 상태) | +-----------------------+ 0x50c0_0000 <--- zImage가 로드될 위치 | | 압축풀린 | | | 커널 이미지 | | +-----------------------+ 0x5000_8000 <---r4 = TEXT_OFFSET | | | |-------------------------------+-----------------------+ 0x5000_0000 | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /*! * r5 = restart 주소 * r6 = _edata 주소 * r10 = malloc end */ sub r9, r6, r5 @ size to copy /*! r9 = _edata - (restart addr)&!0x1f */ add r9, r9, #31 @ rounded up to a multiple bic r9, r9, #31 @ ... of 32 bytes /*! 5비트 단위 올림 for 얼라인 */ /*! restart ~ _edata 까지 32bytes 얼라인 */ /*! r9 = restart ~ _edata(복사할 영역 사이즈) */ add r6, r9, r5 add r9, r9, r10 /*! * r6 = restart + 복사할 영역 사이즈(정렬된 _edata) * r9 = malloc end + 복사할 영역 사이즈(정렬된 restart) */ | cs |
위의 코드를 실행 한뒤 레지스터 +-------------------------------+-----------------------+ <--- r9 = 재배치할 영역의 끝 주소 | | 재배치할 영역 | | +-----------------------+ | | 빈 영역 | | +-----------------------+ (malloc_end) <--- r10 | | malloc space | | +-----------------------+ (malloc_start) | | .stack | | (DTB information) +-----------------------+ <--- sp(r13) = _end | DTB sig (_edata[0] ) | .bss | +-------------------------------+-----------------------+ <--- r6 = 복사될 영역의 끝 주소 | | | <--- vmlinux의 기타 섹션들(piggy, got, ...) +-------------------------------+-----------------------+ | 크기를 저장한 레지스터 | | | +--- wont_overwrite ---+ | | ... | | +----- restart -----+ <--- r5(얼라인 된 상태) | +-----------------------+ 0x50c0_0000 <--- zImage가 로드될 위치 | | 압축풀린 | | | 커널 이미지 | | r9 = _edata - restart addr +-----------------------+ 0x5000_8000 <---r4 = TEXT_OFFSET | (재배치할 영역 크기) | | |-------------------------------+-----------------------+ 0x5000_0000 | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /*! * 재배치 루프(1: ldmdb ~ bhi 1b) * r6(edata)부터 복사해서 r9(재배치할 영역 끝 주소) 붙여넣기 */ 1: ldmdb r6!, {r0 - r3, r10 - r12, lr} cmp r6, r5 stmdb r9!, {r0 - r3, r10 - r12, lr} bhi 1b /* Preserve offset to relocated code. */ sub r6, r9, r6 /*! r6 = 재배치 영역까지 오프셋 */ #ifndef CONFIG_ZBOOT_ROM /* cache_clean_flush may use the stack, so relocate it */ add sp, sp, r6 /*! sp 위치 조절 */ tst r4, #1 /*! * not_angel의 재배치 검사 과정에서 cache_on을 안 해줬을 때 * r4에 1이 세트되어있음. 이 경우 z bit에 셋이 되어있지 않으므로 * eq 실행 안됨. */ /*! * 현재 아키텍처에 맞는 캐쉬 플러쉬 함수 콜 * cache_on이 실행 되었을 경우 cache_clean_flush 실행 */ bleq cache_clean_flush adr r0, BSYM(restart) add r0, r0, r6 mov pc, r0 | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 위의 코드를 실행 한뒤 레지스터 ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓ ┃ (DTB information) ┃ stack ┃ ┃ DTB sig (_edata[0] ) ┣━━━━━━━━━━━┫ <--- sp(r13) = 재배치 완료 후 sp ┣━━━━━━━━━━━━━━━╋ ┃ ┃ ┃ 재배치 완료 ┃ <--- pc = restart부터 다시 시작 ┃ ┣━━━━━━━━━━━┫ <--- r9 = 재배치 완료 후 r9 ┃ ┃ 빈 영역 ┃ ┃ ┣━━━━━━━━━━━┫(malloc_end) <--- r10 ┃ ┃ malloc space ┃ ┃ ┣━━━━━━━━━━━┫(malloc_start) ┃ ┃ .stack ┃ ┃ ┣━━━━━━━━━━━┫ ┃ ┃ .bss ┃ ┃ ┣━━━━━━━━━━━┫<--- _edata ┃ ┃ ┃<--- vmlinux의 기타 섹션들(piggy, got, ...) ┣━━━━━━━━━━━━━━━╋━━━━━━━━━━━┫ ┃크기를 저장한 레지스터 ┃ ┃ ┃ ┣━ wont_overwrite ━┫ ┃ ┃ ... ┃ ┃ ┣━━ restart ━━┫ ┃ ┣━━━━━━━━━━━┫ 0x50c0_0000 <--- zImage가 로드될 위치 ┃ ┃압축풀린 ┃ ┃r6 = 재배치 오프셋 ┃커널 이미지 ┃ ┃r9 = _edata - restart addr ┣━━━━━━━━━━━┫ 0x5000_8000 <---r4 = TEXT_OFFSET ┃ (재배치할 영역 크기) ┃ ┃ ┗━━━━━━━━━━━━━━━┻━━━━━━━━━━━┛ 0x5000_0000 | cs |
'Kernel분석' 카테고리의 다른 글
kernel 분석 환경설정(arm) (0) | 2015.07.07 |
---|---|
iamroot kernel 분석 스터디 (0) | 2015.07.07 |