멀티 태스킹이 가능한 32비트 커널을 만들기 위해서는 CPU가 Protected Mode로 동작하도록 하는 것이 필수
Real Mode의 한계
-
PC가 부팅할 때 맨 처음 동작하는 CPU의 모드
-
16비트로 동작하기 때문에 모든 레지스터를 16비트의 형태로 사용
- 16비트 Real Mode에서는 offset을 16비트 레지스터를 사용
- 세그먼트 영역 안에서 0x0000~0xFFFF 밖으로 나갈 수 없음
-
모든 메모리 영역에 접근 가능하므로 시스템 코드나 타인의 코드를 오염시킬 수 있음
- 세그먼트:오프셋 형태로 주소 지정을 하면 “세그먼트*0x16+오프셋”으로 계산
- 세그먼트의 시작 영역으로 지정하고 싶어도 할 수 없는 경우 발생
- ex) 0x30305는 세그먼트 시작점으로 지정할 방법이 없음
- 지정할 수 있는 메모리 주소가 0xFFFF:FFFF = 0x10FFEF이므로 최대 약 1MB의 메모리 밖에 사용하지 못함
- 0xA0000~0x100000은 비디오 메모리, BIOS가 사용하는 영역이 존재
- 실질적으로 사용할 수 있는 메모리 영역은 640KB
Protected Mode
-
주소 지정을 하기 위해서는 리얼 모드에서의 전환 전에 GDT(Global Descriptor Table)을 준비해야 함
-
Protected Mode로 변환 후 컴퓨터의 전원이 꺼질 때 까지 GDT가 있어야 함
-
Limit 값을 0xFFFFFFFF까지 지정 할 수 있음
- Base Address가 0이면 최대 4GB의 영역 사용 가능
-
DPL 값을 통해 커널 시스템 영역으로 유저 애플리케이션이 접근하지 못하도록 막거나 코드/데이터 세그먼트를 분리시켜 접근하지 못하게 막을 수 있음
-
Protected Mode의 GDT의 각 디스크립터에는 세그먼트 시작 주소를 물리 주소로 지정 가능
- 물리 주소 값으로 직접 넣기 때문에 1byte 단위로 지정 가능
GDT
Base Address: 세그먼트의 시작 주소, 물리 주소로 하위 16비트와 상위 16비트 두 군데로 저장
Limit: 세그먼트의 한계점(크기), 오프셋은 숫자를 넘어갈 수 없음. 20비트로 구성
- G 비트가 0이면 세그먼트의 크기가 바이트 단위, 1이면 4KB 단위
P 비트: 이 세그먼트가 메모리 상에 존재하는 지 나타내는 값. 메모리 관리 루틴이 사용
DPL : 2비트 값. 커널 레벨인지 유저 레벨인지를 나타냄. 0~3의 값으로 4가지 레벨이 있음. 0이면 커널, 3이면 유저
S: 해당 세그먼트가 시스템 세그먼트인지(0), 코드 or 데이터 세그먼트인지(1)를 지정. 항상 1
Type은 4비트로 구성
10진수 | E | W | A | 디스크립터 타입 | 설명 | |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 데이터 | 읽기 전용 |
1 | 0 | 0 | 0 | 1 | 데이터 | 읽기 전용, 액세스 |
2 | 0 | 0 | 1 | 0 | 데이터 | 읽기/쓰기 |
3 | 0 | 0 | 1 | 1 | 데이터 | 읽기/쓰기, 액세스 |
4 | 0 | 1 | 0 | 0 | 데이터 | 읽기 전용, EXPAND DOWN |
5 | 0 | 1 | 0 | 1 | 데이터 | 읽기 전용, EXPAND DOWN, 액세스 |
6 | 0 | 1 | 1 | 0 | 데이터 | 읽기/쓰기, EXPAND DOWN |
7 | 0 | 1 | 1 | 1 | 데이터 | 읽기/쓰기, EXPAND DOWN, 액세스 |
C | R | A | ||||
8 | 1 | 0 | 0 | 0 | 코드 | 실행 전용 |
9 | 1 | 0 | 0 | 1 | 코드 | 실행 전용, 액세스 |
10 | 1 | 0 | 1 | 1 | 코드 | 실행/읽기 |
11 | 1 | 1 | 0 | 0 | 코드 | 실행/읽기, 액세스 |
12 | 1 | 1 | 0 | 1 | 코드 | 실행 전용, CONFORMING |
13 | 1 | 1 | 1 | 0 | 코드 | 실행 전용, CONFORMING, 액세스 |
14 | 1 | 1 | 1 | 0 | 코드 | 실행/읽기 전용, CONFORMING |
15 | 1 | 1 | 1 | 1 | 코드 | 실행/읽기 전용, CONFORMING, 액세스 |
최상위 비트: 코드 세그먼트인지 데이터 세그먼트 인지
마지막 비트: 액세스 비트, 해당 세그먼트에 접근했을 때 CPU가 A 비트를 1로 바꿔 줌(0으로는 안해줌)
D 비트: 0이면 16비트, 1이면 32비트
GDTR
- 48비트 크기
- 처음 16비트 GDT의 크기, 하위 32비트 GDT의 시작 주소가 물리 주소로 들어감
보호 모드에서는 각 세그먼트 레지스터가 셀렉터 레지스터와 디스크립터 레지스터로 나뉨
셀렉터 레지스터
-
상위 13비트 : 디스크립터를 찾기 위한 인덱스
-
2번째 비트 : TI 값
-
0과 1비트 : RPL 값
보호 모드에서 실제로 프로그래머가 다룰 수 있는 것은 세그먼트 셀렉터 뿐
16비트 RealMode -> 32비트 Protected Mode
mov eax, cr0
or eax, 0x00000001
mov cr0, eax
CPU에는 CR0, CR1, CR2, CR3이라는 레지스터들이 존재
CPU의 기능 변경을 위해 프로그래머가 쓰거나, CPU가 현재 상태를 알려주기 위해 사용하거나 디버깅할 때 씀
CR0에 0x00000001을 or 연산해 CR0의 최하위 비트(PE 비트)에 보호 모드라는 것을 표시하는 것
or 연산 이유? 다른 비트가 손상되지 않게 하기 위해서
CPU 명령 읽기, 해석, 실행 유닛은 순차적이 아니라 동시에 진행
보호 모드로 넘어간 후 읽기, 해석 유닛에는 Real Mode 명령어가 존재
jmp $+2
nop
nop
읽기, 해석 유닛에 존재하는 real mode 명령어 삭제
db 0x66 ; prefix : 16bit 명령<=>32bit 명령을 알려주는 표시
db 0x67 ; prefix
db 0xEA ; JMP
dd PM_Start
dw SysCodeSelector
세그먼트 레지스터들이 16비트 값을 포함하고 있으므로 바꿔야 함
[bit 32]
PM_Start:
mov bx, SysDataSelctor
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
mos ss, bx
보호 모드 진입 후 데이터 세그먼트를 각 세그먼트 셀렉터에 넣어 줌
세그먼트 셀렉터 레지스터에 16비트 값이 들어있기 때문