Post

부트로더 - 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 카테고리의 포스팅을 참고하기 바란다.

참고 문헌

  1. 64Bit 멀티코어 OS의 구조 - 한승훈 저
  2. 한빛출판사 - 64Bit 멀티코어 OS의 구조 첨부 부트로더 코드

업데이트 내역

  1. GRUB2 분석 내용 분리
This post is licensed under CC BY 4.0 by the author.