ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [OS] 13. Kernel Barebones
    OS/OS from Scratch 2021. 6. 27. 15:07

    서론

    이번 강의에서는 Kernel과 boot sector를 연결해 kernel을 실행시켜 보겠습니다. 해당 Github 강의는 다음과 같습니다.

    https://github.com/cfenollosa/os-tutorial/tree/master/13-kernel-barebones

     

    cfenollosa/os-tutorial

    How to create an OS from scratch. Contribute to cfenollosa/os-tutorial development by creating an account on GitHub.

    github.com


    이론

    Kernel

    Kernel은 하드웨어 바로 위 단계에서 작동하며 애플리케이션들의 CPU와 메모리 등의 리소스를 관리하는 메모리에 항상 상주하고 있는 프로그램입니다.  Kernel에는 여러 디바이스 드라이버들이 있어 블루투스나 파일 시스템 등을 사용하려면 Kernel을 이용해 접근하게 됩니다.

    ELF (Excuatble and Linkable Format)

    ELF 포맷은 리눅스에서 사용하는 실행 파일의 형식입니다. ELF 포맷은 주로 리눅스에서 실행 파일, 공유 라이브러리, Object 파일, Coredump 파일, Kernel boot 이미지에 사용됩니다. 자세한 설명은 이 블로그를 참고하세요.

    Makefile

    리눅스나 OSX의 Shell에서 Makefile이 있는 디렉토리에서 make 명령어를 실행하면 Makefile에 적힌 대로 파일 간의 종속 관계를 파악해 Shell에 명령어들이 순차적으로 실행됩니다. 이를 통해 컴파일을 쉽게 할 수 있습니다. Makefile에 대해서는 이 블로그에 자세히 설명되어있습니다.

    Makefile에는 다양한 명령어들이 있지만, 본 강의에서는 다음 명령어들만 사용하겠습니다.

    • target: dependency1, dependency2, ... : target 파일을 만들기 위해 필요한 구성요소 (dependency)
      • command: 해당 target 파일을 만들기 위한 명령어
    • all: Makefile에 파라미터가 입력되지 않았을 때 실행할 타겟절
    • clean: make clean 명령어를 실행시키는 경우 make를 통해 만들어진 모든 파일을 삭제
    • $@: Target 파일
    • $<: 첫 번째 dependency
    • $^: 모든 dependency

    Shell에서 파일 합치기

    두 파일 bootsect.bin, kernel.bin을 이어 붙어 os-image.bin이라는 파일을 만들기 위한 명령어는 다음과 같습니다.

    cat bootsect.bin kernel.bin > os-image.bin

    코드

    kernel.c

    이번 강의에서 사용할 Kernel은 화면에 'X'를 출력하는 가장 단순한 형태의 커널입니다.

    void main() {
        char *video_memory = (char*) 0xb8000;
        *video_memory = 'X';
    }

    kernel_entry.asm

    kernel_entry.asm에서 kernel을 불러옵니다. [extern main] 명령어는 kernel.c의 main 함수를 Calling point로 설정하고, call main을 통해 kernel.c의 main함수를 불러오게 됩니다. 이때 linker에 의해 main함수가 위치한 메모리를 알아내게 됩니다. 

    [bits 32]
    ; Calling Point 설정, kernel.c의 main함수와 동일한 이름
    [extern main]
    call main
    jmp $

    이렇게 만들어진 kernel.c와 kernel_entry.asm을 하나의 binary 파일로 만들기 위해 다음 명령어를 실행시킵니다. 이때 kernel의 위치를 0x1000로 정해줍니다.

    i386-elf-ld -o kernel.bin -Ttext 0x1000 kernel_entry.o kernel.o --oformat binary

    bootsect.asm

    bootsect.asm은 10강에서 만든 bootsector와 매우 유사한 형태입니다. 먼저 Kernel의 offset을 0x1000임을 지정하고 dl레지스터에 저장된 boot drive를 데이터가 덮어 써지는 것을 막기 위해 메모리에 따로 저장해 둡니다. 다음으로는 load_kernel레이블에서 7강에서 만든 boot_sect_disk.asm을 사용하기 위한 설정들을 해준 다음 Protected Mode로 진입합니다. Protected Mode에 진입한 후 Kernel offset 주소를 불러 kernel.c를 실행시킵니다.

    [org 0x7c00]
    KERNEL_OFFSET equ 0x1000
    
    ; BIOS는 boot drive를 dl 레지스터에 저장
    mov [BOOT_DRIVE], dl
    
    mov bp, 0x9000
    mov sp, bp
    
    mov bx, MSG_REAL_MODE
    call print
    call print_nl
    
    ; Disk에서 kernel read
    call load_kernel
    ; Interrupt disable, GDT load
    call switch_to_pm
    
    %include "boot_sect_print_function.asm"
    %include "boot_sect_print_function_hex.asm"
    %include "boot_sect_disk.asm"
    %include "32bit_gdt.asm"
    %include "32bit_print.asm"
    %include "32bit_switch.asm"
    
    [bits 16]
    load_kernel:
        mov bx, MSG_LOAD_KERNEL
        call print
        call print_nl
    
        mov bx, KERNEL_OFFSET
        mov dh, 2
        mov dl, [BOOT_DRIVE]
        call disk_load
        ret
    
    [bits 32]
    BEGIN_PM:
        mov ebx, MSG_PROT_MODE
        call print_string_pm
        
        ; Kernel에게 넘겨줌
        call KERNEL_OFFSET
        
        jmp $
    
    ; dl레지스터가 덮어씌어질 수 있으므로 메모리에 저장
    BOOT_DRIVE db 0
    
    MSG_REAL_MODE db "Started in 16-bit Real Mode", 0x00
    MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0x00
    MSG_LOAD_KERNEL db "Loading Kernel into Memory", 0x00
    
    times 510-($-$$) db 0
    dw 0xaa55

    boot sector를 만든 후 만들어진 kernel과 합쳐 실행파일을 만들어야 합니다. 이를 위해 다음 명령어를 사용합니다.

    cat bootsect.bin kernel.bin > os-image.bin

    Makefile

    앞으로 컴파일하는 과정을 자동화하기 위해 Makefile을 만들어 make 명령어를 통해 OS를 실행시키겠습니다.

    # $@: Target file
    # $<: first dependency
    # $^: all dependency
    
    all: run
    
    kernel.bin: kernel_entry.o kernel.o
    	i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary
    
    kernel_entry.o: kernel_entry.asm
    	nasm $< -f elf -o $@
    
    kernel.o: kernel.c
    	i386-elf-gcc -ffreestanding -c $< -o $@
    
    # Disassemble
    kernel.dis: kernel.bin
    	ndisasm -b 32 $< > $@
    
    bootsect.bin: bootsect.asm
    	nasm $< -f bin -o $@
    
    os-image.bin: bootsect.bin kernel.bin
    	cat $^ > $@
    
    run: os-image.bin
    	qemu-system-i386 -fda $<
    
    clean:
    	rm *.bin *.o *.dis

    실행 결과

    make 명령어로 실행한 결과는 다음과 같습니다. 16bit Real Mode로 진입한 후 32 bit Protected Mode로 진입해 Kernel을 메모리에서 부른 후 화면 가장 좌측 상단에 'X'가 출력된 것을 볼 수 있습니다.

     

    실행결과

     

    'OS > OS from Scratch' 카테고리의 다른 글

    [OS] 15. Video Ports  (0) 2021.06.30
    [OS] 14. Checkpoint  (0) 2021.06.28
    [OS] 12. Kernel C  (0) 2021.06.26
    [OS] 11. Kernel Cross Compiler  (0) 2021.06.23
    [OS] 10. 32 bit Enter  (0) 2021.06.23
Designed by Tistory.