compressed/head.S – start: – 1st restart ~ relocation 수행 과정

DTB 및 재배치 코드 보호

다음 코드는 relocation을 하기 위해 필요한 주소들을 계산한다.

code relocation은 r6(source 상단)부터 r5(source 하단)로 32바이트씩 읽어서 r9(dest 상단)에 복사하면서 내려오는데 각 주소는 다음과 같이 복잡하다. 간단히 알아보고 실제 계산은 각 코드에서 비교를 해야 정확히 알 수 있다.

  • N=복사할 relocation 코드 사이즈
    • (_edata + DTB size – r5)한 사이즈를 상향으로 32바이트 align한 주소
  • r5: source 하단 주소
    • compressed 커널의 restart 주소를 32바이트 align한 주소
  • r6: source 상단 주소
    • r5 + N(복사할 relocation 코드 사이즈)한 주소를 상향으로 32 byte  align 한 주소
  • r9: dest 상단 주소
    • r10(decompressed 커널의 시작) + A + B + C + N
      • A=decompressed 커널 사이즈
      • B=bss가 DTB 영역을 침해하지 않도록 추가한 사이즈
        • bss가 _edata – wont_overwrite 영역을 초과하는 사이즈
      • C=재배치 수행 중 재배치 코드가 파괴되지 않을 만큼의 사이즈
        • reloc_code_end – restart 만큼의 사이즈를 상향으로 256 byte align 한 사이즈
/*
 * Relocate ourselves past the end of the decompressed kernel.
 *   r6  = _edata
 *   r10 = end of the decompressed kernel
 * Because we always copy ahead, we need to do it from the end and go
 * backward in case the source and destination overlap.
 */
                /*
                 * Bump to the next 256-byte boundary with the size of
                 * the relocation code added. This avoids overwriting
                 * ourself when the offset is small.
                 */
                add     r10, r10, #((reloc_code_end - restart + 256) & ~255)
                bic     r10, r10, #255 

                /* Get start of code we want to copy and align it down. */
                adr     r5, restart
                bic     r5, r5, #31

/* Relocate the hyp vector base if necessary */
#ifdef CONFIG_ARM_VIRT_EXT
                mrs     r0, spsr 
                and     r0, r0, #MODE_MASK
                cmp     r0, #HYP_MODE
                bne     1f   

                bl      __hyp_get_vectors
                sub     r0, r0, r5
                add     r0, r0, r10
                bl      __hyp_set_vectors
1:
#endif

                sub     r9, r6, r5              @ size to copy 
                add     r9, r9, #31             @ rounded up to a multiple
                bic     r9, r9, #31             @ ... of 32 bytes
                add     r6, r9, r5
                add     r9, r9, r10
  • decompressed 커널의 끝 주소(위 그림에서의 space B 포함)에 일정 부분의 공간을 추가한다.
    •  기존에는 decompressed 커널의 끝에 256바이트의 space 공간을 두고 relocation되는 compressed 커널이 놓이게 되었었는데 compressed 커널의 시작 주소가 relocation되는 곳의 시작주소의 바로 밑에 바짝 붙어 있는 경우 32바이트씩 copy하면서 overwrite될 가능성과 cache flush code에서 문제가 발생할 확률이 있어서 아예 이슈가 될만한 사이즈만큼 더 띄우게 되었다.
    • add r10, r10, #((reloc_code_end – restart + 256) & ~255)
      • r10: 한 번 교정된 decompressed kernel의 마지막 주소에다가 relocation 전용 코드 길이만큼 추가한다.
        • reloc_code_end 주소 – restart 영역에 256 바이트를 더한 후 256 byte align한 사이즈이다.
        • rpi2:
          • r10 += 0x900((0x928 – 0xc0 + 0x100) & 0xffffff00)
          • 0x900 만큼의 space를 두었다.
    • bic r10, r10, #255
      • 256바이트 align한 주소가 최종적으로 relocated compressed 커널 영역의 시작 주소가 된다.
  • adr r5, restart
    • r5: compressed 커널의 restart 주소를 읽어온다.
  • bic r5, r5, #31
    • restart 주소의 32바이트 align 주소를 compressed 커널에서 복사할 영역의 시작 주소로 한다.
    • 32바이트씩 복사할 수 있도록 32byte align한다.
  • 하이퍼바이저용 벡터 베이스를 위치가 바뀔 offset 만큼 재 설정한다.
    • bl __hyp_get_vectors
      • 하이퍼바이저용 벡터 베이스 주소를 가져온다.
    • sub r0, r0, r5
      • compressed 커널이 relocation될 예정이므로 하이퍼바이저용 벡터 베이스에서 먼저 r5(compressed 커널에서 relocation할 코드 시작 주소)만큼 뺀다.
    • add r0, r0, r10
      • 다시 r10(compressed kernel이 relocation 되는 곳의 시작 주소) 만큼 더해 offset 조정을 완료한다.
    • bl __hyp_set_vectors
      • offset 조정된 주소를 다시 하이퍼바이저 벡터 베이스값으로 지정한다.
  • r9(relocation될 곳의 relocation 영역의 끝 주소)과 r6(compress 커널에서 relocation 코드 영역의 끝 주소)를 계산한다.
    • sub r9, r6, r5
    • add r9, r9, 31
    • bic r9, r9, 31
      • r9 = round_up(r6(_edata + DTB size) + 31 후 32바이트 align
      • 여기까지 계산된 r9 값이 복사할 사이즈이다.
    • add r6, r9, r5
      • r6 = r9(복사할 사이즈) + r5(compressed 커널에서 relocation 영역의 시작 주소)
      • 마지막으로 r6에 compress 커널에서 relocation 영역의 끝 주소가 지정된다.
    • add r9, r9, r10
      • r9 +=  r10(2 개의 추가 영역을 더한 decompressed 커널의 끝 주소)
      • 마지막으로 r9에 decompressed 커널의 reloocation 될 영역 끝 주소가 지정된다.
  • r9: relocation될 곳의 relocation 코드 주소의 끝을 계산한다.
    • 32바이트씩 복사하기 위해 r9 값을 32byte round up 하여 align 한다.
  • add r6, r9, r5
    • r6 = r9(compressed 커널에서 relocation할 코드의 끝 주소) + r5(compressed 커널에서 relocation할 코드 시작 주소)

재배치 코드 보호

 

decompress_head.s분석11

relocation 진행

decompress_head.s분석4f

1:              ldmdb   r6!, {r0 - r3, r10 - r12, lr}
                cmp     r6, r5
                stmdb   r9!, {r0 - r3, r10 - r12, lr}
                bhi     1b
  • r6주소로부터 32바이트씩 읽어 r9 주소에 복사한다.
  • r6주소와 r9주소는 4바이트씩 감소한다.
  • r6 주소가 r5보다 큰동안 루프를 돌며 반복한다.

relocation 완료 후 restart로 다시 이동

                /* Preserve offset to relocated code. */
                sub     r6, r9, r6

#ifndef CONFIG_ZBOOT_ROM
                /* cache_clean_flush may use the stack, so relocate it */
                add     sp, sp, r6
#endif

                bl      cache_clean_flush

                adr     r0, BSYM(restart)
                add     r0, r0, r6
                mov     pc, r0
  • sub r6, r9, r6
    • r6에는 offset이 담긴다.
    • 이 offset을 이용하여 잠시 뒤 재배치된 restart 루틴으로 점프해야 할 때 사용된다
  • add sp, sp, r6
    • ROM에서 동작한 경우가 아니면 스택 위치를 조정한다.
    • 캐시 클린 플러쉬 루틴을 이용하려면 스택이 필요해서 재배치 코드의 위쪽으로 스택을 설정한다.
  • bl cache_clean_flush
    • 캐시를 비운다.
  • 이 루틴에서 restart: 레이블로 점프한다.
    • relocation이 완료되었으므로 다시 restart 부터 영역 비교를 한다.

 

참고

댓글 남기기