Skip to content
JaeMoo Han edited this page Jun 27, 2015 · 2 revisions

#compressed/head.S ##목차

##정리

  • arch/arm/boot/compressed/head.S
  • 2014/5/31 - 7주차 스터디

start label

start 라벨로 .section을 통해 섹션지정까지 되어있습니다. .section의 경우 컴파일러와 링커가 elf포맷을 만들 때 사용합니다.

code

123 .section ".start", #alloc, #execinstr 124 /* 125 * sort out different calling conventions 126 */ .align .arm 127 .align 128 .arm @ Always enter in ARM state

>```

start 섹션 시작 부분 윗부분은 전부 macro 정의 .section: start section의 시작부분으로 vmlinux.lds 의 .start과 연결된다. 섹션의 시작 부분으로 정의한다. #alloc: allocatable #execinstr: excutable

다음처럼 콜롬 표시는 라벨 지정으로 사용됩니다. 점프하거나 해당 주소를 불러줄 때 유용하게 사용할 수 있습니다. 컴파일러, 링커용 전처리는 주석을 참고해 주세요. mov r0, r0를 8번 반복하는 부분이 앞쪽에 들어있는데, 해당 부분은 옛날 어떤 부트로더가 매직넘버 검사를 0번지부터가 아닌 3~4번지부터 시작하여, 해당 부분을 보정하기 위해 앞의 명령어 8개를 의미없는 코드로 반복하뒤 매직넘버를 적도록 했다고 합니다. 그게 지금까지 남아있어 다음과 같은 의미없는 코드 반복이 남아있게 되었다고 합니다. 의미없는 코드 반복 후 부트로더가 zImage임을 확인할 매직넘버, zImage의 절대주소, zImage의 끝주소를 .word를 통해 32비트 데이터로 어셈블리에 삽입해 버립니다. 129 start: 130 .type start,#function @ 131 .rept 7 @ .rept n: .rept부터 .endr까지 n번 반복 132 mov r0, r0 @ 러셀킹 할아버지께서 말하시길 다른 아키텍처에서 시작 부분이 0으로 셋팅 안될 경우가 있어 넣으셨다고 답변을 받았다고함. 133 .endr 134 ARM( mov r0, r0 ) @ ARM()와 같은 부분은 컴파일 과정에서 설정된 아키텍처에 해당하는 경우만 컴파일 되는 부분. 135 ARM( b 1f ) @ b (label)f/b: 브랜치 명령어로 f의 경우 아래에 존재하는 label로 b의 경우 위에 존재하는 라벨로 이동. 136 THUMB( adr r12, BSYM(1f) ) 137 THUMB( bx r12 ) 138 139 .word 0x016f2818 @ Magic numbers to help the loader 140 .word start @ absolute load/run zImage address 141 .word _edata @ zImage end address 위에서랑 아래 코드에서 ARM(), THUMB(), ARM_BE8()처럼 arm모드, thumb모드, arm빅엔디안 모드 등으로 지정해 모드에 맞는 명령어만 실행하도록 설정 할 수 있습니다. 현재 저희는 arm모드로 진행 중이므로 ARM()이외에 나머지 모드는 전부 건너뛰도록 하겠습니다. mrs, msr의 경우 cpsr, spsr처럼 상태레지스터의 내용을 변경하거나 복사할 경우 사용하게 됩니다. 상태 레지스터를 mov로 이동할 수 없다고 합니다. -mrs: 상태레지스터의 내용을 일반 레지스터로 복사 -msr: 일반 레지스터 내용을 상태 레지스터로 복사 -cpsr: 현재 프로세서의 상태 레지스터 -spsr: 모드 전환이 필요할 경우 이전 모드의 cpsr저장용 레지스터로 각 모드마다 하나씩 있음 아래의 코드와 주석대로라면 cpsr을 r9에 저장해둡니다. 142 THUMB( .thumb ) 143 1: 144 ARM_BE8( setend be ) @ go BE8 if compiled for BE8 145 mrs r9, cpsr @ cpsr : current program status register 다음 설정은 kernel/head.S에서 다루고 지나가겠습니다. 간단하게 초기화만 진행해주는 코드가 존재합니다. 146 #ifdef CONFIG_ARM_VIRT_EXT 147 bl __hyp_stub_install @ get into SVC mode, reversibly 148 /*! 하이퍼 바이저 익스텐션을 위한 초기화 */ 149 #endif arm 아키텍처에서 서브루틴 호출 시 인자 전달을 r0부터 채워서 해줍니다. 다음 그림을 참고해 주세요

r0부터 r3까지 4개의 인자를 전달할 수 있으며, 그다음 인자부터는 스택에 저장해서 전달합니다. 서브 루틴의 리턴값은 r0에 넣어주면 됩니다. 그러면 다음 코드에서 r1과 r2에 아키텍처 id와 atags pointer가 있는 이유를 설명할 수 있습니다. 부트로더에서 kernel을 불러줄 때 인자로 넘겨주는 것 같습니다. 자세한 내용은 부트로더를 확인해 보면 알 수 있을것 같습니다. 150 mov r7, r1 @ save architecture ID 151 mov r8, r2 @ save atags pointer 다음은 디버그모드로 보이는 엔젤모드 검사가 되겠습니다. 엔젤 모드에 대한 자세한 사항은 따로 포스팅 하겠습니다. 그 전까지 다음 주석들만 참고해 주세요.

154 /* 155 * Booting from Angel - need to enter SVC mode and disable 156 * FIQs/IRQs (numeric definitions from angel arm.h source). 157 * We only do this if we were in user mode on entry. 158 * angel과 swi 0x123456에 대한 설명은 http://www.iamroot.org/xe/Kernel_8_ARM/57324 참고 159 / 160 mrs r2, cpsr @ get current mode 161 tst r2, #3 @ not user? 162 /! r2 & #3 / 163 /! tst: arm.arch.p.716 참고 / 164 bne not_angel @ if SVC mode, go to not_angel 165 mov r0, #0x17 @ angel_SWIreason_EnterSVC 166 ARM( swi 0x123456 ) @ angel_SWI_ARM 167 /! swi=소프트웨어 인터럽트 명령어 amr.arch.p.713 참고 / 168 /! angel debugging 모드로 동작을 위한 동작? */ 169 THUMB( svc 0xab ) @ angel_SWI_THUMB

-2. 재배치 관련 이슈사항 엔젤모드 검사 후 엔젤모드가 아닐경우 no_angel 라벨로 점프합니다. 해당 라벨에서는 재배치 여부를 검사하여 cache_on 여부를 결정합니다.

-arch/arm/boot/compressed/head.S not_angel: safe_svcmode_maskall r0 /*! /arch/arm/include/asm/assembler.h 에 존재 / msr spsr_cxsf, r9 @ Save the CPU boot mode in /! SPSR = 모드가 바뀌기 전의 cpsr을 저장해둔 레지스터 / / * Note that some cache flushing and other stuff may * be needed here - is there an Angel SWI call for this? / / * some architecture specific code can be inserted * by the linker here, but it should preserve r7, r8, and r9. / .text #ifdef CONFIG_AUTO_ZRELADDR @! ZRE Address = TEXTADDR의 실질적인 물리주소 @! TEXTADDR = 커널의 가상 시작주소, 일반적으로 PAGE_OFFSET + 0x8000 @ determine final kernel image address mov r4, pc
and r4, r4, #0xf8000000 add r4, r4, #TEXT_OFFSET /
Set up a page table only if it won't overwrite ourself.

  • That means r4 < pc && r4 - 16k page directory > &_end.
  • Given that r4 > &_end is most unfrequent, we add a rough
  • additional 1MB of room for a possible appended DTB(device tree binary). */ mov r0, pc cmp r0, r4 @if r0 < r4 { ldrcc r0, LC0+32 @ r0 < r4 일 때 LC0+32 = _end - restart + 16K(page_table) + 1M(DTB) addcc r0, r0, pc cmpcc r4, r0 @ if r4 < r0 orrcc r4, r4, #1 @ remember we skipped cache_on } @ if r4 >= r0 blcs cache_on

위의 값에서 압축해제된 커널을 올릴 주소를 r4에 넣어줍니다. 해당 주소와 현재 pc와 비교해서 재배치의 수행 여부를 결정하게 됩니다. 여기서 재배치가 결정되면 cache_on을 나중으로 미루게 됩니다.

여기서 cmp r0, r4를 한번 살펴보겠습니다. 위의 부등호대로라면 pc의 값이 커널의 위치보다 작다면 재배치를 해주게 되는데 모기향책을 살펴볼 경우 zImage의 위치는 해당 위치보다 한참 위쪽인 0x--C0_0000에 위치합니다. 대충 그림은 다음과 같이 예상합니다. +---------------------+ | | |zImage | <- 재배치 검사할 때의 pc(r0) +---------------------+--0x--C0_0000 |빈공간 | +---------------------+--0x--00_---- |압축풀린 커널이| |올라갈 공간 | +---------------------+--0x--00_8000 <- r4(압축해제된 커널의 위치) |TEXT_OFFSET | -page_directory(16k) | | +---------------------+ 참고1: http://www.iamroot.org/xe/Kernel_10_ARM/186592#comment_186806 참고2: http://www.iamroot.org/xe/index.php?mid=Kernel_10_ARM&document_srl=180812#comment_181518 참고3: http://www.iamroot.org/xe/Kernel_10_ARM/173174

-3. 재배치 이 후 LC0 위치 조절 위의 재배치 문제가 발생할 경우 재배치 이 후 restart를 한번 더 실행하게 된다. 재배치를 수행했다면 _edata(bss시작 주소)와 압축 풀린 커널 사이즈가 있는 주소(r10)의 위치를 조절해주게 된다.

adr r0, LC0
ldmia r0, {r1, r2, r3, r6, r10, r11, r12} ldr sp, [r0, #28] sub r0, r0, r1 @ calculate the delta offset add r6, r6, r0 @ _edata add r10, r10, r0 @ inflated kernel size location

-4. 리틀-엔디안 방식으로 인한 로드 방법 해당 코드에서 로드하는 r10은 LC0에서 받아온 값으로 압축 풀린 커널의 크기가 들어있는 메모리의 주소이다. 해당 사이즈를 받아와서 나중에 어딘가에 쓰기 위해 저장을 해두는 것 같은데, 리틀 엔디안의 경우 메모리에서 레지스터로 값을 로드할 경우 상위 주소와 하위 주소가 다른 문제가 생긴다. 예를 들어 커널 사이즈 0x12345678가 리틀 엔디안 방식에서 메모리에 저장된다면 0x78563412로 저장됩니다. 이 상태에서 빅엔디안에서 해당 메모리에 있는 값을 r9로 불러온다면, 0x78563412 그대로 불러와지게 되기 때문에 아래와 같은 루틴으로 원하는 값을 레지스터에 적재합니다.

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

  1. 참고 사항 혹시 참고가 될까 해서 올려드립니다. 추가적인 참고자료 및 의문점도 댓글로 부탁드립니다.

-1. arch/arm/boot/compressed/vmware 덤프로 알아본 LC0 공부하다가 LC0에 저장된 값들 _end, _edata같은녀석들이 먼가 싶어서 이것 저것 찾아본 결과입니다. #arm-linux-eabi-objdump arch/arm/boot/compressed/vmware
... 000002d8 : 2d8: 000002d8 002a3638 002a3660 002a3638 ....86*.6*.86*. 2e8: 002a35e9 002a35fc 002a3630 002a4660 .5*..5*.06*.F*. 2f8: 003a7598 e320f000 .u:... . ...

#arm-linux-eabi-readelf -S arch/arm/boot/compressed/vmware ... Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 008000 0059d0 00 AX 0 0 32 [ 2] .rodata PROGBITS 000059d0 00d9d0 000d4c 00 A 0 0 4 [ 3] .piggydata PROGBITS 0000671c 00e71c 29ced1 00 A 0 0 1 [ 4] .got.plt PROGBITS 002a35f0 2ab5f0 00000c 04 WA 0 0 4 [ 5] .got PROGBITS 002a35fc 2ab5fc 000034 00 WA 0 0 4 [ 6] .pad PROGBITS 002a3630 2ab630 000008 00 WA 0 0 1 [ 7] .bss NOBITS 002a3638 2ab638 000028 00 WA 0 0 4 [ 8] .stack NOBITS 002a3660 2ab638 001000 00 WA 0 0 1 [ 9] .comment PROGBITS 00000000 2ab638 000030 01 MS 0 0 1 ...

LC0에서 _end와 _edata는 3번째와 4번째에 들어가있습니다. 덤프파일을 보면 _end: 002a3660, _edata: 002a3638와 같습니다. 해당 값들은 섹션 헤더에서 .stack과 .bss의 주소로 나옵니다. 그렇다면 재배치에서 사용하는 LC0의 _end -restart + 16Kb + 1Mb값은 스택 시작주소에서 restart를 뺀값으로 보이는데 재배치해줄때 스택 사이즈는 제외한다는 말처럼 보입니다. 그리고 5번째에는 inputdata - 4가 들어간다고 합니다. 압축된 커널의 끝에 압축 해제된 커널의 사이즈가 붙어있다는 문구가 있길래 살펴보니 살펴보면 002a35e9가 들어가 있으며, 해당 주소는 piggydata 시작주소 + piggydata size - 4를 한 값과 같습니다.