이전 포스팅에서는 환경설정에 대해서 다루었다면 지금 포스팅부터는 OS를 직접 만드는 것에 초첨을 마추고 글을 작성하도록 하겠다. 가장 처음 컴퓨터가 부팅되고 난 뒤에 POST(Power On Self Test)과정을 거쳐 특정 OS 파일에서 첫 512바이트트만큼을 가져온다. 여기서 가장 먼저 실행되는 코드라고 하는데 이를 만들고 알아보도록 하자.
부트과정
처음 컴퓨터에 전원이 들어와서 부팅과정을 거치는데 이를 부트스트랩이라고 한다. 부트스트랩은 장화(boot)의 끈(strap)을 묶고 일어나려는 사람을 가르키기도 하며 그 과정은 아래와 같다. 이 포스팅에서 직접 수정하고 테스트 할 부분은 Step 3 이후에 로드된 MBR(Master Boot Record)이다. 3.20 전산대란이 일어났을 때나 특정 악성코드들이 MBR을 파괴하여 PC를 부팅하지 못하게 한다고 하는데 그때 MBR이 이 MBR이다.
Step 1. 컴퓨터에 전원이 구동되면서 BIOS가 구동된 뒤 POST
(Power On Self Test, 메모리와 하드디스크 및 하드웨어 점검) 단계
Step 2. CMOS 설정 정보를 읽어 부팅에 필요한 정보 획득(부팅 순서, 등등)
Step 3. 하드 디스크의 첫번째 섹터에 있는 Mast Boot Record를 검사한 뒤 로드
Step 4. 부트로더가 실행되서 커널을 메모리로 로딩하는 단계(lilo 또는 GRUB)
Step 5. 커널이 로드된 이후에는 제어권이 OS로 넘어가게 된다.
출처 : http://regularmotion.kr/boot-strap-x86의-부팅-과정
정리하자면 BIOS가 POST(Power-on self-test)단계를 거치고 MBR을 읽어 메모리에 로드 했을 때 그 부분을 수정한다고 보면 되겠다. 이를 통해 특정 메모리부분을 읽고 화면에 문자를 출력하고 헤드, 섹터, 실린더를 지정하여 특정 디스크를 읽는다.
부트코드
부트코드는 처음 BIOS가 디스크의 MBR을 읽고 난뒤에 가장 먼저 실행되는 부분이다. POST과정을 마친 후 부트로더에 의해 MBR의 512바이트가 메모리 0x07C0로 불려진다. 그래서 가장 처음에 해야 할 일이 메모리 0x07C0부분으로 이동하는 것이다.
▲ MBR 구조 - 참고(forensic-proof)
이전 포스팅[OS 만들기 - [1]첫 실행]에서 작성했던 소스는 단순히 화면에 문자를 출력하는 부트코드이다. 그래서 메모리 0x07c0에서 작성된 코드를 복사하고 0xB800의 컬러 텍스트 모드 비디오 메모리에 내가 쓰려는 문자인 "kcats"와 글자색/배경색을 지정해주는 작업을 하는 것이 이 포스팅의 부트코드이다.
가상 OS를 만드는 것이 주 목적이므로 파티션을 나눠 디스크를 관리하는 부분은 본 목적에 벗어난다. 그러므로 이 포스팅에서는 파티션테이블 고려는 안하도록 하겠다.
부트코드 소스
부트코드 소스를 보도록 하겠다. 이 소스는 notepad++환경에서 작성하였으며 NASM으로 컴파일했다.
차근차근히 한줄 한줄 설명하도록 하겠다.
▲ 부트코드 소스[notepad++]
[org 0]
처음 소스코드에서 offset을 말한다. 만약 org 0을 org 10으로 바꾸면 jmp 0x5(상대 오프셋값)가 jmp 0xF로 바뀐다고 알면 되겠다.
[bits 16]
이 코드가 16비트로 이루어져 있다는 것을 컴파일러인 NASM에게 가르쳐주는 것이다. 16bit는 이전 MS-DOS시절에서 사용했는데, 16bit를 real-mode라고 부르고 현재 우리가 사용하는 32bit를 protected-mode라고 부른다. real-mode는 하나의 프로그램만 실행되고, 모든 프로세스가 자기가 원하는 메모리영역에 접근가능하기 때문에 다른 프로그램의 데이터부분에 접근가능하다. 호환성때문에 지금까지 real-mode사용하며 부트코드에서는 protected-mode로 가기전의 여러 값들을 설정하고 넘어가는데 그때까지의 역할을 real-mode가 한다. 추후 포스팅에서 protected-mode에 대해서 설명할 텐데 그때 자세히 설명하도록 하겠다.
[ jmp 0x07c0: start]
아까부터 계속 말했던 0x07C0부분이 나왔다. 내가 이 코드를 디스크에 써두면 메모리 0x07c0부분에 올라 갈 것이므로 현재 프로그램 카운터(PC)를 0x07C0으로 옮겨야한다. 그리고 우리가 일반적으로 C에서 함수를 사용하듯 특정부분을 만들어두고 그 부분으로 jmp를 이용하여 PC를 옮기는 것을 알 수 있다. 이전의 [org 0]이나 [bits 16]과 같이 컴파일러인 NASM에게 start라는 부분으로 이동할 것이라는 것을 알려준다. 이것은 컴파일러만 아는 것이며 이 소스의 결과물인 이미지에는 start라는 부분이 아니라 start의 주소가 기록된다.
[ mov ax, 0xb800
mov es, ax ]
ax에 0xb800을 넣고 난뒤에 es(extended segment)에 ax에 저장된 0xb800을 넣는 것을 의미한다. 그러면 0xb800을 es에 넣는 것을 어떤 의미를 지닐까? 이는 아까전에 잠시 언급을 하였는데 컬러 텍스트 모드 비디오 메모리라는 부분에 접근하는 것을 의미한다. 그래서 비디오 메모리부분에 접근하여서 문자, 문자색/배경색을 지정할 수 있다.
▲시스템 하위 메모리 맵
(참조 - 만들면서 배우는 OS 커널의 구조와 원리)
[ mov byte[es:0], 'k'
mov byte[es:1], 0xFD ]
이 부분은 비디오 메모리에 쓸 문자와 문자색, 배경색을 지정하는 부분이다. 처음 들어가는 부분이 쓰려는 문자이고 그다음 1바이트에서 상위 4비트인 'F'가 배경색, 하위 4비트는 'D'가 문자색이다. 이들의 색표는 아래와 같다.
▲ 16비트 색 표
참고 - http://www.shikadi.net/
[ jmp $]
$는 현재위치를 뜻한다. 그래서 현재 위치로 점프하면 다시 jmp $코드를 만나게 되고 또 점프를 할 것이다. 이 말은 현재 위치에서 계속 실행되고 있도록 하여 다른 작업을 하지 않겟다는 의미로 보면 되겠다.
[ time 510 - ($ - $$) ]
$$는 처음 소스 위치이다. 그래서 $-$$는 현재위치 - 처음위치이므로 지금까지의 소스크기를 의미한다. 그 소스크기를 510에 빼어서 남은 부분을 계산하여 그만큼 0을 반복적으로 채워주는 것을 말한다. 아까 처음 MBR의 크기가 512바이트라고 했는데 512바이트에서 마지막 시그니처인 (0x55AA) 2바이트를 빼면 510이 된다. 그래서 시그니처와 소스코드를 제외한 부분의 크기만큼 0으로 반복하는 것을 의미한다.
▲ 510 - ($ - $$)의 의미
[dw 0xAA55]
MBR의 마지막을 알리는 시그니처인 55AA가 들어간다. 값이 반대로인 것처럼 보이는데 오른쪽부터 낮은주소가 시작되는 것이므로 낮은수 55가 먼저 저장되고 그 다음에 AA가 메모리에 저장된다.
마치면서
정리하자면, 컴퓨터에 전원이 들어오면 BIOS가 POST과정을 거쳐 부트스랩단계에 들어간다. 특정 운영체제 소스가 들어가 있는 이미지에서 처음 512바이트를 가져와서 메모리에 올리는데 이때 이 512바이트를 MBR이라고 부르고 MBR이 저장될 메모리의 주소는 0x07c0이다. 그리고 컬러문자를 출력하기 위해 0xB800:0000에 원하는 색상과 배경을 가진 문자를 썼다.
마지막으로 위 소스로 만들어진 이미지(.img)를 헥스 에디터로 확인한 것을 첨부하여 이 포스팅을 끝내도록 하겠다.
출처: https://kcats.tistory.com/152?category=554568 [kcats's mindstory]