Project/OStrial in AMAZON

#1 부트로더까지 어떻게 만들어봅시다

NONE_31D 2019. 9. 15. 20:39

운영체제 수업을 들으면서 추가 공부를 위해 동기 네명 모아서 OS 개발 진행해보려고 함. 아마 개발이라고 해도 나와있는 코드 짜집기겠지만 기본적인 운영체제, 컴퓨터 구조에 대한 개념만 다시 잡을 수 있어도 좋은 경험일거라 생각했다.

 

*주로 

https://blog.naver.com/simhs93/221256938174 이 포스트 시리즈와

만들면서 배우는 OS 커널의 구조와 원리, 김범준 

이 책을 참고하였습니다.

 


 

1. NASM 설치

 

https://www.nasm.us/pub/nasm/releasebuilds/2.14/win64/

 

Index of /pub/nasm/releasebuilds/2.14/win64

 

www.nasm.us

여기서 깔았음

installer 다운받으면 그냥 설치하는 프로그램 돌아가고 zip 파일 받으면 지정한 위치에 압축 풀면 됨

 

아무데서나 cmd 통해서 nasm 사용하려면 환경변수 설정 필요함

시스템 환경 변수 - Path 에서 nasm 깔린 폴더 넣어주면 됨

 

**cmd 켜놓고 환경변수 수정하면 이미 켜진 cmd에서는 그게 적용이 안됨

당연한거겠지만 저거 모르고 있어서 30분을 엉뚱한 Path 수정에 시간 버렸다 ㅎㅎ


2. 부트스트랩 / 부트로더 / BIOS

 

부트스트랩 : 컴퓨터한테 전원이 들어온 후 무슨 일을 하는지 알려주는 과정.

BIOS : 운영체제의 가장 기본적인 컴퓨터의 입출력 처리하는 소프트웨어. 컴퓨터에 전원이 들어오면 시작되는 프로그램

* 부트로더랑 BIOS가 통합하여 제작되고 부트로더라고 불린다고 함.

 

(이 사이가 POST 가 진행되는 단계)

 

부트로더는 마지막으로 유저가 부팅에 사용할거다! 라고 명시한 MBR(Master Boot Recode, 디스크의 첫 512 바이트)을 읽어 실행함.

 

MBR : BIOS가 POST 단계를 끝내고 바로 실행되는 부분. 부트로더에 의해 MBR이 메모리 0x07C0 으로 불려짐.

왜 0x07C0인지는 IBM 구조 상의 문제

MBR은 부트코드 446바이트, 파티션 테이블 64바이트, 시그니처 (0x55AA로 고정) 2바이트, 총 512바이트로 이루어져 있음.

부트로더에서 MBR을 읽어들이고 마지막 두 바이트를 확인하는데, 이 시그니처가 0x55AA이면 MBR임을 확인, 아니면 오류를 띄우게 된다. 0x55AA가 매직 넘버로 불리는 것 처럼 MBR인지 아닌지를 정하게 되는 시그니처.

 

 

이 글에서 진행할 부분이 저 MBR을 작성하는 파트가 될 예정. 에디터는 vscode 사용합니다. 어차피 NASM으로 컴파일할거라서 에디터 크게 상관 없을겁니다. 확장자도 txt, 없는거, asm 상관 없음.

 


3. 부트코드 작성

 

알아야 할 개념들이 좀 많았는데, 세그먼트 오프셋, 리얼모드와 보호모드 그리고 잡다한 레지스터와 asm 구문들을 익혀야 했다.

 

먼저 코드로 들어가기 전, 리얼모드와 보호모드가 존재한다는 것을 알 수 있었다. 컴퓨터에 전원이 들어온 후, CPU가 돌아가는 모드가 리얼모드이다. 이 모드에서는 프로그램이 한번에 한 개밖에 실행이 되지 않는데, 이때 실행중인 프로그램은 램의 모든 영역에 대한 사용권을 가지게 된다. 이 때문에 MS-DOS로 잘못 프로그램을 실행했다가는 램의 중요 부분에 접근하여 망치는 등의 문제가 생기기 때문에, 거의 사용하지 않게 된 모드이다. 지금은 예전 8086CPU 버전의 DOS프로그램과의 호환성때문에 남아있음. 그래서 처음에는 8086에 프로그램을 올린다는 생각으로 코드를 짜야 한다고 한다.

보호모드는 우리가 아는 OS들이 CPU에서 동작되는 모드인데, 이 때는 리얼 모드와는 다르게 모든 프로그램이 같이 돌아간다. 보호모드는 유저모드와 커널모드로 나뉘어지는데, 유저 모드에서 프로그램이 루틴에 의해 돌면서 램의 영역도 커널모드에 들어갈 때 요청하고, 허락을 받고 사용되는 형식으로 접근하게 된다. 결론은, 지금 짜는 코드는 리얼모드에서 돌아가는 코드라는 것.

 

코드의 맨 첫번째 줄은 [org 0]으로, 이 부트 코드가 메모리의 어느 주소에서 실행해야 하는지 컴파일러에게 알리는 선언문이다. 정확히는 오프셋이 어떻게 되는지 알려주는 구문이다.

일반적으로 컴퓨터는 전원이 들어온 후 POST 단계를 마치면 디스크의 첫 512바이트를 메모리에 읽어들이게 된다. 이때 BIOS는 디스크의 프로그램을 메모리의 0x07C00에 복사하게 된다. 그런데, 아래 코드를 보게 되면 jmp 0x07C0, 즉 다른 주소로 점프하라고 시키는 구문을 볼 수 있다. 0x07C00를 0x07C0으로 표시할 수 있는 부분은 세그먼트와 오프셋을 사용했기 때문이다.

* 0x07C00 : 물리주소, 0x07C0:0000 = 논리 주소. 논리주소에서 좌측은 세그먼트, 우측은 오프셋.

 

16비트 프로그램에서 더 많은 메모리를 사용할 수 있게끔 한게 오프셋의 개념인데, 0x07C00을 0x07C0:0000 과 같은 오프셋을 이용하여 더 넓은 메모리 주소를 사용할 수 있다. 

 

논리 주소에서 물리 주소로 변환하는 법은 다음과 같다.

0x07C0:0000 일 때, 세그먼트에서 *16을 진행하고, 오프셋을 더하면 물리적 주소로 변환할 수 있다. 

즉, 0x07C00 + 0x00000 = 0x07C00 이 되는 것.

 

MBR을 램의 0x7C00에 넣는 이유는 기본적인 IBM 구조 때문인데, 앞부분은 BIOS 및 기타 필수적인 요소가 적재되어 있고 0x500~ 0x9FFFF 까지 프로그램을 자유롭게 넣을 수 있는 공간이 된다. 왜 정확히 7C00 번지인지는 모르겠지만, 우선 이 사이여서 그런 것. (혹시 알게 되면 그때 추가해보겠습니다)

 

[bits 16] : 16비트용 프로그램이라는 것을 알림

 

(start 부분)

 

paint 라벨을 보면, 지정하는 글자를 출력하는 구문이 존재한다.

 

jmp $

 

정말 간단한 이유. 프로그램이 끝날 경우 종료되기 때문에 종료되지 않게 하기 위해 제자리 주소에서 무한루프를 돌게 된다. 이 이후에 있는 구문은 이 상황에서는 읽히지 않는 것이 맞다. 

 

 

 

 

*전체 코드

더보기
[org 0]
[bits 16]
    jmp 0x07C0:start
    start:
        mov ax, cs ;cs : Code Segment
        mov ds, ax

        mov ax, 0xB800
        mov es, ax
        mov di, 0
        mov ax, word [msgBack]
        mov cx, 0x7FF

    
    paint:
        mov word [es:di], ax
        add di, 2
        dec cx
        jnz paint

        mov edi, 0
        mov byte [es:edi], 'S'
        inc edi
        mov byte [es:edi], 0x01
        inc edi
        mov byte [es:edi], 'h'
        inc edi
        mov byte [es:edi], 0x06
        inc edi
        mov byte [es:edi], 'i'
        inc edi
        mov byte [es:edi], 0x01
        inc edi
        mov byte [es:edi], 'n'
        inc edi
        mov byte [es:edi], 0x06
        inc edi
        mov byte [es:edi], 'Y'
        inc edi
        mov byte [es:edi], 0x01
        inc edi
        mov byte [es:edi], 'u'
        inc edi
        mov byte [es:edi], 0x06
        inc edi
        mov byte [es:edi], 'l'
        inc edi
        mov byte [es:edi], 0x01
        inc edi
        mov byte [es:edi], 'i'
        inc edi
        mov byte [es:edi], 0x06
        inc edi
        mov byte [es:edi], 'm'
        inc edi
        mov byte [es:edi], 0x01
        inc edi

        jmp $    

    msgBack db '.', 0xE7

    times 410-($-$$) db 0
    dw 0xAA55

4. 가상머신으로 부팅 확인

 

vmware를 사용, MS-DOS로 가상머신을 만들어두었다. 이후 부팅 디스크를 floppy disk로 추가하였더니 아래와 같이 의도한대로 부팅되는 것을 볼 수 있었음.

 

좌측 상단에 의도한 문자열이 출력되는 것을 확인할 수 있음.