wont_overwrite:
compressed 커널 영역과 decompressed 커널 영역이 더 이상 중복되지 않는다고 판단하면 이 루틴으로 진입한다.
- 이 루틴에 진입했다는 의미는 이미 relocation이 필요 없는 상황이었거나 relocation이 완료된 경우다.
- 이 루틴부터는 본격적으로 압축을 풀고 커널 시작(start_kernel) 으로 가기위한 약간의 보정 활동을 한다.
wont_overwrite: /* * If delta is zero, we are running at the address we were linked at. * r0 = delta * r2 = BSS start * r3 = BSS end * r4 = kernel execution address (possibly with LSB set) * r5 = appended dtb size (0 if not present) * r7 = architecture ID * r8 = atags pointer * r11 = GOT start * r12 = GOT end * sp = stack pointer */ orrs r1, r0, r5 beq not_relocated add r11, r11, r0 add r12, r12, r0
- if (delta = 0) & (r5(dtb size) = 0)
- delta가 0이면서 dtb_size도 0이면 BSS 및 GOT 영역들의 위치가 바뀔 필요가 없으므로 리로케이션 로직을 수행하지 않았던 경우이다.
- r11, r12
- GOT 영역을 알리는 시작과 끝에 delta를 더해 교정함.
relocation이 수행된 경우의 보정 활동
CONFIG_ZBOOT_ROM 옵션에 의해 compressed 커널이 ROM에서 시작한 경우와 부트로더에 의해 RAM에 로드되어 시작한 경우를 나누어 분석한다.
#ifndef CONFIG_ZBOOT_ROM /* * If we're running fully PIC === CONFIG_ZBOOT_ROM = n, * we need to fix up pointers into the BSS region. * Note that the stack pointer has already been fixed up. */ add r2, r2, r0 add r3, r3, r0 /* * Relocate all entries in the GOT table. * Bump bss entries to _edata + dtb size */ 1: ldr r1, [r11, #0] @ relocate entries in the GOT add r1, r1, r0 @ This fixes up C references cmp r1, r2 @ if entry >= bss_start && cmphs r3, r1 @ bss_end > entry addhi r1, r1, r5 @ entry += dtb size str r1, [r11], #4 @ next entry cmp r11, r12 blo 1b /* bump our bss pointers too */ add r2, r2, r5 add r3, r3, r5
decompressed 커널이 RAM에서 동작한 경우이다
- BSS 영역 교정
- r2, r3
- BSS 영역을 알리는 시작과 끝에 delta를 더해 교정한다.
- r2, r3
- GOT 영역내 엔트리 교정
- GOT 엔트리는 재배치가 일어나면 연결된 주소가 다 엉뚱한 곳을 가리키기 때문에 링크가 존재하는 모든 엔트리들은 delta 및 dtb size 만큼 더해서 교정해야 한다.
- 엔트리들은 PLT를 가리킬 수도 있고, 외부 함수를 가리킬 수 있다.
- 그러나 zImage는 외부함수가 연결될 가능성은 없고 내부에서 BSS 영역에 존재하는 관련 함수등이 있을거라 예상된다.
- ldr r1, [r11, #0]
- r11(GOT start) 주소에서 GOT 엔트리를 읽어 r1에 대입
- r1(엔트리) += delta로 교정
- dtb size 만큼 추가로 교정할지 판단
- str r1, [r11], #4
- 교정을 한 후 4바이트 만큼 이동
- 다음 엔트리 주소가 마지막이 아니면 다시 레이블 1로 이동하여 계속 루프를 돈다.
#else /* * Relocate entries in the GOT table. We only relocate * the entries that are outside the (relocated) BSS region. */ 1: ldr r1, [r11, #0] @ relocate entries in the GOT cmp r1, r2 @ entry < bss_start || cmphs r3, r1 @ _end < entry addlo r1, r1, r0 @ table. This fixes up the str r1, [r11], #4 @ C references. cmp r11, r12 blo 1b #endif
decompressed 커널이 ROM에서 동작한 경우이다.
- BSS 영역 교정
- r2, r3
- BSS 영역을 알리는 시작과 끝에 DTB 사이즈만큼 추가 교정(delta는 이미 추가하였었음)
- r2, r3
- GOT 영역내 엔트리 교정
- ROM또는 플래쉬에서 직접 수행되는 경우
- r11(GOT start)주소에서 GOT엔트리를 읽어 r1에 대입
- if r1(엔트리) < r2(bss_start) ||
- r3(_end) < r1(엔트리)
- r1 += r0(delta)
- r3(_end) < r1(엔트리)
- 직역)
- if r1 >= r2 then
- if r3 < r1 then
- r1 += r0
- else
- r1 += r0
- if r3 < r1 then
- if r1 >= r2 then
not_relocated:
not_relocated: mov r0, #0 1: str r0, [r2], #4 @ clear bss str r0, [r2], #4 str r0, [r2], #4 str r0, [r2], #4 cmp r2, r3 blo 1b
- BSS 영역 데이터를0x00으로 초기화 한다.
- rpi: BSS 공간이 0x1c개 만큼인데 일부 over해서 초기화 함
- 사실 16바이트 align을 기대하고 readelf로 분석을 해보니 해당 공간은 8바이트 align 상태여서 뭔가 맞지 않는다는 느낌이다. 어쨋든 그 최대 파괴되는 일부 바이트가 위치한 공간은 스택 중 가장 아래에 위치한 공간이고 확실히 아직 스택을 많이 사용한 적이 없으므로 전혀 상관 없다고 함.
- 캐시가 on된 상태에서 캐시라인을 충분히 사용하므로 4번의 Word를 연속 저장하여 성능을 높이려 함
- .align
- default align은 32비트 시스템은 4바이트, 64비트 시스템은 8바이트
- .align 지시자는 인수의 해석을 시스템에 따라 달리한다.
- 어떤 시스템에서는 n 바이트 단위로 지정을 하고, 또 다른 시스템에서는 2^n 바이트 단위로 동작한다.
- – n 바이트 단위:
- a29k, hppa, m68k, sparc, Xtensa, Renesas / SuperH SH, i386 using ELF format
- – 2^n 바이트 단위:
- i386 using a.out (old format), arm, strong ARMv3
- rpi2와 같은 ARM은 2^N 바이트를 사용한다.
- 대문자로 표현된 ALIGN(8)과 같은 매크로는 8바이트를 의미.
/* * Did we skip the cache setup earlier? * That is indicated by the LSB in r4. * Do it now if so. */ tst r4, #1 bic r4, r4, #1 blne cache_on
- 유보된 캐시(LSB 0 in r4)가 있는 경우 cache_on을 호출
- r4 레지스터는 커널 시작 주소로 계속 사용되어야 하므로 하위 1비트를 다른 용도로 잠시 사용하였던 것을 없애기 위해 clear 해야 한다.
- 주의: blne 명령전에 사용했던 tst는 cmp 명령과 서로 다른 결과를 갖는다.
- tst 명령은 r4와 #1을 and 연산한 결과가 0이 아닌경우 cache_on을 호출
/* * The C runtime environment should now be setup sufficiently. * Set up some pointers, and start decompressing. * r4 = kernel execution address * r7 = architecture ID * r8 = atags pointer */ mov r0, r4 mov r1, sp @ malloc space above stack add r2, sp, #0x10000 @ 64k max mov r3, r7 bl decompress_kernel bl cache_clean_flush bl cache_off mov r1, r7 @ restore architecture number mov r2, r8 @ restore atags pointer
- decompress_kernel(kernel execution address, sp, sp+0x10000, architecture ID) 호출
- decompress가 끝났으면 커널 설정을 하러가기 전에 캐쉬를 클린 + 플러쉬 + off 한다.
#ifdef CONFIG_ARM_VIRT_EXT mrs r0, spsr @ Get saved CPU boot mode and r0, r0, #MODE_MASK cmp r0, #HYP_MODE @ if not booted in HYP mode... bne __enter_kernel @ boot kernel directly adr r12, .L__hyp_reentry_vectors_offset ldr r0, [r12] add r0, r0, r12 bl __hyp_set_vectors __HVC(0) @ otherwise bounce to hyp mode b . @ should never be reached .align 2 .L__hyp_reentry_vectors_offset: .long __hyp_reentry_vectors - . #else b __enter_kernel #endif
- 하이퍼바이저가 정의되어 있는 경우
- 실제 상태 레지스터 값을 확인하여 하이퍼모드가 동작하지 않는 경우 __enter_kernel 루틴으로 점프한다.
- 만일 하이퍼모드가 동작중이면 .L__hyp_reentry_vectors_offset의 값을 읽어 offset을 보정한 후 __hyp_set_vectors 함수를 호출하여 하이퍼바이저의 벡터 오프셋을 설정하고 하이퍼바이저에 의해 __enter_kernel(?)로 점프한다.
- 하이퍼바이저가 커널에 정의되어 있지 않는 경우 곧바로 __enter_kernel 루틴으로 점프한다.
참고
- start: | 문c
- 영역 검사를 하여 하단부가 중복된 경우만 캐시 on 보류 | 문c
- cache_on: | 문c
- cache_clean_flush: | 문c
- 영역 검사를 하여 하단부가 중복된 경우만 캐시 on 보류 | 문c
- restart: | 문c
- wont_overwrite: – (현재 글)