부트로더 - 8
이전 코드에 이어서 계속해서 코드 분석에 들어가도록 하겠다. 부트 로더 파트는 계속해서 포스팅이 업데이트가 될 예정이니 언제 업데이트가 되었는지는 하단에 업데이트 날짜와 내역에 대해서 기재할 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 함수 코드 영역
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 디스크 에러를 처리하는 함수
HANDLEDISKERROR:
push DISKERRORMESSAGE ; 에러 문자열의 어드레스를 스택에 삽입
push 1 ; 화면 Y 좌표(1)를 스택에 삽입
push 20 ; 화면 X 좌표(20)를 스택에 삽입
call PRINTMESSAGE ; PRINTMESSAGE 함수 호출
jmp $ ; 현재 위치에서 무한 루프 수행
디스크 에러가 났을때 메세지 출력하는 함수로 화면에 표기해주는 함수다 실제로 디스크 에러가 났을때 체크하는 부분은 해당 함수를 호출하기 전에 이루어지고 이 함수는 그냥 DISKERRORMESSAGE 라벨에 연결된 DISK Error~!!를 화면에 표기하는데만 쓰인다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
; 메시지를 출력하는 함수
; PARAM: x 좌표, y 좌표, 문자열
PRINTMESSAGE:
push bp ; 베이스 포인터 레지스터(BP)를 스택에 삽입
mov bp, sp ; 베이스 포인터 레지스터(BP)에 스택 포인터 레지스터(SP)의 값을 설정
; 베이스 포인터 레지스터(BP)를 이용해서 파라미터에 접근할 목적
push es ; ES 세그먼트 레지스터부터 DX 레지스터까지 스택에 삽입
push si ; 함수에서 임시로 사용하는 레지스터로 함수의 마지막 부분에서
push di ; 스택에 삽입된 값을 꺼내 원래 값으로 복원
push ax
push cx
push dx
; ES 세그먼트 레지스터에 비디오 모드 어드레스 설정
mov ax, 0xB800 ; 비디오 메모리 시작 어드레스(0x0B8000)를
; 세그먼트 레지스터 값으로 변환
mov es, ax ; ES 세그먼트 레지스터에 설정
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; X, Y의 좌표로 비디오 메모리의 어드레스를 계산함
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Y 좌표를 이용해서 먼저 라인 어드레스를 구함
mov ax, word [ bp + 6 ] ; 파라미터 2(화면 좌표 Y)를 AX 레지스터에 설정
mov si, 160 ; 한 라인의 바이트 수(2 * 80 컬럼)를 SI 레지스터에 설정
mul si ; AX 레지스터와 SI 레지스터를 곱하여 화면 Y 어드레스 계산
mov di, ax ; 계산된 화면 Y 어드레스를 DI 레지스터에 설정
; X 좌료를 이용해서 2를 곱한 후 최종 어드레스를 구함
mov ax, word [ bp + 4 ] ; 파라미터 1(화면 좌표 X)를 AX 레지스터에 설정
mov si, 2 ; 한 문자를 나타내는 바이트 수(2)를 SI 레지스터에 설정
mul si ; AX 레지스터와 SI 레지스터를 곱하여 화면 X 어드레스를 계산
add di, ax ; 화면 Y 어드레스와 계산된 X 어드레스를 더해서
; 실제 비디오 메모리 어드레스를 계산
; 출력할 문자열의 어드레스
mov si, word [ bp + 8 ] ; 파라미터 3(출력할 문자열의 어드레스)
.MESSAGELOOP: ; 메시지를 출력하는 루프
mov cl, byte [ si ] ; SI 레지스터가 가리키는 문자열 위치에서 한 문자를
; CL 레지스터에 복사
; CL 레지스터는 CX 레지스터의 하위 1바이트를 의미
; 문자열은 1바이트면 충분하므로 CX 레지스터의 하위 1바이트만 사용
cmp cl, 0 ; 복사된 문자와 0을 비교
je .MESSAGEEND ; 복사한 문자의 값이 0이면 문자열이 종료되었음을
; 의미하므로 .MESSAGEEND로 이동하여 문자 출력 종료
mov byte [ es: di ], cl ; 0이 아니라면 비디오 메모리 어드레스 0xB800:di에 문자를 출력
add si, 1 ; SI 레지스터에 1을 더하여 다음 문자열로 이동
add di, 2 ; DI 레지스터에 2를 더하여 비디오 메모리의 다음 문자 위치로 이동
; 비디오 메모리는 (문자, 속성)의 쌍으로 구성되므로 문자만 출력하려면
; 2를 더해야 함
jmp .MESSAGELOOP ; 메시지 출력 루프로 이동하여 다음 문자를 출력
.MESSAGEEND:
pop dx ; 함수에서 사용이 끝난 DX 레지스터부터 ES 레지스터까지를 스택에
pop cx ; 삽입된 값을 이용해서 복원
pop ax ; 스택은 가장 마지막에 들어간 데이터가 가장 먼저 나오는
pop di ; 자료구조(Last-In, First-Out)이므로 삽입(push)의 역순으로
pop si ; 제거(pop) 해야 함
pop es
pop bp ; 베이스 포인터 레지스터(BP) 복원
ret ; 함수를 호출한 다음 코드의 위치로 복귀
화면에 표기하는 코드에 대한 부분으로 사실 이 부분은 이전에 부트로더-3 포스팅에서 다룬바 있다. 이 부분을 스택을 이용하여 함수화하면 다음과 같은 코드로 된다. 여기까지는 코드에 대한 내용이었고 그 다음 코드는 실질적으로 사용하는 데이터에 대한 영역이다.
1
2
3
4
5
6
7
8
9
10
11
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 데이터 영역
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 부트 로더 시작 메시지
MESSAGE1: db 'MINT64 OS Boot Loader Start~!!', 0 ; 출력할 메시지 정의
; 마지막은 0으로 설정하여 .MESSAGELOOP에서
; 문자열이 종료되었음을 알 수 있도록 함
DISKERRORMESSAGE: db 'DISK Error~!!', 0
IMAGELOADINGMESSAGE: db 'OS Image Loading...', 0
LOADINGCOMPLETEMESSAGE: db 'Complete~!!', 0
해당 레이블에는 string으로 값이 들어간 주소가 지정되어있으며 끝에 0을 넣어서 해당 문자열이 끝났음을 체크한다.
1
2
3
4
; 디스크 읽기에 관련된 변수들
SECTORNUMBER: db 0x02 ; OS 이미지가 시작하는 섹터 번호를 저장하는 영역
HEADNUMBER: db 0x00 ; OS 이미지가 시작하는 헤드 번호를 저장하는 영역
TRACKNUMBER: db 0x00 ; OS 이미지가 시작하는 트랙 번호를 저장하는 영역
여기까지 실사용 데이터 영역이고 아래 쪽은 Data align을 맞춰주는 부분과 signature 부분이다
1
2
3
4
5
6
7
times 510 - ( $ - $$ ) db 0x00 ; $ : 현재 라인의 어드레스
; $$ : 현재 섹션(.text)의 시작 어드레스
; $ - $$ : 현재 섹션을 기준으로 하는 오프셋
; 510 - ( $ - $$ ) : 현재부터 어드레스 510까지
; db 0x00 : 1바이트를 선언하고 값은 0x00
; time : 반복 수행
; 현재 위치에서 어드레스 510까지 0x00으로 채움
초기에 부트디스크에서 1sector 크기(512Bytes)만큼 갖고와서 구동하기 때문에 이 512Bytes만큼 데이터 크기를 맞춰주기 위해서 0x00을 510까지 채워넣어준다
1
2
3
db 0x55 ; 1바이트를 선언하고 값은 0x55
db 0xAA ; 1바이트를 선언하고 값은 0xAA
; 어드레스 511, 512에 0x55, 0xAA를 써서 부트 섹터로 표기함
512Bytes 끝 2바이트에는 0x55 0xAA가 들어가야 부트 섹터로 인식이 되기 때문에 직접 데이터를 넣어준다.
이렇게 책에 나와있는 부트로더는 설명이 다 끝났지만 실제 사용되는 사용 OS에서는 어떻게 되는지 궁금해졌다 이러한 분석에 대해서 관심이 있다면 GRUB2 카테고리의 포스팅을 참고하기 바란다.
참고 문헌
- 64Bit 멀티코어 OS의 구조 - 한승훈 저
- 한빛출판사 - 64Bit 멀티코어 OS의 구조 첨부 부트로더 코드
업데이트 내역
- GRUB2 분석 내용 분리