5. 16비트 Real Mode에서 32비트 Protected Mode

 

(1) CR0 레지스터 설정

GDT를 준비하였으면, CPU에게 이제부터 Protected Mode로 넘어간다는 것을 알려야 합니다.

이런 역할을 하는 것이 CR0의 마지막 비트입니다.

다음 그림은 CR0레지스터 입니다.

CR0 레지스터는

31 bit (PG:Paging) – 만약 이 비트가 1일 때, 페이징과 CR3 레지스터를 활성화 하고 0일 때 페이징을 비활성합니다.

30 bit (CD : Cache disable) – 전역적으로 메모리 캐시를 활성화하거나 비활성화 합니다.

29 bit(NW:Not-write through) – 전역적으로 캐시를 통해 쓰는 것을 활성화 하거나 비활성화 합니다.

18 bit(AM:Alignment mask) – AM이 세트 되어 있으면 alignment 체크를 활성화하고, AC 플래그(EFLAGS에 있습니다.)가 세트 되어 있으면 권한이 level3 임을 의미합니다.

16 bit(WP:Write protect) – CPU가 페이지에 쓸 수 있는지 결정하여, 권한 레벨이 0일 때 read-only로 표시합니다

5 bit(NE:Numeric error) : 세트되어 있을 때 내부 x87 부동소수점 에러 리포팅을 활성화합니다. 0일 때, 내부 PC x87 에러를 검사합니다.

4 bit(ET:Extension type) : 386에서 , 외부 수리 보조처리기가 80287 인지 80387인지 구별합니다.

3 bit(TS:Task Switced): x87 명령이 사용된 이후에만 태스크 스위치 시에 x87 context를 저장합니다.

2 bit(EM:Emulation) : 세트되어 있을 때, x87 부동소수점 유닛이 존재하지 않고, 클리어 되어 있을 때, x87 FPU가 존재합니다.

1bit(MP:Monitor coprocessor) : WAIT/FWAIT 명령의 작용을 TS 플래그와 함께 제어합니다.

0bit(PE:Protected Mode Enable):세트 되었을 때 시스템은 protected mode 이며, 해제되었을때, real mode 입니다.

 

0비트를 set 해주면 Protected Mode가 활성화 됩니다.

 

mov eax, cr0

or eax, 0x00000001

mov cr0, eax

 

위 코드는 GDTR에 gdt를 등록하고 난 뒤에, CPU에게 이제부터 Protected Mode로 바꾸는 것을 알리기 위해

CR0의 최 하위 비트를 세트 해주는 코드입니다.

CR0의 다른 비트에 영향을 끼치지 않게 하기 위해여 OR 연산을 하였습니다.

 

(2) CPU 파이프라인 유닛 비우기

Protected Mode로 변경되었으나 CPU에는 파이프라이닝으로 인해 16비트 명령어 코드가 남아있을 수 있습니다. 따라서 jmp 명령어를 이용하여 CPU의 16비트 코드를 깨끗하게 지웁니다.

jmp $+2

nop

nop

 

 

(3) 세그먼트 레지스터 값 변경

세그먼트 레지스터가 16비트의 값을 포함하지 않도록 바꿔주어야 Protected Mode로 넘어갈 수 있습니다.

Db 0x66

Db 0x67

Db 0xEA

Dd PM_Start

Dw SysCodeSelector

 

Far JMP를 하기 위해서 CS레지스터에 새로운 세그먼트 값이 들어갑니다. CS에는 SysCodeSelector가, EIP 레지스터에는 PM_Start가 들어갑니다.

0x66 은 16비트에서 오퍼랜드를 32비트로 쓰겠다는 의미이고, 0x67은 16비트 모드에서 주소 값을 32비트로 쓰겠다는 의미입니다. 이 prefix를 붙이는 이유는 아직 명령어가 16비트 부분이기 때문입니다. 따라서 이를 어셈블리어로 옮기면

jmp DWORD SysCodeSelector:PM_Start

가 됩니다.

 

부트로더에서 Kernel로 진입하기 위해 데이터 세그먼트들을 CS와 같이 0x1000으로 할당하였습니다.

(커널을 0x10000 위치에 로드 했습니다.)

아직 그 값이 남아 있으므로 그 다음 세그먼트 레지스터를 Protected Mode에서 사용할 수 있도록, 다음과 같이 세그먼트 레지스터(셀렉터)에 데이터 세그먼트를 넣어 초기화 합니다.

 

[bits 32]

 

PM_Start:

mov bx, SysDataSelector

mov ds, bx

mov es, bx

mov fs, bx

mov gs, bx

mov ss, bx

 

+ Recent posts