2부. 64비트 세상으로 점프
6장. 32비트 보호 모드로 전환하자
세그먼트 디스크립터
- 세그먼트 디스크립터 (Segment Descriptor) : 세그먼테이션 기법에서 세드먼트의 정보를 나타내는 구조
- 세그먼트 (Segment) : 메모리 공간을 임의의 크기로 나눈 영역
세그먼트 디스크립터 종류
- 코드 세그먼트 디스크립터
- 실행 가능한 코드가 포함된 세그먼트에 대한 정보
- CS 세그먼트 셀렉터 사용
- 데이터 세그먼트 디스크립터
- 데이터가 포함된 세그먼트에 대한 정보
- CS 세그먼트 셀렉터를 제외한 나머지 셀렉터 사용
- 코드 세그먼트 디스크립터
코드 세그먼트 디스크립터와 데이터 세그먼트 디스크립터 타입 설정
- 비트 8
- 접근 여부 비트
- 프로세서에서 설정하는 비트로 프로세서는 해당 디스크립터가 참조될 때마다 비트 8을 1로 설정
- 비트 10
- 역방향 확장 비트 (데이터 세그먼트 타입인 경우)
- 상위 어드레스에서 하위 어드레스로 자라는 스택을 위한 옵션
- 세그먼트의 크기를 동적으로 확장 / 축소할 목적으로 사용
- 접근 승인 비트 (코드 세그먼트 타입인 경우)
- 권한에 관계없이 해당 코드 세그먼트에 접근할 수 있음을 나타냄
- 역방향 확장 비트 (데이터 세그먼트 타입인 경우)
세그먼트 디스크립터의 구조와 메모리 공간, 어셈블리어 코드의 관계
GDT
GDT
- Global Descriptor Table
- 연속된 디스크립터의 집합
- 널 티스크립터(NULL Descriptor)를 가장 앞부분에 추가해야 함
LDT
- Local Descriptor Table
- GDT와 비슷한 역할을 하는 테이블
- GDT 크기 부족 문제 해결
보호 모드로 전환
- GDTR 레지스터 설정
- CR0 컨트롤 레지스터 설정
- jmp 명령 수행
CR0 컨트롤 레지스터
- 외부 메모리의 내용과 캐시의 내용을 일치시키는 동기화 정책
- Write-through 방식
- 메모리에 쓰기를 수행할 때마다 캐시의 내용과 외부 메모리의 내용을 모두 갱신
- Write-back 방식
- 쓴 내용을 캐시에만 갱신하고 외부 메모리에 쓰는 작업을 최대한 뒤로 미룸
- 캐시의 내용을 버릴 때 캐시의 내용을 외부 메모리에 갱신
- 속도 면에서 Write-back 방식이 유리하지만 메모리 맵 I/O 방식(메모리 어드레스에 읽고 쓰는 데이터를 디바이스로 송수신하는 방식)을 사용하는 디바이스에는 부적절함(외부 메모리에 업데이트가 바로 되지 않아 문제 발생)
- Write-through 방식
보호 모드 커널의 엔트리 포인트 소스 코드(EntryPoint.s)
[ORG 0x00]
[BITS 16]
SECTION .text
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 코드 영역
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
START:
mov ax, 0x1000 ; 보호 모드 엔트리 포인트의 시작 어드레스(0x10000)를 세그먼트 레지스터 값으로 변환
mov ds, ax
mov es, ax
cli ; 인터럽트가 발생하지 못하도록 설정
lgdt [GDTR] ; GDTR 자료구조를 프로세서에 설정하여 GDT 테이블을 로드
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 보호 모드로 진입
; Disable Paging, Disable Cache, Internel FPU, Disable Align Check, Enable ProtectedMode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov eax, 0x4000003B ; PG=0, CD=1, NW=0, AM=0, WP=0, NE=1, ET=1, TS=1, EM=0, MP=1, PE=1
mov cr0, eax ; CR0 컨트롤 레지스터에 위에서 저장한 플래그를 설정하여 보호 모드로 전환
; 커널 코드 세그먼트를 0x00을 기준으로 하는 것으로 교체하고 EIP의 값을 0x00을 기준으로 재설정
; CS 세그먼트 셀렉터 : EIP
jmp dword 0x08:(PROTECTEDMODE - $$ + 0x10000)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 보호 모드로 진입
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 32]
PROTECTEDMODE:
mov ax, 0x10 ; 보호 모드 커널용 데이터 세그먼트 디스크립터를 AX 레지스터에 저장
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; 스택을 0x00000000 ~ 0x0000FFFF 영역에 64KB 크기로 생성
mov ss, ax
mov esp, 0xFFFE
mov ebp, 0xFFFE
; 화면에 보호 모드로 전환되었다는 메시지를 찍는다.
push (SWITCHSUCCESSMESSAGE - $$ + 0x10000)
push 2 ; 화면 Y 좌표(2)
push 0 ; 화면 X 좌표(0)
call PRINTMESSAGE
add esp, 12
jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 함수 코드 영역
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 메시지를 출력하는 함수
; 스택에 x 좌표, y 좌표, 문자열
PRINTMESSAGE:
push ebp
mov ebp, esp
push esi
push edi
push eax
push ecx
push edx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; X, Y의 좌표로 비디오 메모리의 어드레스를 계산함
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Y 좌표
mov eax, dword [ebp + 12]
mov esi, 160
mul esi
mov edi, eax
; X 좌표
mov eax, dword [ebp + 8]
mov esi, 2
mul esi
add edi, eax
; 출력할 문자열의 어드레스
mov esi, dword [ebp + 16]
.MESSAGELOOP:
mov cl, byte [esi]
cmp cl, 0
je .MESSAGEEND
mov byte [edi + 0xB8000], cl
add esi, 1
add edi, 2
jmp .MESSAGELOOP
.MESSAGEEND:
pop edx
pop ecx
pop eax
pop edi
pop esi
pop ebp
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 데이터 영역
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 아래의 데이터를 8바이트에 맞춰 정렬하기 위해 추가
align 8, db 0
; GDTR의 끝을 8byte로 정렬하기 위해 추가
dw 0x0000
; GDTR 자료구조 정의
GDTR:
dw GDTEND - GDT - 1 ; GDT 테이블의 전체크기
dd (GDT - $$ + 0x10000) ; GDT 테이블의 시작 어드레스
; GDT 테이블 정의
GDT:
; 널 디스크립터, 반드시 0으로 초기화해야 함
NULLDESCRIPTOR:
dw 0x0000
dw 0x0000
db 0x00
db 0x00
db 0x00
db 0x00
; 보호 모드 커널용 코드 세그먼트 디스크립터
CODEDESCRIPTOR:
dw 0xFFFF ; Limit[15:00]
dw 0x0000 ; Base[15:00]
db 0x00 ; Base[23:16]
db 0x9A ; P=1, DPL=0, Code Segment, Execute/Read
db 0xCF ; G=1, D=1, L=0, Limit[19:16]
db 0x00 ; Base[31:24]
; 보호 모드 커널용 데이터 세그먼트 디스크립터
DATAEDESCRIPTOR:
dw 0xFFFF ; Limit[15:00]
dw 0x0000 ; Base[15:00]
db 0x00 ; Base[23:16]
db 0x92 ; P=1, DPL=0, Data Segment, Read/Write
db 0xCF ; G=1, D=1, L=0, Limit[19:16]
db 0x00 ; Base[31:24]
GDTEND:
SWITCHSUCCESSMESSAGE: db 'Switch To Protected Mode Success~!!', 0
times 512 - ($ - $$) db 0x00
01.Kernel32 디렉터리의 makefile
all: Kernel32.bin
Kernel32.bin: Source/EntryPoint.s
nasm -o Kernel32.bin $<
clean:
rm -f Kernel32.bin
- $< : Dependency(:의 오른쪽)의 첫 번째 파일을 의미하는 메크로
Wonix 디렉터리의 makefile 수정 부분
Disk.img: 00.BootLoader/BootLoader.bin 01.Kernel32/Kernel32.bin
@echo
@echo ============== Disk Image Build Start ==============
@echo
cat $^ > Disk.img
@echo
@echo ============== All Build Complete ==============
@echo
- $^ : Dependency(:의 오른쪽)에 나열된 전체 파일을 의미하는 메크로
보호 모드 전환된 결과
보호 모드 커널 이미지의 크기가 512바이트(1섹터)이므로 부트로더 BootLoader.asm의 TOTALSECTORCOUNT를 다음과 같이 수정해야 한다.
TOTALSECTORCOUNT: dw 1