1. 16비트 Real Mode
Real Mode는 PC를 부팅할 때 맨 처음 동작하는 CPU모드로, 16비트 명령어로 동작하며 16비트 형식의 레지스터를 사용합니다.
Real Mode의 특징
(1) 범용 레지스터
16비트 모드에서의 레지스터들은 16비트, WORD로 되어 있고 0에서 0xFFFF까지 저장할 수 있습니다.
(2) 주소지정
주소지정은 세그먼트 레지스터와 오프셋 레지스터를 사용하여 할 수 있습니다.
이 두 레지스터는 16비트 모드에서 각각 16비트 값만 취할 수 있습니다.
만약 물리 번지 0x30004F로 점프(예 – jmp)를 하려고 시도를 한다면 IP 레지스터 또한 16비트의 값만 취급 하므로 점프를 할 수 없습니다.
그러므로 IP 레지스터(오프셋으로 사용)가 현재 지정한 세그먼트 영역 안에서 0x0000 ~ 0xFFFF의 범위 밖으로 나갈 수 없어 점프가 불가능 한 경우,
Far jmp를 사용하여 세그먼트와 오프셋을 함께 바꾸어야 합니다.
Jmp operation
Opcode
|
Mnemonic
|
Description
|
EB cb
|
JMP rel8
|
Jump short, relative, displacement
relative to next instruction.
|
E9 cw
|
JMP rel16
|
Jump near, relative, displacement
relative to next instruction.
|
E9 cd
|
JMP rel32
|
Jump near, relative, displacement
relative to next instruction.
|
FF /4
|
JMP r/m16
|
Jump near, absolute indirect,
address given in r/m16.
|
FF /4
|
JMP r/m32
|
Jump near, absolute indirect,
address given in r/m32.
|
EA cd
|
JMP ptr16:16
|
Jump far, absolute, address given
in operand.
|
EA cp
|
JMP ptr16:32
|
Jump far, absolute, address given
in operand.
|
FF /5
|
JMP m16:16
|
Jump far, absolute indirect,
address given in m16:16.
|
FF /5
|
JMP m16:32
|
Jump far, absolute indirect,
address given in m16:32.
|
X86 어셈블리언어에서 jmp 명령은 무조건 분기를 수행합니다. 내부적으로는 IP 레지스터를 변경하는 것으로 수행됩니다. CPU가 real mode인지, protected 모드인지에 따라, 명령의 오퍼랜드가 16비트인지, 32비트인지, 혹은 세그먼트:오프셋 형식을 가지는지, 오버라이드 명령어가 사용되는지에 따라서 각각 다른 opcode가 맵핑 되어 있습니다. 점프에는 근접 점프, 조건 점프, 레지스터의 간접 점프, 즉치값을 이용한 점프 등이 있습니다.
1. 16비트 포인터를 사용한 간접 점프
2. 세그먼트 안에서 32비트 포인터를 이용한 long jmp
3. eax 레지스터를 이용한
register-indirect absolute jump // 프로텍트 모드에서만 가능
|
(3) 세그먼트
Real mode에서의 세그먼트 접근의 위험성
Real 모드에서는 세그먼트 내에서는 어떤 메모리 영역에도 접근이 가능하여 ,
다른 프로그램의 실행코드 등을 임의로 변경시키는 등의 위험한 상황이 발생할 수 있습니다.
세그먼트 지정의 제약
Real 모드에서 세그먼트:오프셋 형태로 주소를 지정하게 되면 CPU는 세그먼트 값에 16을 곱한 뒤 오프셋과 더하는 연산을 하여 주소를 구하게 됩니다. 따라서 16비트 모드에서는 세그먼트는 항상 16비트 단위로 지정이 되게 됩니다.
(4) 물리주소
Real Mode에서 최대한 지정할 수 있는 메모리 주소는 0xFFFF:0xFFFF = 0x10FFEF 입니다.
따라서 아무리 램의 용량이 많다고 하여도 접근할 수 있는 주소 영역은 0x000000 에서 0x10FFEF 입니다.
거기에 0xA0000 ~ 0x100000까지는 그래픽 모드 비디오 메모리, 텍스트 모드 비디오 메모리, BIOS 등이 사용하는 영역 등이 정해져 있어 실제로 사용자가 프로그램을 저장할 수 있는 공간은 더 적습니다. 실제로 사용할 수 있는 영역은0x000~0xA0000, 640KB밖에 사용할 수 없습니다.
2. Protected Mode
Protected Mode의 특징
(1) 최대 4 GB의 영역을 지정할 수 있음
(2) 커널 시스템 영역과 유저 영역으로 나누어, 커널 시스템 영역을 유저 애플리케이션으로부터 보호하거나 코드/데이터 세그먼트를 분리시켜 코드를 일거나 쓰지 못하도록 막을수 있음
(3) 각 세그먼트의 시작 주소를 물리주소로 지정 할 수 있고, 1byte 단위로 지정할 수 있음
3. GDT
15
|
14
|
13
|
12
|
11
|
10
|
9
|
8
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
Lmit의 15 - 0비트
|
Base Address의 15 - 0비트
|
P
|
DPL
|
S
|
Type
|
Base Address의 23 – 16 비트
|
Base Address의 31 – 24 비트
|
G
|
D
|
0
|
AVL
|
Lmit의 19 - 16비트
|
63
|
62
|
61
|
60
|
59
|
58
|
57
|
56
|
55
|
54
|
53
|
52
|
51
|
50
|
49
|
48
|
(1) Base Address
이 세그먼트의 시작 주소로, 하위 0 – 15 비트와 상위 16 – 19 비트로 나눠서 저장합니다.
(2) Lmit
이 세그먼트의 한계점(크기)를 나타냅니다. 오프셋은 이 숫자를 넘어갈 수 없고 넘어가게 되면 GP fault(Protected Mode 규약 위반)이 발생합니다.
Base Address처럼 나누어서 하위 0 – 15 비트와 ,16 – 23 비트, 상위 24 - 31비트로 나누어서 저장합니다.
G 비트가 0일 경우 Limit 값의 단위는 그대로 이고, 1일 경우 0xFFF을 곱하여 한계점으로 정합니다.
(3) P bit
이 세그먼트가 메모리상에 존재하는지를 나타내는 값으로, 커널 프로그램의 메모리 관리 루틴에 사용됩니다. 페이지와 관련된 비트입니다.
(4) DPL
이 세그먼트가 커널 레벨인지 유저 레벨인지 나타냅니다.
인텔 x86 계열 CPU에는 4가지 레벨의 권한이 있으며, 0이면 커널 레벨이고 3이면 유저 레벨입니다.
(5) S bit
시스템 세그먼트(0)인지 코드 혹은 데이터 세그먼트(1)인지를 결정합니다. 항상 1로 지정합니다.
(6) Type
Type Field
|
디스크립터
타입
|
설명
|
10진수
|
11
|
10
|
9
|
8
|
|
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 DWON
|
7
|
0
|
1
|
1
|
1
|
데이터
|
읽기/쓰기, EXPAND DOWN, 액세스
|
|
|
C
|
R
|
A
|
|
|
8
|
1
|
0
|
0
|
0
|
코드
|
실행 전용
|
9
|
1
|
0
|
0
|
1
|
코드
|
실행 전용, 액세스
|
10
|
1
|
0
|
1
|
0
|
코드
|
실행/읽기
|
11
|
1
|
0
|
1
|
1
|
코드
|
실행/읽기, 액세스
|
12
|
1
|
1
|
0
|
0
|
코드
|
실행 전용, CONFORMING
|
13
|
1
|
1
|
0
|
1
|
코드
|
실행 전용, CONFORMING, 액세스
|
14
|
1
|
1
|
1
|
0
|
코드
|
실행/읽기 전용, CONFORMING
|
15
|
1
|
1
|
1
|
1
|
코드
|
실행/읽기 전용, CONFORMING, 액세스
|
- 최상위 비트 : 코드 세그먼트/데이터 세그먼트 지정
마지막 비트 : 액세스 비트
어떤 프로그램이 이 세그먼트에 접근했을 때 cpu가 1로 바꿔주고, 클리어는 커널이 함
첫번째 비트가 0일 때
코드 세그먼트로 사용
두번째 비트 : Conforming 지원 여부(CPU Protected Mode의 보호 정책)
세번째 비트 : 세그먼트의 읽기 권한
GDT의 첫번재 디스크립터는 NULL 디스크립터를 기재해 주어야 됩니다.
15
|
14
|
13
|
12
|
11
|
10
|
9
|
8
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
0x0000
|
0x0000
|
0
|
00
|
0
|
0000
|
0x00
|
0x00
|
0
|
0
|
0
|
00
|
00
|
63
|
62
|
61
|
60
|
59
|
58
|
57
|
56
|
55
|
54
|
53
|
52
|
51
|
50
|
49
|
48
|
이것을
코드로 나타내면 다음과 같습니다.
gdt:
dw 0 ; limit 0 - 15
dw 0 ; base 0 - 15
dd 0 ; base 16 - 23
db 0 ; type, s, DPL,
P
db 0 ; limit 16 – 19,
AVL, 0, D, G
db 0 ; base 24 - 63
|
코드
세그먼트 디스크립터의 예시 입니다.
15
|
14
|
13
|
12
|
11
|
10
|
9
|
8
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
0xFFFF
|
0x0000
|
1
|
00
|
1
|
1010
|
0x01
|
0x00
|
1
|
1
|
0
|
00
|
0xF
|
63
|
62
|
61
|
60
|
59
|
58
|
57
|
56
|
55
|
54
|
53
|
52
|
51
|
50
|
49
|
48
|
위의
디스크립터를 코드로 나타내면 다음과 같습니다.
SysCodeSelector equ 0x08
dw 0xFFFF ;
limit 0 - 15
dw 0x0000 ; base
0 - 15
dd 0x01 ;
base 16 - 23
db 0x9A ;
10011010(2)
db 0xCF ;
11001111(2)
db 0x00 ;
base 24 - 63
|
5번째 줄의 한 바이트 중 상위 4비트는 0x9 중, 4번째 비트가 1이므로 코드 세그먼트라는 것을 알 수 있습니다.
마찬가지로
두 번째 비트와 세 번째 비트가 00 인데 이것으로 DPL이 0이고 따라서 커널 영역으로 사용됨을 나타내는 것을 알 수 있습니다.
데이터
세그먼트 디스크립터의 예시입니다.
15
|
14
|
13
|
12
|
11
|
10
|
9
|
8
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
0xFFFF
|
0x0000
|
1
|
00
|
1
|
0010
|
0x01
|
0x00
|
1
|
1
|
0
|
00
|
0xF
|
SysDataSelector equ 0x10
dw 0xFFFF ;
limit 0 - 15
dw 0x0000 ; base
0 - 15
dd 0x01 ;
base 16 - 23
db 0x92 ;
10010010(2)
db 0xCF ;
11001111(2)
db 0x00 ;
base 24 - 63
|
Type 필드가 0010임으로 위의 type 표를 보면, 이 세그먼트기 데이터 세그먼트이고, 읽기/쓰기가
가능하다는 것을 알 수 있습니다.
비디오
메모리를 위한 디스크립터의 예시입니다.
15
|
14
|
13
|
12
|
11
|
10
|
9
|
8
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
0xFFFF
|
0x8000
|
1
|
00
|
1
|
1010
|
0x0B
|
0x00
|
1
|
0
|
0
|
00
|
0x0
|
63
|
62
|
61
|
60
|
59
|
58
|
57
|
56
|
55
|
54
|
53
|
52
|
51
|
50
|
49
|
48
|
VideoSelector equ 0x18
dw 0xFFFF ;
limit 0 - 15
dw 0x8000 ; base
0 - 15
dd 0x0B ;
base 16 - 23
db 0x92 ;
10010010(2)
db 0x40 ;
10000000(2)
db 0x00 ;
base 24 - 63
|
6번째 줄을 보면, 상위 4비트가 4인 것을 알수 있으며, G가 0이 되므로, 세그먼트의 크기가 바이트 단위로 지정이 되었습니다.
GDTR
레지스터
GDTR은 GDT가 어디에 있는지, 몇 개나 있는지 저장하고 있는 레지스터입니다. 이 레지스터에 만든 GDT를 등록시켜 CPU가 사용할 수 있도록 해야됩니다.
처음 0 – 15 까지의 비트는 사용할 수 있는 GDT의 크기를 저장하고, 16 – 47까지의 비트는 GDT의 시작점 주소를 저장합니다.
GDTR 명령어는 GDT를 저장하고 있는 주소를 오퍼랜드로 받아, GDT를 등록합니다.
Lgdt[gdtr]
gdtr:
dw gdt_end – gdt – 1
dd gdt+0x10000
|
Gdt는 첫번째 gdt의 위치를 가리키며, gdt_end는 gdt의 끝을 나타냅니다. 0부터 시작함으로 1을 빼줍니다.
두번째는
커널의 베이스 어드레스가 0x10000이므로 0x10000를 더해줍니다.
Gdtr에 넣어야 하는 값은 세그먼트:오프셋이 아니라 물리 주소를 넣어야 합니다. 따라서 물리 주소의 값을 만들어서 넣어야 됩니다.
lgdt 명령어 동작
if(OperandSize == 16 { GDT.Limit = Source[0..16]; GDTR.Base = Source[16..47] & 0xFFFFFF; } else { //OperandSize == 32 GDTR.Lmit = Source[0..15]; GDTR.Base = Source[16..47]; } |
예외상황
Protected Mode
#UD |
source 오퍼랜드가 메모리 영역이 아닐때 |
#GP (0) |
현재 권한 레벨이 0이 아닐때 메모리 오퍼랜드의 유효 주소가 Cs, DS, ES, FS, GS 세그먼트의 리미트를 벗어날때 Null 세그먼트 셀렉터를 가지고 있는 DS, ES, FS, GS 레지스터가 메모리에 접근하는데 사용될 때 |
#SS 9)0 |
메모리 오퍼랜드의 유효 주소가 SS 세그먼트 리미트를 벗어날 때 |