Tuuna Computer Science

[OS]Protected Mode(보호모드)와 GDT 본문

OS

[OS]Protected Mode(보호모드)와 GDT

GuTTe 2018. 11. 10. 22:48

OS 제작과정 중에 Real Mode에서 Protected Mode로 변환할 때 가장 중요한 것은


32비트 Kernel을 개발하는 것이다. 즉, 이는 CPU가 Protected Mode로 동작하도록 해야하는 것이다.


이 Real Mode와 Protected Mode로 변환하기 전엔 GDT(Global Descriptor Table)이라는 것이 필요로 한다.


GDT는 각 세그먼트 영역에 대해 이것들을 어떻게 사용할 것인지에 대해 서술한 것을 의미한다.

(여담이지만 최신 윈도우와 리눅스는 LDT라고 Local Descriptor Table을 사용하는데 나중에 한 번 공부해봐야겠다.)


---------------------------------------------
Base Address : 세그먼트의 시작 주소 
Limit : 세그먼트의 크기 
속성 : 세그먼트를 어떻게 활용할 것인가
----------------------------------------------
(GDT-디스크립터 테이블)

GDT를 구성하는 각 요소에 대해 알아보자

※ Limit :: 세그먼트의 한계점을 나타내어 준다. (20bit)
-G비트가 0이면 바이트단위, 1이면 세그먼트 크기를 4KB단위로 한다.

※Base Address :: 세그먼트의 시작 주소를 의미한다. (32bit)

※P비트 :: 세그먼트가 메모리상에 존재하는지를 나타내는 값 (Default value : 1) (1bit)

※DPL비트 :: 세그먼트가 커널 레벨인지, 유저 레벨인지 나타낸다. (2bit)
(0~3 kernel의 경우 0, User의 경우 3)

※S비트 :: 세그먼트가 시스템(0)인지 코드 혹은 데이터 세그먼트(1)인지 결정할 때 (1bit)

※Type비트 :: 세그먼트에 대해 자세한 속성을 부여(4bit)


최상위 비트가 1인지 0인지에 대해 코드 영역과 데이터 영역인지에 달라짐.

※최상위 비트가 1일 때 : 코드 영역
※두 번째 비트가 1일 때 : EXPAND DOWN (스택의 크기가 변활 때 모양)
※두 번째 비트가 0일 때 : EXPAND UP

※세 번째 비트가 1일 때 : READ or WRITE
※세 번째 비트가 0일 떄 : READ

※최상위 비트가 0일 때 : 데이터 영역
※두 번째 비트가 1일 때 : Comforming 
※두 번째 비트가 0일 때 : Non-Comforming

※세 번째 비트가 1일 때 : EXECUTE or READ
※세 번째 비트가 0일 떄 : ONLY EXECUTE!

※네 번째 비트가 1일 때 : ACCESS
※네 번째 비트가 0일 때 : Non-ACCESS

※D비트 :: 이 세그먼트가 16bit(0)인지 32bit(1)인지 결정한다. (1bit)
===============================================
gdt:
    DW 0                        ; limit 0~15비트
    DW 0                        ; 베이스 어드레스의 하위 2바이트
    DB 0                        ; 베이스 어드레스 16~23비트
    DB 0                        ; 타입
    DB 0                        ; limit 16~19비트, 플래그
    DB 0 
==============================================
첫 번째 디스크립터는 NULL 디스크립터이다. 0으로 채워주자

==============================================
SysCodeSelector EQU 0x08
    DW 0xFFFF                      ; limit:0xFFFF (limit의 상위 16비트)
    DW 0x0000                      ; base 0~15 bit
    DB 0x01                        ; base 16~23 bit
    DB 0x9A                        ; P:1, DPL:0, Code, non-conforming, readable
    DB 0xCF                        ; G:1, D:1, limit 16~19 bit:0xF (limit의 하위 4비트) 
    DB 0x00                        ; base 24~32 bit
==============================================

이 디스크립터는 코드 영역의 세그먼트이다. 이 예제를 통해 자세히 살펴보자.

일단 먼저 0~15비트 Limit(0xFFFF) + 16~19비트 Limit(0xF) => 0xFFFFF이Gl다.
여기서 G비트가 있는 5번 째줄에 0xCF를 이진수로 까보면 (1100 1111)이다.

즉, G비트는 1이므로 Limit의 0xFFFFF * 4KB(0xFFF)를 해주어야 한다. => 0xFFFFFFFF

0~15비트 Base Address(0x0000) + 16~23비트 Base Address(0x01) + 24~32비트 Base Address(0x00)을 계산하면

0x10000이 나온다. 즉 Base Address는 0x10000

나머지 비트도 구해보자.

P 비트와 DPL비트 Type비트가 있는 네 번째 줄
0x9A를 2진수로 풀어보면 (1001 1010) 즉, P비트는 1, DPL비트는 : 00, S비트는 1, Type비트는 1010

이렇게 데이터 세그먼트에 대한 GDT를 계산했다. 나머지 것들도 계산해서 하나의 디스크립터를 만들고
모으면 테이블 형식을 취하게 된다.

리눅스 kernel 2.4의 GDT부분인데 개인적으로 한 번 구해봐야 겠다.
=====================================
gdt: 
    .word 0, 0, 0, 0 #dummy
    .word 0, 0, 0, 0 

    .word 0xFFFF    #4Gb - (0x100000*0x1000 - 4Gb) 
    .word 0         #base address = 0 
    .word 0x9A00    #code read/exec 
    .word 0x00CD    #granularity = 4096, 386 
    
    .word 0xFFFF    #4Gb 
    .word 0         #base address 
    .word 0x9200    #data read/write 
    .word 0x00CD    #grandularity = 4096 386 
======================================

아 그리고 이렇게 만들어진 디스크립터들은 CPU가 모른다!

그래서 GDT가 어디번지에 있는지 몇개가 있는지 CPU의 GDTR레지스터에 등록해야한다.

lgdt[gdtr]    ;gdtr의 주소

lgdt명령어로 gdtr의 주소를 포인터형으로 넘겨야 한다.

gdtr에는 48bit GDTR레지스터에 들어갈 수 있게 GDT크기(16bit) + GDT의 시작점 주(32bit)

※ Protected Mode에서 주소 지정 방법

흔히 CPU에는 세그먼트 레지스터가 있다고 들었을 것이다.
예를 들어 CS, DS ... 이런식으로

리얼모드에선 이 세그먼트 레지스터가 16비트형식으로 단순히 있었지만

보호모드 형식으로 진입하면서 이 세그먼트 레지스터가 2개의 레지스터가 합쳐진것을 의미하게 된다.

즉, 세그먼트 셀렉터와 세그먼트 디스크립터 레지스터로 이루어진다.

(세그먼트 레지스터를 이루는 것들) 

여기서 디스크립터 레지스터는 프로그래머가 손댈 수 없고 세그먼트 셀렉터에 값을 넣는 순간
CPU가 알아서 해준다.

(세그먼트 셀렉터)

총 16비트를 가지고 있다.

3~15비트는 각 GDT의 디스크립터들의 INDEX를 매긴다.
TI비트는 사용할 세그먼트가 LDT인지 GDT인지를 의미한다.

RPL은 권한을 의미하는데 GDT의 DPL비트와 동일한 의미를 지닌다.

예를 들어서 SysCodeSelector equ 0x10이 있다고 치자.

0x10은 2진수로 10000이다. 이를 세그먼트 셀렉터형식으로 취하면

index는 10
TI는 0
RPL은 00이 된다.

즉, GDT의 (10) 2번 인덱스에서 디스크립터를 가져오고 이 디스크립터의 DPL과 세그먼트 셀렉터의 RPL과
같이 같다면 해당 디스크립터 레지스터에 복사된다.

※디스크립터 하나의 크기는 8바이트












Comments