7. 디스크에서 OS이미지 로딩

BIOS 서비스와 소프트웨어 인터럽트

  • BIOS는 우리가 일반적으로 많이 쓰는 라이브러리 파일과 달리 특별한 방법으로 외부에 제공한다.
  • 함수의 어드레스를 인터럽트 벡터 테이블(Interrupt Vector Table)에 넣어 두고, 소프트웨어 인터럽트(SWI, Software Interrupt)를 호출하는 방법을 사용한다.
  • 인터럽트 벡터 테이블(Interrupt Vector Table)
    • 메모리 어드레스 0에 있는 테이블로 특정 번호의 인터럽트가 발생했을 때 인터럽트를 처리하는 함수(인터럽트 핸들러, Interrupt Handler) 검색에 사용한다.
    • 테이블의 각항목은 인덱스에 해당하는 인터럽트가 발생했을 때 처리하는 함수 어드레스가 저장되어 있다.
    • 각 항목은 크기가 4바이트 이다.
    • 인터럽트는 최대 256개까지 설정할 수 있으므로 리얼모드의 인터럽트 벡터 크기는 최대 256*4 = 1024바이트가 된다.

  • 디스크 서비스를 사용하려면 위의 표에 나와있듯이 0x13 인터럽트를 발생시켜야 한다.
  • BIOS의 기능을 사용하려면 레지스터를 이용해 함수의 파라미터와 return값을 주고 받는다.
  • BIOS 서비스 마다 요구하는 파라미터의 수가 다르므로 서비스에 맞는 레지스터를 확인하고 사용 할 것

플로피 디스크의 내부구조

  • 헤드는 디스크의 표면을 의미
    • 헤드의 개수는 디스크 수 * 2
    • 플로피 디스크의 경우 디스크가 한장이므로 헤드의 개수는 2이다.
  • 트랙은 디스크를 여러 개의 동심원으로 나눴을 때 그 동심원 하나가 가지는 영역을 의미한다.
    • 플로피 디스크의 경우 80개
  • 섹터는 디스크를 구성하는 가장 작은 단위로 트랙을 여러 조각으로 자른 것을 의미
    • 플로피 디스크의 경우 18개
    • 섹터번호는 1부터 시작하므로 1~18의 값을 갖는다.
  • 플로피디스크의 모든 섹터를 순차적으로 읽는 알골리즘
    1. 섹터 = 1, 헤드 = 0, 트랙 = 0으로 설정
    2. 섹터를 1에서 18까지 증가시키면서 읽음
    3. 섹터 18번까지 다 읽었으면 0번 헤드를 다 읽었으므로 헤드 1증가
      A. 헤드 = 1, 섹터 = 1로 변경
    4. 섹터를 1에서 18까지 증가시키면서 읽음
    5. 섹터 18번까지 다 읽었으면 0번과 1번 헤드를 모두 다 읽었으므로, 트랙 1증가
      A. 트랙 = 1, 헤드 = 0, 섹터 = 1로 변경
    6. 2번에서 5번과정을 79번 트랙까지 반복

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

9. 테스트를 위한 가상 os이미지 생성  (0) 2019.07.16
8. os 이미지 로딩 기능 구현  (0) 2019.07.16
6. Bootloader 만들기  (0) 2019.07.11
5. 부팅과 부트로더  (0) 2019.07.11
4. 운영모드와 메모리 관리기법  (0) 2019.07.11

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

5. 부팅과 부트로더

BIOS

  • 부팅은 PC가 켜진 후에 OS가 실행되기 전까지 수행되는 일련의 작업과정을 의미한다.
  • 부팅과정 중 하드웨어와 관련된 작업을 BIOS(Basic Input/Output System)가 담당한다.
  • BIOS에서 수행하는 각종 테스트나 초기화를 POST(Power On Self Test)라고 부른다.
  • 메인보드에 포함된 펌웨어의 일종
  • 입출력을 담당하는 프로그램
  • 부팅옵션 설정이나 시스템 전반적인 설정값(configuration)을 관리하는 역할도 한다.
  • 설정값으로 시스템을 초기화하여 os를 실행할 수 있는 환경을 만든다.

<우리가 만들 os의 부팅과정>

  • 부트로더 이미지를 메모리로 복사하는 단계가 가장 중요 (BIOS영역에서 os영역으로 넘어가는 부분)
  • POST가 완료된 후 부트로더가 존재한다면 코드를 0x7c00 어드레스에 복사한 후 프로세서가 0x7c00 어드레스부터 수행하도록 한다.
  • 하필 0x7c00인 이유는 IBM 사가 메모리의 0x00007c00 ~ 0x00007dff 번지(크기 512바이트) 를 부트 섹터가 읽혀지는 어드레스로 지정했기 때문
    출처: https://heeyamsec.tistory.com/19 [HeeYamSec]

부트로더

  • 가장 첫번째 섹터 MBR(Master Boot Record)에 있는 작은 프로그램 (섹터하나는 512바이트로 구성)
  • 부트로더 checklist
    • BIOS에 첫번째 섹터가 부트로더라는 것을 어떻게 알려줄 것인가?
    • 첫번째 섹터가 과연 정상적인 부트로더인지 어떻게 확인?
  • BIOS는 읽어드인 512바이트 중에 가장 마지막 2바이트의 값이 0x55인지 검사해서 부트로더인지 확인한다.

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

7. 디스크에서 os이미지 로딩  (0) 2019.07.16
6. Bootloader 만들기  (0) 2019.07.11
4. 운영모드와 메모리 관리기법  (0) 2019.07.11
3. 운영모드와 레지스터  (0) 2019.07.11
2. 운영모드  (0) 2019.07.11

+ Recent posts