6. Bootloader 만들기

<우리가 만들 os 디렉터리 구조>

make프로그램

  • 실행파일 또는 라이브러리 파일을 만들어주는 빌드관련 유틸리티
  • make문법의 기본형식은 Target, Dependency, Command 세부분을 구성

  • Target은 생성할 파일
  • Dependency는 Target 생성에 필요한 소스파일이나 오브젝트 파일 등
  • Command는 Dependency에 관련된 파일이 수정되면 실행할 명령

<간단한 makefile 예제>

  • makefile은 역순으로 따라가면 된다.
  • 최종으로 생성해야 하는 결과물이 output.exe
  • output.exe: 라인을 보면 dependency가 a.o b.o가 필요한 것을 알 수 있다.
  • a.o와 b.o는 a.c 와 b.c를 컴파일 해서 생성되는 것을 알 수 있다.
  • 가장 윗부분에 all: target이 보이는데 기본적으로 사용하는 target이다.
  • 여러 target을 빌드할 때 all target의 오른쪽에 순서대로 나열하면 한번에 처리할 수 있다.
  • 빌드 과정에서 library 디렉터리를 빌드해야 한다면 -C옵션을 사용하면 된다. (아래 그림 참조)

Bootloader 작성

./makefile

all: BootLoader Disk.img

BootLoader:
  @echo
  @echo ============== Build Boot Loader ==============
  @echo

  make -C 00.BootLoader

  @echo
  @echo ============== Build Complete ==============
  @echo

Disk.img: 00.BootLoader/BootLoader.bin
  @echo
  @echo ============== Disk Image Build Start ==============
  @echo

  cp 00.BootLoader/BootLoader.bin Disk.img

  @echo
  @echo ============== All Build Complete ==============
  @echo

clean:
  make -C 00.BootLoader clean
  rm -f Disk.img
  • 최상위 makefile의 목적은 os 이미지 생성을 위해 하위 디렉터리의 makefile을 실행하는 것
  • clean target은 빌드 과정에서 생성된 파일을 삭제할 목적으로 정의했음.

./00.BootLoader/makefile

all: BootLoader.bin

BootLoader.bin: BootLoader.asm
  nasm -o BootLoader.bin BootLoader.asm

clean:
  rm -f BootLoader.bin

BootLoader 제작

기본적인 BootLoader 제작

  • 부트로더를 메모리에 정상적으로 복사하려면 부트섹터 512바이트에서 마지막 2바이트를 0x55, 0xAA로 저장하면 된다.

./00.BootLoader/BootLoader.asm

[ORG 0x00]    ; 코드의 시작 어드레스를 0x00으로 설정
[BITS 16]     ; 이하의 코드는 16비트 코드로 설정

SECTION.text  ; text 섹션(세그먼트)을 정의

jmp $         ; 현재 위치에서 무한 루프 수행

time 510 - ($ - $$) db  0x00  ; $: 현재 라인의 어드레스
                              ; $$: 현재 섹션(.text)의 시작 어드레스
                              ; $ -$$: 현재 섹션을 기준으로 하는 오프셋
                              ; 510 - ($ - $$): 현재부터 어드레스 510까지
                              ; db 0x00: 1바이트를 선언하고 값은 0x00
                              ; time: 반복수행
                              ; 현재 위치에서 어드레스 510까지 0x00으로 채움

db 0x55       ; 1바이트를 선언하고 값은 0x55
db 0xAA       ; 1바이트를 선언하고 값은 0xAA
              ; 어드레스 511, 512에 0x55, 0xAA를 써서 부트 섹터로 표기함

qemu실행

$ sudo qemu-system-x86_64 -m 64 -fda ./Disk.img -localtime -M pc

실행결과

화면버퍼와 화면제어

  • 기본으로 설정되는 화면 모드는 텍스트모드로 크기는 가로 80character, 세로 25character이다.
  • 비디오 메모리 어드레스는 0xB8000에서 시작한다.
  • 총 메모리 크기는 80252=4000바이트

  • 속성값은 하위 4비트의 전경색, 상위 4비트의 배경색으로 구분된다.
  • 각각 전경색, 배경색에서 최상위의 특수기능 비트가 있따.

<0xB8000와 0xB8001 어드레스에 값을 설정하는 코드>

  • 물리주소 0xB8000이 기준 어드레스로 사용
  • 화면 맨위는 0xB8000이므로 0xB8000과 0xB8001에 'M'과 0x4A를 쓰면 빨간색 배경에 밝은 녹색으로 'M'을 출력할 수 있다.
  • DS 세그먼트의 값이 0xB800이므로 0x00, 0x01을 지정하면 세그먼트:오프셋이 0xB800:0xB0000과 0xB800:0x0001이 되며 이는 물리주소 0xB8000, 0xB8001을 뜻한다.

세그먼트 레지스터 초기화

  • 세그먼트 레지스터를 초기화하는 코드가 필요하다.
  • 세그먼트 레지스터에는 BIOS가 사용하던 값이 들어 있기 때문

  • 그렇다면 어떤 값으로?
  • BIOS가 부트로더를 디스크에서 읽어 메모리에 복사하는 위치가 0x7C00이기 때문에 0x7C0로 초기화 한다.

  • 부트로더의 코드(Code Segment)와 데이터(Data Segment)는 0x7C00부터 512바이트 범위에 존재 하므로 CS와 DS 세그먼트 레지스터를 모두 0x7C0으로 설정했다.
  • CS 세그먼트 레지스터는 mov명령으로 처리할 수 없으며, 수정하려면 jmp명령세그먼트 레지스터 접두사를 이용해야 한다.

세그먼트 레지스터 초기화

SECTION .text     ;text 섹션(세그먼트)을 정의

jmp 0x07C0:START  ; CS세그먼트 레지스터에 0x07C0을 복사하면서 START 레이블로 이동

START:
  mov ax, 0x07C0  ; 부트로더의 시작 어드레스(0x7C00)를 세그먼트 레지스터 값으로 변환
  mov ds, ax      ; DS 세그먼트 레지스터에 설정
  mov ax, 0xB800  ; 비디오메모리의 시작어드레스(0xB800)를 세그먼트 레지스터 값으로 변환
  mov ex, ax      ; ES 세그먼트 레지스터에 설정
  • 비디오 모드에 관련된 세그먼트 레지스터가 DS -> ES 로 되었으니 이후 출력에 관계된 코드는 모두 ES세그먼트 레지스터를 기준으로 하게 수정 해야한다.
  • 세그먼트 레지스터 접두사를 쓰는 방법은 [세그먼트 레지스터:오프셋] 형식으로 쓰면 된다.

./00.BootLoader/BootLoader.asm

[ORG 0x00]   ; Code start address : 0x00
[BITS 16]    ; 16-bit environment

SECTION.text  ; text section(Segment)

jmp 0x07C0:START    ; copy 0x0C70 to cs, and goto START

START:
    mov ax, 0x07C0 ; convert start address to 0x0C70
    mov ds, ax   ; set ds register
    mov ax, 0xB800 ; base video address
    mov es, ax   ; set es register(videos address)

.SCREENCLEARLOOP:
    mov byte [ es: si ], 0     ; delete character at si index
    mov byte [ es: si + 1], 0x0A  ; copy 0x)A(black / gree)
    add si, 2            ; go to next location
    cmp si, 80 * 25 *2       ; compare si and screen size
    jl .SCREENCLEARLOOP       ; end loop if si == screen size

    mov si, 0            ; initialize si register
    mov di, 0            ; initialize di register

.MESSAGELOOP:
    mov cl, byte [ si + MESSAGE1 ] ; copy charactor which is on the address MESSAGE1's addr + SI register's value
    cmp cl, 0            ; compare the charactor and 0
    je .MESSAGEEND         ; if value is 0 -> string index is out of bound -> finish the routine

    mov byte [ es : di], cl     ; if value is not 0 -> print the charactor on 0xB800 + di
    add si, 1            ; go to next index
    add di, 2            ; go to next video address

    jmp .MESSAGELOOP        ; loop code

.MESSAGEEND:
    jmp $              ; infinite loop

MESSAGE1:    db 'OS Boot Loader Start!!', 0 ; define the string tha I want to print

times 510 - ($ - $$)  db   0x00  ; $ : current line's address
                    ; $$ : current section's base address
                    ; $ - $$ : offset!
                    ; 510 - ($ - $$) : offset to addr 510
                    ; db - 0x00 : declare 1byte and init to 0x00
                    ; time : loop
                    ; fill 0x00 from current address to 510

db 0x55 ; declare 1byte and init to 0x55
db 0xAA ; declare 1byte and init to 0xAA
    ; Address 511 : 0x55
    ; 512 : 0xAA -> declare that this sector is boot sector

'Project > OS' 카테고리의 다른 글

8. os 이미지 로딩 기능 구현  (0) 2019.07.16
7. 디스크에서 os이미지 로딩  (0) 2019.07.16
5. 부팅과 부트로더  (0) 2019.07.11
4. 운영모드와 메모리 관리기법  (0) 2019.07.11
3. 운영모드와 레지스터  (0) 2019.07.11

+ Recent posts