IT 그리고 정보보안/Knowledge base

MintOS 32-bit 보호모드 전환

plummmm 2021. 4. 12. 22:15
반응형

자.. 이제 부트로더를 다 만들어 봤으니 32비트 보호모드로 점프 뛰어보자.

보호모드로 전환하려면 아래의 6가지 단계를 거쳐야 한다.

 

세그먼트 디스크립터 생성, GDT 정보 생성 단계는 보호 모드 전환에 필요한 자료구조를 생성하는 단계이고,

그 다음 프로세서에 GDT 정보 설정 ~ 각종 세그먼트 셀렉터 및 스택 초기화 단계에서는

생성된 자료구조를 프로세서에 설정하는 단계이다.

 

세그먼트 디스크립터와 GDT는 보호모드가 실행되자 마자 즉시 프로세서에서 참조하는 부분이니까

보호모드로 전환하기 전에 미리 우리가 이 두가지 자료구조들은 생성을 해두어야 한다.

 

1. 세그먼트 디스크립터 생성

세그먼트 디스크립터 생성부터 해봅시다.

이 놈은 세그먼테이션에서 세그먼트의 정보를 나타내는 자료구조이다.

먼저 우리가 사용할 세그먼트에 대해 정리를 먼저 해야할 것이다. 아래 한번 정리해봅시다.

 

1. 커널 코드와 데이터용 세그먼트 디스크립터 각각 1개

2. 커널 코드와 데이터 세그먼트는 0 ~ 4GB 까지 모든 영역에 접근할 수 있어야 한다.

3. 보호 모드용 코드와 데이터에 사용할 기본 오퍼랜드는 크기가 32비트 여야 한다.

4. 보호 기능은 사용하지 않으며, 프로세서의 명령을 사용하는 데 제약이 없어야 하므로 최상위 권한(0) 여야 한다.

 

위 4가지 조건을 가지고 세그먼트 디스크립터를 구성해야 한다. 

 

먼저 코드 세그먼트와 데이터 세그먼트 디스크립터 타입을 설정해보자.

위에 포스트에 정리가 되어 있긴 하지만 약간 보기 힘들다. S필드와 TYPE 필드를 건드려야 한다.

 

먼저 S필드는 0이 시스템 디스크립터, 1이 세그먼트 디스크립터이다. 코드,데이터 세그먼트 디스크립터를 설정하는 거니까

당연히 S필드의 값은 1이 되어야 할 것이다. 

 

다음 TYPE필드를 보자. 이놈은 약간 복잡해서 표에다가 정리를 좀 해서 봐야 될 것 같다.

코드 세그먼트는 실행/읽기 타입으로 설정하고 데이터 세그먼트는 읽기/쓰기 타입으로 설정한다.

그럼 코드 세그먼트는 0x0A, 데이터 세그먼트는 0x02로 설정하면 될것이다.

 

위에서 접근됨<- 이 옵션이 있는데, 8번 비트 A가 1로 셋팅되면 접근됨으로 설정된다.

프로세서에서 설정하는 비트로, 프로세스는 해당 디스크립터가 참조될 때마다 8번을 1로 설정한다.

그니까 해당 디스크립터가 사용되었느냐? 를 판별하는 비트라고 할 수 있다.

 

10번 비트 C는 데이터 세그먼트에서 역방향 확장으로 반대로 쌓이는 스택을 위한 옵션이다. 

스택 세그먼트의 크기를 동적으로 확장, 축소할 목적으로 사용한다. 

코드 세그먼트에서는 접근 승인에 대한 비트인데, 권한에 관계없이 코드 세그먼트에 접근할 수 있음을 나타내는 것이다.

승인 기능을 사용하면 어플리케이션에서 커널 코드를 직접 실행가능하고, 어플리케이션, 커널간의 코드 공유를 할 때

유용하게 사용되는 기능이다.

 

자 이제 TYPE과 S비트를 설정했으니,  세그먼트의 영역 설정을 해보자.

우리가 만들 MINT64 OS의 커널 세그먼트 디스크립터는 4GB 영역 전부다 접근가능 해야 한다.

그러므로 기준 주소는 0으로 설정하고 크기는 4GB가 되어야 겠지.

 

근데 크기 비트 (Limit Address) 는 총 20비트 크기인데, 이걸로는 해봤자 2^20 = 1MB 밖에 안된다.

그럼 어떻게 해야 할까. 바로 G 필드가 그 답이다. 위 포스트 링크에서 G 필드가 1로 셋팅 되면 4KB만큼 값을 곱한다했다.

그니까 (1MB *  4KB = 4GB) 이렇게 된다.

 

그 다음 기본 오퍼랜드 크기와 권한 설정을 해보자.

위에서 말했듯이 32비트 크기로 설정해야 한다. 이거에 관한 필드는 D/B 필드이다.

0이면 16비트 세그먼트로, 1이면 32비트 세그먼트로 인식한다. 만약 64비트로 하고 싶으면 L필드도 건드려야 하는데

아직 보호모드까지만 하는 거니까.. 이건 다음에 나오면 다시한번 보자.

 

권한에 관한 필드는 DPL이라고 따로 있지만, 우리가 만들 MINT는 다 최상위 권한 00 으로 설정한다.

 

그다음 기타 필드들을 설정하자. P필드를 1로 설정하여 해당 디스크립터가 유효하다는 것을 알려주고

AVL 필드를 0으로 설정한다. (그냥 임의로 사용하는 디스크립터다.이말)

 

이제 필드 값을 뭘 집어넣어야 할지 다 찾았다. 그럼 세그먼트 디스크립터 생성 코드를 한번 작성해봐야겠지

 

솔직히 이걸 보면 무슨 말인지, 어디에 매칭되는지 잘 이해가 안갈수도 있다. 그래서 그림을 그렸다.

 

이런 느낌인데, 이해가 갈런지 모르겠다. 내가 이해했으니 그냥 넘어가도록 하겠다.(?)

일단 여기까지가 세그먼트 디스크립터를 생성하는 과정이다.

 

2. GDT 정보 생성

이번엔 저번에 계속해서 이어 32비트 보호모드 전환, GDT 정보 생성에 대해 알아보자.

 

GDT(Global Descriptor Table) 은 연속된 디스크립터의 집합이다.

우리가 저번에 생성했던 코드 세그먼트 디스크립터, 데이터 세그먼트 디스크립터를 연속적으로

어셈블리 코드로 나열한다면 그게 GDT가 되는 것이다.

 

여기서 GDT를 구성할 때 한가지 유의해야 될점은 가장 앞부분에 NULL 디스크립터를 추가해야 한다.

이녀석은 프로세서에 의해 예약된 디스크립터로, 모든 필드가 0으로 초기화된 디스크립터이다.

 

47---------------------------16 15------------0

 l      32비트 기준주소          l       크기       l

 --------------------------------------------------

 

위 그림(?)은 GDT 정보를 저장하는 자료 구조이다.

기준 주소는 32비트이며, 데이트 세그먼트 기준주소와 관계없이 0으로 시작하는 선형주소이다.

그래서 GDT의 시작 주소를 실제 메모리 공간상의 어드레스로 변형해야 한다.

 

GDT의 선형주소는 현재 코드가 실행되고 있는 세그먼트의 기준주소를 알고 있으니

GDT의 옵셋을 구하고 세그먼트 기준 주소를 더해주면 구할 수 있다.

 

부트로더에 의해서 0x10000에 로딩되어 실행이 되니까 기준 주소는 0x10000이다.

이에 착안하여 코드를 한번 작성해보겠다. 그냥 생성한 디스크립터 앞뒤로 코드 몇개 넣어주면 된다.

 

자 이렇게 GDT 정보 생성하는 코드가 끝났다.

 

하나 추가로 얘기하자면, 기존의 x86에서는 GDT에 생성가능한 디스크립터의 갯수( 최대 8192개)가 부족할 경우가 생겨

LDT(Local Descriptor Table) 이라는 것이 있다. GDT와 거의 유사하고 작업을 분산시켜 더욱 처리가 유연해질 수 있따.

하지만 우리가 만드는 MINT64 OS는 이런 테스크 스위칭 기능을 소프트웨어적으로 구현하기 때문에 

LDT를 사용하지 않는다.



일단 여기까지 작성하고 뒷내용은 천천히 작성하겟슴다

반응형

'IT 그리고 정보보안 > Knowledge base' 카테고리의 다른 글

윈도우 커널 생성 (Ntos****.exe)  (0) 2021.04.13
윈도우 부팅 과정 및 OS 기초  (0) 2021.04.13
MintOS 부트 로더 제작  (0) 2021.04.12
MintOS 부팅 과정  (0) 2021.04.12
OS 메모리 관리 기법  (0) 2021.04.12