본문 바로가기
Research/SystemProg

압축 해제 이후 커널 패치 과정

by sunnyan 2004. 6. 6.
728x90
유영창 (http://kelp.or.kr)  
압축 해제 이후 커널 패치 과정(1).txt  

압축 해제 이후 커널 패치 과정
=============================

1. 개요

   이 문서는 커널의 압축 해제 루틴 이후부터의 커널 패치를
   하는 과정에 대하여 시간순으로 기술한 문서이다.
  
2. 문서

   이 문서를 작성하기 위하여 권수호씨가 작성한 문서를 참고 하였다.
      http://kelp.or.kr/korweblog/upload/12/20010725160834/045-LinuxKernel-
chapter38_SA1100_Booting_head_S.doc
      
3. 패치 과정

   head.S 의 함수를 추적하기 위한 방법은
  
   돈이 있다면 가장 좋은 것은 에뮬레이터를 이용하는 것이다.
   하지만 돈이 없다면...
  
   결국 몸으로 때우는 수밖에 없다...
  
   여기서는 몸으로 때우는 방법을 검토해 보자.
  
   보통 루틴 추적 방법은 확인하고자 하는 위치에 도달했는가 하는 것과
   도달 했다면 검사하고자 하는 값을 보아야 하는데..
  
   일단 위치잡는 최악의 방법은 LED를 점멸 시키는 방버이다.
   하지만 이것의 문제는 값을 보기가 힘들다는 점
  
   두번째는 시리얼을 이용하는 방법이다.
  
   일단 부팅 메세지가 나오므로
   우리는 시리얼을 이용하는 방법을 찾아 보자.
  
   이 시리얼 메세지를 보는 방법은
   head.S 를 추적할 때 쓰는 나의 방법이다.
  
   물론 이것은 원칙적으로 arch_decomp_setup() 함수가 호출된 이후에
   동작되지만 EZ-M28 에서는 특별히 하는일이 없으므로 그냥 쓴다.
  
   그리고 어셈블러가 조금 약한 나는 그냥
  
   misc.에 출력 함수를 쓰고
  
   이를 어셈블러에서 호출하는 방식을 쓴다.
  
   여기서 알아야 할것은
  
   arm 에서 한두개의 파라메터를 전달하고자 한다면
  
   r0,r1,r2 이 정도만 사용하면 된다.
  
   우리는 이에 대한 루틴을 만들어 보자..
  
   우선 misc.c 에 다음 함수를 추가 한다.
   이 함수는 나중에 지우는 것이 좋다. (^^)
  
   void CheckMsg2( int a )
   {
        char buff[2];
        
        a = a & 0xF;
        
        if( a >= 10 ) buff[0] = a - 10 + 'A' ;
        else          buff[0] = a + '0' ;
        
        buff[1] = 0;
        
        puts( buff  );
   }
   void CheckMsg( int a )
   {
        
        puts("Check1[");
        
        CheckMsg2( a >> 28 );
        CheckMsg2( a >> 24 );
        CheckMsg2( a >> 20 );
        CheckMsg2( a >> 16 );
        CheckMsg2( a >> 12 );
        CheckMsg2( a >>  8 );
        CheckMsg2( a >>  4 );
        CheckMsg2( a       );
                
        puts("]n");
        
        while(1); // 무한 루프를 돈다.
        
   }

   어셈블러에서 호출할대는
  
   bl CheckMsg
  
   라고 하면 되고
   r0 에 파라메터 a에 해당하는 값을 넣으면 된다.

   체크 포인트를 찍는 방법중에 또 다른 하나는 강제 리셋이다.

   어셈블러에서 다음 코드가 강제로 리셋 시키는 것이다.
    
                ldr     r1, =0x10000010
                mov     r0, #1
                str     r0,[r1]
                
   그리고 가장 일반적인 방법은 붙어 있는 led에 빛나게 하는 것이다.
   아래 코드는 EZ-M28 일 경우이다.
  
                ldr     r1, =0x1010001c
                ldr     r0, =0x7          <== 키고 싶은 LED 상태
dled0:          str     r0,[r1]
                b       dled0

  
   커널의 압축 해제를 수행하는 함수는
  
     arch/arm/boot/compressed/head.S 화일의

   bl        decompress_kernel 를 호출하는 부분이다.
  
   이렇게 호출하는 부분은 두가지가 있는데
  
   EZ-M28에서 호출되는 위치는
  
   243 라인이다.
  

   압축이 해제된 이후에
  
   커널의 압축이 풀린 후 풀린 압축 데이타와 실제 커널의 위치가 다르므로
   이를 맞추는 작업을 수행한다.
  
   현재 수행되는 코드를 압축된 뒤로  옳긴 후 커널을 재배치 하게 된다.
  
   아래의 루틴이 그것이다. ^^  
   ==[247 - 269]=====================================================================  
        /*
         * r0     = decompressed kernel length
         * r1-r3  = unused
         * r4     = kernel execution address
         * r5     = decompressed kernel start
         * r6     = processor ID
         * r7     = architecture ID
         * r8-r14 = unused
         */
        
                        add        r1, r5, r0                @ end of decompressed kernel
                        adr        r2, reloc_start
                        ldr        r3, LC1
                        add        r3, r2, r3
        1:                ldmia        r2!, {r8 - r13}                @ copy relocation code
                        stmia        r1!, {r8 - r13}
                        ldmia        r2!, {r8 - r13}
                        stmia        r1!, {r8 - r13}
                        cmp        r2, r3
                        blo        1b
        
                        bl        cache_clean_flush
                        add        pc, r5, r0                @ call relocation code
   ==================================================================================  

   이후 제어는 reloc_start 가 수행된다.
  
   현재 점검 결과 해당 루틴으로 즉 reloc_start 로 진행되지 않는 것으로 보인다.
  
   하나씩 점검 해봐야 할듯
  
                   add        r1, r5, r0                @ end of decompressed kernel
                mov     r0, r1                                       
                
    다음 문장을 수행한 후에
    
       add        r1, r5, r0                @ end of decompressed kernel            

    r1에 담겨진 주소는  0x0820A894 이다.
    
    즉 커널이미지 압축이 풀린 이미지의 끝 위치가 0820A894 의미이다.
    
    다음 문장을 수행한 후에  
    
       adr        r2, reloc_start
       ldr        r3, LC1
       add        r3, r2, r3
    
    r3에 담겨진 주소는 0x0820A894 이다.
    
    이건 좀 이상하다. ?
    
    왜 이런 현상이 생길까?
    
    좀더 조사해 보니
    
    r2 는 0x08008260 이다.  
    r3 는   000001B4 이다.
            
    더하면 0x08008414 이다.    
    
    어? 내가 뭘 잘못했나?
    
    다시 찍어 보니 0x08008414 가 나왔다.
    
    정리해 보면 r1 0x0820A894 은 압축이 풀린 커널 이미지의 끝 위치
                  
                    
                r2 0x08008260 는 재 배치 코드의 시작 위치 즉 reloc_start
                    
                    
                r3 0x08008414 는 재 배치 코드의 끝 위치 즉   reloc_end 값이 들어 가 있다.
                    
    
    다음 문장은    
    
        1:                ldmia        r2!, {r8 - r13}                @ copy relocation code
                        stmia        r1!, {r8 - r13}
                        ldmia        r2!, {r8 - r13}
                        stmia        r1!, {r8 - r13}
                        cmp        r2, r3
                        blo        1b
    
    재 배치 코드의 영역을 압축이 풀린 커널 이미지의 끝 쪽에 이동시키는 것이다.
    
    재 배치 코드는 압축이 풀린 커널 이미지를 실제로 위치할 곳으로 이동시켜주는
    코드 이다.
    
    이와 같이 뒤쪽에 위치해서 수행되어야  이미지 복사중에도 문제가 없기 때문이다.
    
    일단 이 루틴 까지 진행이 제대로 되는지 검증하기로 하자..
    
    그런데 스택은 어떻게 되어 있을 까 ?
    
    그래서 스택 포인터 값을 찍어 보기로 했다.
    
    압축 풀기 직전에 스택 위치는  0x080ABF94 이다.

   위 문자을 수행하기 이전의 스택 포인터는 080ABF98 이다.
  
   별로 변한것이 없다.
  
   그런 왜 위 문장을 수행한 이후에 처리가 되지 않는 것일까?
  
   위 문장 바로 위에서의 PC값은 얼마일까?
  
    PC => 0x080080FC 이다.
    
    정리
    
    r1 : 0x0820A894 커널 이미지 끝
    r2 : 0x08008260 reloc_start
    r3 : 0x08008414 reloc_end
    sp : 0x080ABF98    
    pc : 0x080080FC
    
    위 루틴이 하는 것은
    
        0x08008260 에서 0x08008414 의 영역을
        0x0820A894 로 옮기는 것이다.
        
     그런데 이것을 수행한 이후에는 시리얼 출력이 제대로
     수행되지 않는다?
    
     이유가 무엇일까?
    
   실제 시스템 패닉이 나는 부분은
  
     1:                ldmia        r2!, {r8 - r13}                @ copy relocation code
                stmia        r1!, {r8 - r13}
                ldmia        r2!, {r8 - r13}
                stmia        r1!, {r8 - r13}

    이다..
    
    이 부분을 다음과 같이 수정하였다.
    
    1:                ldmia        r2!, {r8 - r11}                @ copy relocation code
                stmia        r1!, {r8 - r11}
                ldmia        r2!, {r8 - r11}                @ copy relocation code
                stmia        r1!, {r8 - r11}
                ldmia        r2!, {r8 - r11}                @ copy relocation code
                stmia        r1!, {r8 - r11}

    정상적으로 동작했다.
    
    현재 추정은 SDRAM 과 메모리 컨트롤러의 버스트 모드의 문제로
    보인다.
    
    정확한 이유는 모르겠다. ㅜㅜ
    
    다음은
    
    bl        cache_clean_flush 인데
    
    이것은 캐쉬 데이타를 없애주는 역활을 한다.
    
    이것은 조금 더 분석해야 할듯...
    
    일단 재배치 위치에서 동작하여야 하는데 다음 문장
    
       add        pc, r5, r0                @ call relocation code
    
    으로 인해서
    
    
    옮겨진 reloc_start 의 위치로 점프한다.
    
   ==================================================================================  
        reloc_start:        add        r8, r5, r0
                        debug_reloc_start
                        mov        r1, r4
        
        1:
                        .rept        4
                        ldmia        r5!, {r0, r2, r3, r9 - r13}        @ relocate kernel
                        stmia        r1!, {r0, r2, r3, r9 - r13}
                        .endr
        
                        cmp        r5, r8
                        blo        1b
                        debug_reloc_end
        
        call_kernel:        bl        cache_clean_flush
                        bl        cache_off
                        mov        r0, #0
                        mov        r1, r7                        @ restore architecture number
                        mov        pc, r4                        @ call kernel
   ==================================================================================  

   이 루틴은 풀린 커널 이미지를 실제의 위치로 배 배치한다.
  
   재 배치 주소는 r4 에 가지고 있고 배치도리 커널 이미지의 위치는 r5 에 가지고 있게 된다.
  
   그래서 복사하게 된다.
  
   그리고 r4로 점프한다.
  
   이 루틴까지는 정상적으로 동작한다.
  
   여기서 잠깐 검토해 보아야 할 것이
  
   cache_clean_flush 함수이다.

   이 함수는 다음과 같은 형태를 띄고 있다.
      
   cache_clean_flush:
                mov        r3, #16
                b        call_cache_fn

   call_cache_fn 은 부른다.
  
   이 루틴은 다음과 같은 형태를 띄고 있다.
  
        call_cache_fn:        adr        r12, proc_types
                        mrc        p15, 0, r6, c0, c0        @ get processor ID
        1:                ldr        r1, [r12, #0]                @ get value
                        ldr        r2, [r12, #4]                @ get mask
                        eor        r1, r1, r6                @ (real ^ match)
                        tst        r1, r2                        @       & mask
                        addeq        pc, r12, r3                @ call cache function
                        add        r12, r12, #4*5
                        b        1b

   이 루틴은
  
   mrc        p15, 0, r6, c0, c0        @ get processor ID
  
   을 통하여 r6 레지스터에 프로세서 ID를 가져 온다.
  
   이후 proc_types 에 등록된 프로세스 타입을 검색해서
   일치하는 프로세스 타입을 만나면 r3 에 등록된 오프셋에
   해당하는 루틴을 수행하게 된다.
  
   위 루틴을 통해서 ARM920T 의 armv4_cache_flush을 수행하게 도니다.
  
                   .word        0x41129200                @ ARM920T
                .word        0xff00fff0
                b        __armv4_cache_on
                b        __armv4_cache_off
                b        __armv4_cache_flush

   일단 r6에 어떤값이 생기는지 보자
  
    정확하게 0x41129200  이 읽힌다.
    
   이제 마지막으로 r7 에 담겨져 있는 architecture ID 를
   조사해 보자
  
   체크해 보니 0x0000012F 즉 303 이 나오고 있다.
   당연하다 부트로더에서 303이라는 값으로 설정하고 있기 때문이다.

   S3C2410 은 어떻게 되어 있을까?
        
   아키텍쳐 ID는 다음의 위치에 기록되어 있다.
  
   arch/arm/tools/mach-types 란 파일이다.
  
   s3c2410                        ARCH_S3C2410                S3C2410                
        182
  
   이다.
  
   이것은 CPU에 따른 하드웨어의 특성을 커널에 반영하기 위한
   자료 구조에 사용되는 매크로 화일의 번호값이기도 하다.
  
   원래 이런식으로 하면 조금 곤란하다.
   보드별로 따로 받아야 한다.
  
   하지만 진행을 위해서 이 부분의 수정은 하지 않기로 하였다.
  
   최종적인 EZ-M28을 위해서 따로 만들어야 할 것이다.  
  
   이 값은 현재 arch/arm/boot/compressed/head-s3c2410.S
   에서 선언해 주고 있는데
  
   우리는 이전에 무시하게 했었다.
  
   하지만 이부분만 살리기로 한다.
  
   다음과 같은 부분이다.
  
        #if defined(CONFIG_ARCH_S3C2410)
                mov        r7, #MACH_TYPE_S3C2410
                mov        r8, #0
        #endif
  
   이것은
  
   #if 0  // 무시시작
  
   라는 명령문으로 만든 바로 위에 복사해 넣는다.
  
   나중에는 제거할 것이다.
   즉 부트로더에서 직접 주게 할것이다.
  
   다시 r7 에 담겨져 있는 architecture ID 를 조사해 보면
  
     0x000000B6 이 나온다.
    
  즉 arch/arm/tools/mach-types 에 적혀 있던 대로 182 란 값이
  나온다.
  
  이제 정말 실제 커널로 점프했는지를 조사해야 한다.
  
  그래야 압축이 정확하게 풀린 것이니까...
  
  클클
  
  진짜(?) 커널의 시작 위치는
  
  arch/arm/kernel/head-armv.S 이다.
  
  그리고 가장 처음 시작되는 위치는 다음이다.
  
   ==================================================================================  
        /*
         *  Kernel startup entry point.
         *
         * The rules are:
         *  r0      - should be 0
         *  r1      - unique architecture number
         *  MMU     - off
         *  I-cache - on or off
         *  D-cache - off
         *
         * See linux/arch/arm/tools/mach-types for the complete list of numbers
         * for r1.
         */
                        .section ".text.init",#alloc,#execinstr
                        .type        stext, #function
        ENTRY(stext)
                mov        r12, r0

   ==================================================================================  
    
   아직까지 MMU가 활성화 되어 있지 않으므로 당분간 위에서 사용한
   디버깅 방법을 사용할수 있다.
  
   물론 다른 디버깅 방법도 강구해야 하지만
  
   일단 LED로 이 지점에 도착했나를 체크해 보자
  
   여기서 도착 유무는 led로 해 보기로 했다.
  
   다음과 같은 루틴을
  
   ==================================================================================  
                        mov     r1,   #0x10000000    @C  
                        add     r1, r1, #0x100000    @C
                        add     r1, r1,     #0x1C    @C
                        mov     r0, #0x7             @C
        dled0:          str     r0,[r1]              @C
                        b       dled0                @C
   ==================================================================================  

    mov        r12, r0 다음에 삽입하였다.
    
    테스트 결과 통과 !!! 큭큭... 뭔가 되어 가는 것 같다.
    
    자 이제
    
    디버깅을 위해서 제공된 커널 디버깅 옵션을 활성화 하는 것에 대하여
    연구해 보자.
    
    클클...

    커밋한 CVS :  arch/arm/boot/compressed/head.S
                  arch/arm/boot/compressed/head-s3c2410.S



2003-07-26 18:20:43

728x90

'Research > SystemProg' 카테고리의 다른 글

가상 메모리  (0) 2004.06.07
커널 디버깅 기능 활성화과정  (0) 2004.06.06
[Q] relocation of bss and data  (0) 2003.12.10
Glabl변수의 초기화 및 영역  (0) 2003.12.01
커널에서 double형의 연산이 가능한가요?  (0) 2002.12.04