반응형

리얼 모드에서 보호 모드로 전환하려면 크게 6단계를 거쳐야 하는데 그중 상위 2 단계는 보호 모드 전환에 필요한 자료구조를 생성하는 단계이고, 나머지 4단계는 생성된 자료구조를 프로세서에 설정하는 단계입니다.

보호 모드에서 반드시 생성해야 하는 자료구조세그먼트 디스크립터 GDT가 있는데, 이 두 가지 자료구조는 보호 모드로 전환하는 즉시 프로세서에 의해 참조되기 때문에 미리 생성해둬야 합니다.

자료구조를 생성하는 두 단계를 제외한 나머지 단계프로세서에 자료구조를 설정하거나 보호 모드로 전환하고 나서 필요한 초기화 작업을 수행합니다.

보호 모드로 전환하는 여섯 가지 단계


→ 세그먼트 디스크립터 생성


세그먼트 디스크립터세그먼테이션 기법(메모리 관리 기법)에서 세그먼트의 정보를 나타내는 자료구조이며, 크게 코드 세그먼트 디스크립터데이터 세그먼트 디스크립터로 나뉩니다.

세그먼트메모리 공간을 임의의 크기로 나눈 영역을 의미하며, 세그먼트를 복잡하게 구성할수록 세그먼트 디스크립터의 수도 증가합니다.

FS64 OS에서는 보호 모드의 기본 기능만 사용하므로 4GB 전체 메모리 공간을 지정하는 커널 코드와 데이터 세그먼트만 사용합니다.

→ 코드 세그먼트 디스크립터

실행 가능한 코드가 포함된 세그먼트에 대한 정보를 나타내며, CS 세그먼트 셀렉터(세그먼트 레지스터)에 사용됩니다.

데이터 세그먼트 디스크립터

● 데이터가 포함된 세그먼트에 대한 정보를 나타내며, CS 세그먼트 셀렉터(세그먼트 레지스터)를 제외한 나머지 셀렉터(레지스터)에 사용할 수 있습니다.

스택 영역 또한 데이터를 읽고 쓰는 데이터 영역의 한 종류이므로 데이터 세그먼트 디스크립터를 사용합니다.

보호 모드세그먼트 디스크립터8byte로 다양한 필드(항목)가 있습니다.

각 필드에 대한 자세한 설명은 아래의 사진을 참고하시면 됩니다.

http://egllos.zum.com/anster/v/2135644
https://velog.io/@wimes/32%EB%B9%84%ED%8A%B8-%EB%B3%B4%ED%98%B8%EB%AA%A8%EB%93%9C%EB%A1%9C-%EC%A0%84%ED%99%98%ED%95%98%EC%9E%90


다음은 세그먼트의 각 필드에 값을 설정하기 전에 앞으로 설정하고자 하는 세그먼트에 대해서 정리한 것입니다.

아래의 정리를 참고하면 디스크립터의 전체 값은 아니더라도 세그먼트 디스크립터의 몇몇 필드 값을 대략 유추할 수 있습니다.

커널 코드와 데이터용 세그먼트 디스크립터 각 1개
커널 코드와 데이터용 세그먼트 0 ~ 4GB까지 모든 영역에 접근할 수 있어야 합니다.
보호 모드용 코드와 데이터에 사용할 기본 오퍼랜드 크기는 32bit(4byte)여야 합니다.
보호 기능은 사용하지 않으며, 프로세서의 명령을 사용하는데 제약이 없어야 하므로 최상위 권한(0)이어야 합니다.


→ 코드 세그먼트 디스크립터와 데이터 세그먼트 디스크립터 타입 설정

코드 세그먼트데이터 세그먼트를 설정하려면 S 필드와 타입 필드를 조합해야 합니다.

코드 세그먼트데이터 세그먼트세그먼트 디스크립터이기 때문에 간단하게 S 필드의 값을 1로 설정하고, 세그먼트 타입은 4bit 크기의 타입 필드를 이용해서 설정합니다.

FS64 OS에서는 기본적인 세그먼트 타입만 사용하고, 코드 세그먼트는 실행/읽기 타입으로 설정하고 데이터 세그먼트는 읽기/쓰기 타입으로 설정합니다.

아래의 표를 참고하면, 코드 세그먼트 타입은 0x0A(Execute/Read) 데이터 세그먼트 타입은 0x02(Read/Write)가 됩니다.

타입 필드의 값과 의미


참고)
세그먼트 타입의 bit 8에 있는 접근 여부 bit는 프로세서에서 설정하는 bit로 프로세스는 해당 디스크립터가 참조될 때마다 bit 8을 1로 설정합니다.

이를 이용하면 bit 8을 이용해서 특정 세그먼트 디스크립터가 사용되었는지 여부를 확인할 수 있습니다.

비트 10에 있는 역방향 확장 비트는 아래로(상위 어드레스 → 하위 어드레스) 자라는 스택을 위한 옵션이며 스택 세그먼트의 크기를 동적으로 확장 또는 축소할 목적으로 사용합니다.

역방향 확장 기능을 사용하면 세그먼트 크기(Limit) 값이 기준 주소에서 아래 방향(0byte) 방향으로 적용되며, 세그먼트의 범위는(세그먼트의 기준 주소 - 세그먼트 크기) ~ 세그먼트의 기준 주소가 됩니다.

그렇기에 스택의 변화에 맞추어 세그먼트 크기를 동적으로 변경할 수 있습니다.

그리고 같은 bit 10에 위치한 접근 승인 비트는 권한에 관계없이 해당 코드 세그먼트에 접근할 수 있음을 나타냅니다.

승인 기능을 사용하면 애플리케이션도 커널 코드를 직접 실행할 수 있으며, 커널과 애플리케이션 간에 코드 공유가 필요할 때도 요긴하게 쓸 수 있습니다.

세그먼트 디스크립터 타입에 대한 자세한 설명은 VOLUME 3A: System Programming Guide Part 1의 3.4.5 System Descriptors를 참고하면 됩니다.


→ 세그먼트의 영역 설정

FS64 OS커널 세그먼트 디스크립터4GB 전체 영역에 접근할 수 있어야 하기에 커널용 세그먼트 디스크립터의 기준 주소는 0으로 설정합니다.

크기(Limit) 필드는 총 20bit이며, 20bit로 표현할 수 있는 최댓값은 2 ^ 20(=1MB)이기 때문에 이 필드만으로는 4GB까지의 영역을 표현할 수 없으므로 G필드를 사용하는데 G 필드의 값을 1로 설정하면 크기 필드에 4KB를 곱한 것이 실제 세그먼트의 크기가 됩니다.

1MB4KB를 곱하면 4GB가 되므로 크기(Limit) 필드G 필드를 사용하면 메모리 전체 영역을 세그먼트의 영역으로 설정할 수 있습니다.


→ 기본 오퍼랜드 크기와 권한 설정

보호 모드32bit로 동작하므로 기본 오퍼랜드의 크기도 32bit로 설정해야 하는데, 기본 오퍼랜드의 크기는 D/B 필드가 담당하므로 이 필드를 1로 설정하면 기본 오퍼랜드의 크기를 32bit로 설정할 수 있습니다.

기본 오퍼랜드의 크기와 관련된 필드는 D/B 필드 말고도 IA-32e 모드의 64bit 서브 모드 또는 32bit 호환 모드를 설정하는 L 필드도 있는데, 지금 생성하는 디스크립터는 보호 모드용이므로 L 필드는 0으로 설정합니다.

권한 필드보호 모드의 주요 특징 중 하나인 보호 기능에 핵심 역할을 하는 필드이고, 프로세서는 디스크립터의 권한 필드에 설정된 값과 세그먼트 셀렉터의 권한을 비교하여 접근이 가능한지를 판단하며 x86 프로세서에서 동작하는 운영체제의 대부분도 이 기능을 사용하여 OS의 핵심 부분을 보호하고 있습니다.

FS64 OS보호 모드는 권한을 따로 구분하지 않기에 권한 필드를 모두 최상위 권한(0)으로 설정합니다.


→ 기타 필드 설정

생성한 세그먼트 디스크립터보호 모드로 전환하는 과정에서 사용하므로 유효한 디스크립터라는 것을 알려야 하기에 디스크립터가 유효함을 나타내는 필드인 P 필드를 1로 설정하면 해당 디스크립터를 사용할 수 있습니다.

AVL 필드임의로 사용할 수 있는 필드FS64 OS에서는 별도로 값을 쓰지 않기 때문에 0으로 설정합니다.


어셈블리어를 사용하여 코드 세그먼트 디스크립터데이터 세그먼트 디스크립터를 생성하기 전에 아래의 사진을 이해하고 넘어가야 합니다.

자료구조와 메모리 공간, 어셈블리어 코드와의 관계를 나타낸 사진입니다.

인텔 문서하위 방향에서 상위 방향, 오른쪽에서 왼쪽으로 주소가 증가하는데, 아래 그림의 메모리 공간의 배열과 같은 구조입니다.

어셈블리어 코드하위 주소의 데이터부터 상위 주소의 데이터 순서로 기술하기 때문에 메모리 공간에 나타난 순서와는 반대입니다.

앞으로 나오는 자료구조는 이와 같은 방법으로 변환할 것입니다.

세그먼트 디스크립터 구조와 메모리 공간, 그리고 어셈블리어 코드의 관계


각 필드의 값을 토대로 코드 세그먼트 디스크립터데이터 세그먼트 디스크립터를 생성하는 실제 어셈블리어 코드는 아래와 같습니다.

커널 코드 세그먼트와 데이터 세그먼트 디스크립터 생성 코드


매뉴얼 상 64bit IA-32e 모드로 전환하려면 반드시 32bit 보호 모드를 거쳐야 한다고 되어 있습니다만 한 가지 팁으로

보호 모드에서 하는 작업들을 리얼 모드에서 한 뒤 IA-32e 모드로 jump 하면 반드시 보호모드를 거치지 않아도 리얼 모드에서 바로 IA-32e 모드로 넘어 갈 수 있습니다.
(https://kkamagui.tistory.com/756?category=187695)

보호 모드는 현대 OS가 제공하는 4GB의 주소 공간, 멀티태스킹, 페이징, 메모리 보호 등의 기능을 하드웨어적으로 지원합니다.

보호 모드는 FS64 OS에서는 64bit로 전환하기 위한 임시 모드에 불과하므로 심도 있게 다룰 필요가 없습니다.


→ GDT 정보 생성

GDT(Global Descriptor Table) 자체는 연속된 디스크립터의 집합입니다.

FS64 OS에서 사용하는 코드 세그먼트 디스크립터데이터 세그먼트 디스크립터연속된 어셈블리어 코드로 나타내면 그 전체 영역이 GDT가 되지만, 한 가지 제약 조건으로는 널 디스크립터(NULL Descriptor)를 가장 앞부분에 추가해야 합니다.

NULL Descriptor프로세서에 의해 예약된 디스크립터로 모든 필드가 0으로 초기화된 디스크립터이며 일반적으로 참조되지 않습니다.

GDT디스크립터 집합이므로 프로세서에 GDT의 시작 주소크기 정보를 로딩해야 하는데, 이것을 저장하는 자료구조가 필요하고, 이 자료구조는 아래와 같은 구조를 하고 있습니다.

GDT 정보를 저장하는 자료구조


GDT 정보를 저장하는 자료구조의 기준 주소 필드는 32bit 크기이며, 데이터 세그먼트의 기준 주소와 관계 없이 주소 0을 기준으로 하는 선형 주소입니다.

즉, 선형 주소상의 GDT의 시작 주소를 나타내는 32bit(4byte) 기준 주소 필드와 GDT의 전체 크기를 나타내는 16bit(2byte) 크기 필드를 포함하는 GDTR 레지스터는 프로세서가 리셋되거나 켜졌을 때 기준 주소 필드는 0으로 초기화하고, 크기 필드는 0xFFFF 로 초기화 합니다.

그렇기 때문에 GDT의 시작 주소를 실제 메모리 공간상의 주소로 변환할 필요가 있습니다.

GDT의 선형 주소현재 코드가 실행되고 있는 세그먼트의 기준 주소를 알고 있으므로 현재 세그먼트의 시작을 기준으로 GDT의 오프셋을 구하고, 세그먼트 기준 주소를 더해주면 구할 수 있는데
현재 코드는 부트 로더에 의해 0x10000에 로딩되어 실행되고 있으므로 자료구조를 생성할 때 GDT 오프셋에 아래의 코드와 같이 0x10000을 더해주면 선형 주소가 됩니다.

앞에서 세그먼트 디스크립터를 생성했으므로, NULL 디스크립터와 GDT 정보를 저장하는 자료구조만 앞뒤로 추가하여 어셈블리어 코드를 작성하면 됩니다.

GDTR 및 GDT 정보 생성 코드


이렇게 보호 모드로 전환하는 데 필요한 데이터가 모두 준비되었습니다.

참고)
GDT 정보를 담는 자료구조에서 GDT의 크기를 나타내는 필드가 2byte(16bit)이므로 GDT에 포함 가능한 디스크립터(8byte 크기) 수는 65536/8이 되어 최대 8192개가 됩니다.

하지만 이는 멀티태스킹을 위해 태스크 디스크립터태스크별 세그먼트 디스크립터, 기타 디스크립터를 생성하다 보면 공간이 부족합니다.

그래서 x86 프로세서GDT와 비슷한 역할을 하는 LDT(Local Descriptor Table)을 제공하는데, LDT 역시 GDT와 같이 디스크립터를 포함하는 테이블이며, 크기도 GDT와 같이 최대 8192개로 제한되어 있습니다.

LDT를 사용하면 태스크에 관련된 세그먼트 디스크립터나 기타 시스템 디스크립터를 GDT에서 LDT로 옮길 수 있으므로 더 많은 태스크를 생성할 수 있다는 장점이 있지만

FS64 OS멀티태스킹에 핵심적인 역할을 하는 태스크 스위칭 기능을 소프트웨어적으로 구현하여, x86 프로세서의 하드웨어 기능에 의존하지 않기 때문에 태스크 증가에 따른 GDT 사용량이 많지 않아 LDT를 사용하지 않습니다.

반응형

+ Recent posts