Part10. 부트 로더(Boot Loader) 설명

원문 :  http://kkamagui.springnote.com/pages/347742

 

들어가기 전에...

0.시작하면서...

 부트 로더(Boot Loader)는 BIOS로부터 제어를 넘겨받아서 처음으로 실행되는 프로그램이다. 그렇다보니 C Runtime 환경 같은건 전혀 기대할 수 없고 어셈블리어로 구현하는 것이 보통이다.

 부트 로더 코더를 세세하게 설명하면 끝이 없으므로, 간단히 기능적인 관점으로 나누어 설명하겠다.

 부트 로더의 소스는 프레임워크 안에 01Boot 폴더에 00Bootstrap 안에 있다. 소스를 같이 참고하면서 보자.

 

 부팅이 되고나면 부트 로더에서 해야하는 작업은 크게 세가지다.

  1. 코드/데이터/스택 영역 설정 및 초기화
  2. 커널 로더 및 커널의 이미지 로딩
  3. 커널 로더의 실행

 

 그럼 이제 각각에 대해서 알아보도록하자.

 

1.코드/데이터/스택 영역 설정 및 초기화

 부트 로더가 제어를 넘겨받았을 때 레지스터의 상태는 BIOS에서 코드 수행시에 사용되던 값이다. 이대로 사용해도 괜찮지만 BIOS가 여러종류가 있고 각 BIOS 마다 코드가 다르므로 레지스터의 값을 예측할 수 없다. 애매한 상황을 피하기위해 각 레지스터의 값을 새로 설정해 준다.

  1. jmp 0x07c0:start <== 코드 세그먼트 레지스터를 0x07c0로 설정
    start :
            mov     ax, cs
            mov     ds, ax
            mov     es, ax
            ;   스텍의 설정
            ;   함수 호출을 위해 필수
            mov     ax, 0x0000
            mov     ss, ax
            mov     ax, 0xffff
            mov     sp, ax
            mov     bp, ax

 위와 같이 세그먼트 레지스터를 코드 영역과 같이 설정해 주고 스택을 세그먼트의 끝에서부터 자라도록 설정해준다.

 위에서 jmp 0x07c0:start 코드를 볼 수 있는데, 이 코드는 CS 세그먼트 레지스터에 0x07c0을 설정하고 IP 레지스터에 start의 주소를 설정하는 코드이다. CS 세그먼트 레지스터에 0x07c0을 설정하면 어떤 일이 발생하는 것일까? 16bit 모드에서 세그먼트 레지스터의 역할은 32bit 모드의 역할과 다르다.

 16bit 모드의 세그먼트 레지스터의 역할은 Base 주소 역할만 하는데, 주소 계산 방식은 아래와 같다.

실제 주소 = 세그먼트 레지스터의 값 << 4 + 레지스터의 값

 즉 start 코드가 위치하는 실제 주소는 0x7c00  + start의 주소가 되는 것이다.

 

 그렇다면 0x7c00 주소는 무엇일까? 0x7c00의 주소는 BIOS가 디스크로부터 한 섹터를 읽어들여 메모리에 복사하는 위치이다. 다시말해 부트 코드가 0x7c00에 위치하며 BIOS가 0x7c00의 주소로 jump해서 부트 코드를 실행한다. 부트 코드가 정상적으로 실행되기 위해서는 0x7c00에서 실행되도록 컴파일 되어야 함은 두말할 필요도 없다.

 

2.이미지 로딩

2.1 플로피 디스크(Floppy Disk) 분석

 부트 로더의 코드 중에 가장 복잡한 부분이다. 플로피에 저장된 이미지를 모두 로딩하는 역할을 하는 함수인데, 코드를 이해하기 위해서는 플로피 디스크에 대한 지식이 필요하다.

 그럼 간단히 플로피 디스크에 대해 알아보자. 플로피 디스크는 아래와 같은 세가지로 구성된다.

  • 섹터(Sector) : 실제적인 데이터를 저장하는 영역. 일반적으로 512Byte 크기. 1~18번까지 총 18개 섹터가 모여서 하나의 트랙을 구성
  • 트랙(Track) : 섹터가 모여 구성된 영역. 0~79번까지의 총 80개의 트랙이 모여서 하나의 헤드를 구성
  • 헤드(Head) : 트랙이 모여 구성된 영역.  플로피 디스크는 일반적으로 2개의 헤드를 가짐.

  이것을 그림으로 보면 아래와 같다.

플로피구조.PNG

<섹터/트랙/헤드의 구성> 

 

 고용량의 플로피 디스크(1.44Mbyte)는 양면으로 되어있기 때문에 헤드의 값이 2이다. 일단 섹터/트랙/헤드의 구성에 대해 알아보았으니, 섹터를 읽어 데이터를 로드한다고 가정하고 어떤 순서로 로딩하는지 알아보자.

 플로피 디스크에 데이터를 읽고 쓰는 순서는 어떻게 될까? 0부터 끝까지 계속 데이터를 써내려간다고 가정하면 아래와 같은 순서로 읽고 쓰게 된다.포인트는 섹터 -> 헤드 -> 트랙 순으로 증가하는 값이다.

  1. 섹터 1, 트랙 0, 헤드 0 ~ 섹터 18, 트랙 0, 헤드 0 까지 작업
  2. 헤드를 1로 변경하고 다시 위의 1 과정을 반복 
  3. 헤드를 0으로 변경하고 트랙을 1 증가. 다시 위의 1~2 과정을 반복 
  4. 위의 1~3 과정을 트랙 0~79까지 반복

 

2.2 플로피 디스크(Floppy Disk) 제어 코드

 위의 일련의 작업을 코드로 옮긴 것이 아래의 코드다.

  1. ;   플로피로 부터 커널 로더를 읽어 들인다.
    ReadSector:
        push    bp
        push    es
        pushf
       
        mov     ax, 0xb800
        mov     gs, ax
       
        reset:  ;   reset floppy
            mov     ax, 0
            mov     dl, 0               ;   Drive=0(A Drive)
            int     0x13                ;   명령전송
            jc      reset               ;   문제가 생기면 다시 시도 한다.  
            mov     ax, 0x1000;0x07e0   ;   이미지를 읽어들일 segment
            mov     es, ax
            mov     bx, 0
  2.     ;   아래의 부분을 반복한다.
        read:       ;   read 하는 부분
            mov     ah, 2                   ;   bios 명령 포멧 es:bx에 저장한다.
            mov     al, 1                   ;   Load 1 Sector
            mov     ch, byte [TRACKCOUNT]   ;   Cylinder    = 0
            mov     cl, byte [SECTORCOUNT]  ;   Sector 2 부터
            mov     dh, byte [HEADCOUNT]    ;   Head 0
            mov     dl, 0                   ;   Drive 0 (A Drive)
            int     0x13                    ;   명령 전송
            jc      error
  3.         ;   그리고 섹터값을 증가시킨다.
            ;   플레그 레지스터를 저장해서 증가 시키는 루틴으로 인해
            ;   플레그의 값이 바뀌는일이 없도록 한다.
            inc     byte [SECTORCOUNT]      ;   섹터를 1 증가한다.
            cmp     byte [SECTORCOUNT], 19  ;   섹터는 0 부터 18 까지 있으므로
            jb      endRead                 ;   작으면 다음 섹터를 읽고
            mov     byte [SECTORCOUNT], 1   ;   같으면 섹터의 크기를 1로 만들고
            inc     byte [HEADCOUNT]        ;   헤드를 하나 증가 시킨다.
            cmp     byte [HEADCOUNT], 2     ;   헤드는 0 에서 1까지 있으므로
            jb      endRead                 ;   작으면 다음 섹터를 읽고
            mov     byte [HEADCOUNT], 0     ;   같으면 헤드를 0 설정
            inc     byte [TRACKCOUNT]       ;   트렉을 하나 증가시킨다.
            cmp     byte [TRACKCOUNT], 80
            jb      endRead
            ;   트렉이 넘어 섰나 체크는 안해도 된다.
            ;   커널이 1.44 메가를 넘길려고...
       
        endRead:
            ;   한섹터가 0x200 이므로 세그먼트 레지스터를 증가 시킨다.
    mov     bx, es
            add     bx, 0x20
            mov     es, bx
            ;   디버깅
            mov     ax, es
            mov     byte [gs:0x00], ah
            mov     byte [gs:0x02], al
            ;   디버깅 끝
       
            ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            ;   진행상황 표시
            mov     bx, word [LOOPCOUNT]
            mov     byte [gs:402], bl
            mov     byte [gs:403], 0x02
            ;   진행상황 점찍기(일단 주석처리)
            ;mov    bx, word [LOOPCOUNT]
            ;mov    ax, 2
            ;mul    bx
            ;mov    bx, ax
            ;add    bx, 482
            ;mov    byte [gs:bx], '.'
            ;inc    bx
            ;mov    byte [gs:bx], 0x02
            ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            ;   루프 횟수 계산하기
            mov     bx, 0
            inc     word [LOOPCOUNT]   
            mov     cx, word [LOOPCOUNT]
            cmp     cx, word [ds:0x01f8]
            jb      read
  4.     ; 다 읽었으면 Fdd를 Reset 하고 초기화 시켜둔다.
        reset_end:  ;   reset floppy
            mov     ax, 0
            mov     dl, 0           ;   Drive=0(A Drive)
            int     0x13            ;   명령전송
            jc      reset_end       ;   문제가 생기면 다시 시도 한다.  
       
        popf
        pop     es
        pop     bp
        retn

 

3.커널 로더 실행

  커널 로더를 실행하는 부분은 아주 간단하다. 커널 이미지가 0x10000 주소에 로딩되기 때문에 jump만 하면 된다. 아래는 그 코드이다.

  1.         jmp     0x1000:0 ;0x07e0:0

 

 

4.마치면서...

 이상으로 부트 로더에 대해서 간단히 알아보았다. 어셈블리어 코드 하나하나를 설명하면 좋겠지만 세부적인 내용이 궁금한 사람은 Intel Architecture Manual을 참고하자. 다음에는 커널 로더에 대해 알아보자.

 

 

5.첨부

 

 

 

이 글은 스프링노트에서 작성되었습니다.

이 글은 스프링노트에서 작성되었습니다.

이 글은 스프링노트에서 작성되었습니다.

+ Recent posts