compressed/head.S – wont_overwrite

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를 더해 교정한다.
  • 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는 이미 추가하였었음)
  • GOT 영역내 엔트리 교정
    • ROM또는 플래쉬에서 직접 수행되는 경우
    • r11(GOT start)주소에서 GOT엔트리를 읽어 r1에 대입
    • if r1(엔트리) < r2(bss_start) ||
      • r3(_end) < r1(엔트리)
        • r1 += r0(delta)
    • 직역)
      • if r1 >= r2 then
        • if r3 < r1 then
          • r1 += r0
        • else
          • r1 += r0

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 루틴으로 점프한다.

 

참고

댓글 남기기