반응형

IA-32e 모드를 활성화하는데 필요한 페이징 기능과 세그먼트 디스크립터를 이제 실제 코드로 작성하여 IA-32e 모드로 전환해보고 IA-32e 모드 커널로 들어가기 전에 코드를 정리합니다.

 


 

→ 물리 메모리 확장 기능 활성화와 페이지 테이블 설정

 

물리 메모리 확장 기능을 활성화하고 프로세서에 페이지 테이블을 설정하는 과정을 앞에서 봤습니다.

 

물리 메모리 확장(PAE, Physical Address Extensions) 기능CR4 레지스터의 PAE bit(bit 5)가 담당하고 있으며, PAE bit를 1로 설정하여 물리 메모리 확장 기능을 사용할 수 있습니다.

 

프로세서에 페이지 테이블을 설정하려면 CR3 레지스터에 PML4 테이블의 주소를 저장하면 됩니다.

 

아래는 CR3 레지스터CR4 레지스터를 조작하여 페이지 테이블을 설정하고 물리 메모리 확장 기능을 활성화하는 코드입니다.

 

물리 메모리 확장 기능을 활성화하고 페이지 테이블을 설정하는 코드

 


 

→ 64bit 모드 활성화와 페이징 활성화

 

IA-32e 모드를 활성화하는 실질적인 최종 관문은 범용 또는 컨트롤 레지스터가 아니며, MSR(Model-Specific Register)라고 불리는 특수한 용도의 레지스터IA32_EFER 레지스터의 LME bit를 1로 설정하는 것입니다.

 

IA32_EFER 레지스터의 bit를 활성화하지 않으면 IA-32e 모드용 세그먼트 디스크립터로 교체한다 해도 32bit 보호 모드로 동작할 뿐입니다.

 

MSR 레지스터프로세서 모델에 따라 특수하게 정의된 레지스터크기는 64bit이며 각 모델에 따라 차이가 있고, 크게 6가지 종류가 있습니다.

 

● 디버깅 및 성능 측정(Debugging And Performance Monitoring)

● 하드웨어 에러 검사(Machine-Check)

● 메모리 범위와 메모리 타입 설정(Memory Type Range Registers, MTRRs)

● 온도와 전력 관리(Thermal And Power Management)

● 특수 명령어 지원(Instruction-Specific Support)

● 프로세서 특성과 모드 지원(Processor Feature/Mode Support)

 

IA-32e 모드 전환에 사용할 IA32_EFER(Extended Feature Enable Register) 레지스터프로세서 특성과 모드 지원에 속하는 MSR 레지스터로 프로세서의 확장 기능을 제어할 수 있는 레지스터입니다.

 

IA32_EFER 레지스터로 제어할 수 있는 항목으로는 SYSCALL/SYSRET 사용, IA-32e 모드 사용, Execute Disable(EXB) 사용 등이 있으며, 제어 기능 외에 현재 운영 중인 모드가 IA-32e 모드인지 확인하는 기능도 포함되어 있습니다.

 

 

MSR 레지스터개별적으로 할당된 레지스터 주소가 있고, 해당 주소를 통해서만 접근할 수 있으며, 다른 레지스터와 달리 데이터 이동 명령(mov)으로 접근할 수 없고, MSR 레지스터용 명령어인 RDMSR(Read From Model Specific Register)과 WRMSR(Write To Model Specific Register)을 사용해야 합니다.

 

 

▶ RDMSR 명령어

 

MSR 레지스터에서 값을 읽어오는 역할이며, ECX와 EDX 그리고 EAX 레지스터를 사용하는데 ECX 레지스터에 읽을 MSR 레지스터 주소를 넘겨주면 MSR 레지스터의 상위 32bit는 EDX 레지스터에 하위 32bit는 EAX 레지스터에 저장해줍니다.

 

WRMSR 명령어

 

MSR 레지스터에 값을 쓰는 역할이며, RDMSR 명령어와 같은 레지스터를 사용하는데 ECX 레지스터에 쓸 MSR 레지스터를 넘겨주면, 상위 32bit는 EDX 레지스터의 값으로 하위 32 bit는 EAX 레지스터의 값으로 MSR 레지스터에 저장해줍니다.

 

앞으로 사용할 IA32_EFER 레지스터는 0xC0000080 주소에 있고 아래와 같이 구성되어 있습니다.

 

IA32_EFER 레지스터의 비트 구성

 

LME 비트를 1로 설정하는 코드를 보면 위의 사진에서 보는 것과 같이 IA-32e 모드 활성화 여부는 bit 8에 위치하는 LME bit와 관계가 있고 이 bit를 1로 설정하면 IA-32e 모드를 활성화할 수 있습니다.

 

아래는 IA32_EFER 레지스터를 변경하여 IA-32e 모드를 활성화하는 코드입니다.

 

IA32_EFER 레지스터를 통한 IA-32e 모드 활성화

 


→ IA-32e 모드로 전환과 세그먼트 셀렉터 초기화

 

IA-32e 모드로 전환하는 마지막 작업CR0 레지스터를 변경하여 캐시와 페이징을 활성화하고서 세그먼트 셀렉터를 IA-32e 커널용으로 교체하는 것입니다.

 

다루지 않았던 캐시를 활성화하는 방법만 알아보고 코드로 넘어갑니다.

 

앞에서 초기화한 페이지 테이블의 PCD bit와 PWT 비트는 페이징을 활성화했을 때만 유효합니다.

 

x86 계열의 프로세서에는 페이지의 캐시 설정보다 우선하는 캐시 관련 bit가 있으며, 그것은 바로 CR0 컨트롤 레지스터의 NW bit(Not Write-through, bit 29)와 CD bit(Cache Disable, bit 30)입니다.

 

캐시를 사용하려면 위의 두 bit 모두 0으로 설정해야 합니다.

 

CR0 컨트롤 레지스터의 CD bit를 1로 설정하여 캐시를 비활성화했다면, 페이지 테이블에 있는 PCD bit와 PWT 비트가 캐시를 사용하게 설정되었다 해도 무시됩니다.

 

아래는 CR0 레지스터를 변경하여 페이징을 활성화하고 IA-32e 모드로 전환하여 세그먼트 셀렉터를 초기화하는 코드입니다.

 

IA-32e 모드 커널용 코드 세그먼트 디스크립터와 데이터 세그먼트 디스크립터를 GDT 테이블의 오프셋 0x08, 0x10에 생성했으므로 해당 값을 참고하여 세그먼트 셀렉터를 초기화했습니다.

 

캐시와 페이지 활성화, IA-32e 모드 전환, 세그먼트 셀렉터 초기화를 수행하는 코드

 


→ 소스 코드 1차 정리와 실행

 

보호 모드 커널을 마무리하고 IA-32e 모드 커널을 시작하는 부분이므로 양쪽 커널을 모두 수정해야 하는데 IA-32e 모드 커널을 추가하려면 많은 부분을 수정해야 하므로 보호 모드 커널을 간단히 정리하고 IA-32e 모드 커널로 넘어갑니다.

 

가장 먼저 할 작업은 이전 글에서 나온 CPUID 명령어 및 IA-32e 모드 전환에 관련된 어셈블리어 코드를 하나의 파일로 합칩니다.

 

어셈블리어 코드가 IA-32e 모드 전환에 관련된 함수이므로, 그에 맞춰 01.Kernel32/Source/ModeSwitch.asm01.Kernel32/Source/ModeSwitch.h로 합니다.

 

IA-32e 모드 전환에 관련된 어셈블리어 코드(01.Kernel32/Source/ModeSwitch.asm) 1

 

IA-32e 모드 전환에 관련된 어셈블리어 코드(01.Kernel32/Source/ModeSwitch.asm) 2

 

IA-32e 모드 전환에 관련된 어셈블리어 코드(01.Kernel32/Source/ModeSwitch.asm) 3

 

 

IA-32e 모드 전환에 관련된 헤더 파일(01.Kernel32/Source/ModeSwitch.h)

 


 

→ 보호 모드 커널의 엔트리 포인트 파일 수정

보호 모드 커널의 엔트리 포인트 파일에는 IA-32e 모드 커널용 세그먼트 디스크립터가 추가되었기 때문에 보호 모드 커널용 디스크립터의 위치가 변경되었으며, 코드 세그먼트 디스크립터는 0x08에서 0x18로 데이터 세그먼트 디스크립터는 오프셋 0x10에서 0x20으로 옮겨졌습니다.

 

세그먼트 디스크립터의 위치가 변경되면 jmp 시 지정하는 코드 세그먼트 디스크립터와 모드 전환 후 설정하는 데이터 세그먼트 디스크립터를 변경해야합니다.

 

수정한 보호 모드 커널의 엔트리 포인터 파일입니다.

 

보호 모드 커널의 엔트리 포인트 소스 코드(01.Kernel32/Source/EntryPoint.s) 1

 

보호 모드 커널의 엔트리 포인트 소스 코드(01.Kernel32/Source/EntryPoint.s) 2

 

보호 모드 커널의 엔트리 포인트 소스 코드(01.Kernel32/Source/EntryPoint.s) 3

 

보호 모드 커널의 엔트리 포인트 소스 코드(01.Kernel32/Source/EntryPoint.s) 4

 

보호 모드 커널의 엔트리 포인트 소스 코드(01.Kernel32/Source/EntryPoint.s) 5

 


 

→ 보호 모드 커널의 C 언어 엔트리 포인트 파일 수정

 

IA-32e 모드 지원 여부를 검사하는 기능과 IA-32e 모드로 전환하는 기능이 추가되었으니 보호 모드 커널의 C언어 엔트리 포인트 함수에서 이를 호출해야 합니다.

 

하지만 아직 IA-32e 모드 커널이 존재하지 않아 IA-32e 모드 전환 함수를 호출할 수 없으므로 IA-32e 모드 전환 함수는 코드를 넣어두되 주석으로 처리해둡니다.

 

아래는 IA-32e 모드 지원 여부를 검사하는 코드와 IA-32e 모드 전환 함수가 추가된 보호 모드 커널의 C 언어 엔트리 포인트 파일(01.Kernel32/Source/Main.c)을 나타낸 것입니다.

 

보호 모드 커널의 C언어 엔트리 포인트 소스 코드(01.Kernel32/Source/Main.c) 1

 

보호 모드 커널의 C언어 엔트리 포인트 소스 코드(01.Kernel32/Source/Main.c) 2

 

보호 모드 커널의 C언어 엔트리 포인트 소스 코드(01.Kernel32/Source/Main.c) 3

 

보호 모드 커널의 C언어 엔트리 포인트 소스 코드(01.Kernel32/Source/Main.c) 4

 


 

→ 빌드와 실행

 

이제 중간 점검을 위해 FS64 OS의 최상위 디렉터리에서 make를 입력하거나 IDE(ex. eclipse)에서 빌드를 실행하여 Disk.img 파일을 생성한 후 가상 머신 QEMU에서 테스트합니다.

 

만약 빌드 오류 시 make clean을 해본 뒤 make를 해주시면 됩니다.

 

정상적으로 잘 실행되었다면 아래의 사진과 같은 화면이 뜰 것입니다.

 

1차 정리된 FS64OS를 실행한 화면

반응형

+ Recent posts