들어가기
64비트 모드로 전환하는데 필요한 페이징 기능 활성화와 IA-32e 모드 커널을 작성
본론
8.1 프로세서의 제조사와 IA-32e 지원 여부 검사
- 프로세서마다 지원하는 기능이 조금씩 다름.
- 프로세서 제조사는 지원하는 기능을 확인할 수 있는
CPUID
명령어 제공.
8.1.1 CPUID를 사용하여 프로세서 정보 확인 방법

CPUID
명령어는 EAX에 설정된 값에 따라 해당 정보를 조회, 반환값은 EAX, EBX, ECX, EDX에 저장.
8.1.2 프로세서 제조사와 IA-32e 모드 지원 여부 확인
| [BITS 32] |
| global kReadCPUID |
| |
| SECTION .text |
| |
| |
| |
| kReadCPUID: |
| push ebp |
| mov ebp, esp |
| push eax |
| push ebx |
| push ecx |
| push edx |
| push esi |
| |
| |
| |
| |
| mov eax, dword [ ebp + 8 ] |
| cpuid |
| |
| |
| |
| |
| |
| mov esi, dword [ ebp + 12 ] |
| mov dword [ esi ], eax |
| |
| |
| mov esi, dword [ ebp + 16 ] |
| mov dword [ esi ], ebx |
| |
| |
| |
| mov esi, dword [ ebp + 20 ] |
| mov dword [ esi ], ecx |
| |
| |
| |
| mov esi, dword [ ebp + 24 ] |
| mov dword [ esi ], edx |
| |
| |
| pop esi |
| pop edx |
| pop ecx |
| pop ebx |
| pop eax |
| pop ebp |
| ret |
- CPUID 명령어를 사용하려면 어셈블리어 코드로 직접 레지스터를 제어해야 함.
- CPUID 명령을 수행하는 함수가 외부에서도 실행 가능하도록 global 사용.
- 주의해서 볼 부분은 결과를 넘겨줄때 esi를 사용 -> C 코드에서 넘어올 때 주소로 넘어옴.
| |
| kReadCPUID( 0x80000001, &dwEAX, &dwEBX, &dwECX, &dwEDX ); |
| kPrintString( 0, 8, "64bit Mode Support Check -------------------[ ]" ); |
| if( dwEDX & ( 1 << 29 ) ) |
| { |
| kPrintString( 45, 8, "PASS" ); |
| } |
| else |
| { |
| kPrintString( 45, 8, "FAIL" ); |
| kPrintString( 0, 9, "This processor does not support 64bit mode~!!" ); |
| while( 1 ) ; |
| } |
- EAX에 0x80000001을 넣은후 kReadCPUID 호출.
8.2 IA-32e 모드용 세그먼트 디스크립터 추가
8.2.1 보호 모드 커널 엔트리 포인트에 디스크립터 추가

- 보호 모드의 세그먼트 디스크립터와 거의 같은 크기와 필드로 구성.
- 차이점, IA-32e 모드에서는 디스크립터의 기준 주소와 세그먼트 크기 값에 상관없이 64GB 전체 영역으로 설정된다는 것, L 비트가 IA-32e 서브 모드 중 호환 모드 또는 64비트 모드를 선택.
| GDT: |
| ; 널(NULL) 디스크립터, 반드시 0으로 초기화해야 함 |
| NULLDescriptor: |
| dw 0x0000 |
| dw 0x0000 |
| db 0x00 |
| db 0x00 |
| db 0x00 |
| db 0x00 |
| |
| ; IA-32e 모드 커널용 코드 세그먼트 디스크립터 |
| IA_32eCODEDESCRIPTOR: |
| dw 0xFFFF ; Limit [15:0] |
| dw 0x0000 ; Base [15:0] |
| db 0x00 ; Base [23:16] |
| db 0x9A ; P=1, DPL=0, Code Segment, Execute/Read |
| db 0xAF ; G=1, D=0, L=1, Limit[19:16] |
| db 0x00 ; Base [31:24] |
| |
| ; IA-32e 모드 커널용 데이터 세그먼트 디스크립터 |
| IA_32eDATADESCRIPTOR: |
| dw 0xFFFF ; Limit [15:0] |
| dw 0x0000 ; Base [15:0] |
| db 0x00 ; Base [23:16] |
| db 0x92 ; P=1, DPL=0, Data Segment, Read/Write |
| db 0xAF ; G=1, D=0, L=1, Limit[19:16] |
| db 0x00 ; Base [31:24] |
- 편의상 기존 GDT의 NULL 디스크립터 바로 다음에 위치.
8.3 IA-32e 모드 전환과 1차 정리
8.3.1 물리 메모리 확장 기능 활성화와 페이지 테이블 설정
| |
| mov eax, cr4 |
| or eax, 0x20 |
| mov cr4, eax |
| |
| mov eax, 0x100000 |
| mov cr3, eax |
- PAE 비트를 1로 설정해서 물리 메모리 확장 기능 사용.
- cr3 레지스터에 PML4 테이블의 어드레스를 저장.
8.3.2 64비트 모드 활성화와 페이징 활성화

- IA-32e 모드를 활성화하는 실질적인 최종 관문 IA32_EFER 레지스터의 LME 비트를 1로 설정.
- IA32_EFER 레지스터를 세팅하지 않으면 IA-32 모드용 세그먼트 레지스터로 교체한다 해도 보호 모드로 동작.
- 해당 레지스터는 RDMSR 또는 WRMSR로 읽기 및 쓰기.
- ECX에 해당 레지스터의 주소를 넣으면 결과 상위 32비트는 EDX, 하위 32비트는 EAX에 저장.
- MSR 중 IA32_EFER은 0xC0000080에 위치.
| |
| mov ecx, 0xC0000080 |
| rdmsr |
| |
| or eax, 0x0100 |
| |
| wrmsr |
- LME비트(비트 8)를 1로 설정하여 IA-32e 모드 활성화.
8.3.3 IA-32e 모드로 전환과 세그먼트 셀렉터 초기화
| |
| mov eax, cr0 |
| or eax, 0xE0000000 |
| |
| xor eax, 0x60000000 |
| |
| mov cr0, eax |
| |
| jmp 0x08:0x200000 |
| |
- 앞 정리에서 본 PCD 비트와 PWT 비트는 페이징을 활성화했을 때만 유효.
- x86 계열의 프로세서에는 페이지의 캐시 설정보다 우선시되는 캐시 비트가 있음.
- 두 비트를 모두 0으로 설정해야 페이지 캐시 사용 가능.
8.3.4 소스코드 1차 정리와 실행

8.4 IA-32e 모드용 커널 준비
8.4.1 커널 엔트리 포인트 파일 생성
| [BITS 64] |
| |
| SECTION .text |
| |
| |
| extern Main |
| |
| START: |
| mov ax, 0x10 |
| mov ds, ax |
| mov es, ax |
| mov fs, ax |
| mov gs, ax |
| |
| |
| mov ss, ax |
| mov rsp, 0x6FFFF8 |
| mov rbp, 0x6FFFF8 |
| |
| call Main |
| |
| jmp $ |
- IA-32e 모드 커널 엔트리 포인트의 역할은 보호 모드의 엔트리 포인트와 비슷.
- 차이점이라면 보호 모드 커널에 IA-32e 모드로 전환하는 모든 코드가 포함되여 IA-32e 모드의 커널 엔트리 포인트는 단순히 세그먼트 레지스터를 교체하고 C 커널 호출.
- extern 지시어로 외부 Main 함수 사용 가능.
C 언어 엔트리 포인트 파일 생성
- C 언어 엔트리 포인트는 보호 모드를 그래도 복사해도 상관없이 작동.
링크 스크립트 파일 생성
링커 스크립트 파일 코드 보기
- IA-32e 커널 역시 보호 모드 커널과 마찬가지로 라이브러리를 사용하지 않도록 빌드.
- 또 0x200000 어드레스로 복사되어 실행 될 것.
makefile 생성
IA-32e makefile 코드 보기
- 커널 엔트리 포인트와 C 언어 커널 엔트리 포인트가 개별적으로 빌드되어 합쳐지는 형태가 아님.
- IA-32e 모드 커널의 커널 엔트리 포인트 파일은 오브젝트 파일의 형태로 컴파일 되어 C 언어 커널과 함께 링크.
- 따라서 보호 모드 기반으로 사용하되, 커널 엔트리 포인트 파일이 링크 목록의 가장 앞에 위치.
8.5 보호 모드 커널과 IA-32e 모드 커널 통합
8.5.1 최상위 makefile 수정
- 최상위 makefile에 Kernel64를 추가.
8.5.2 부트 로더 파일 수정
8.5.3 이미지 메이커 프로그램 수정
이미지 메이커 코드 보기
- 이미지 메이커의 매개 변수의 개수 변경.
- 총 섹터 수와 보호 모드 커널 섹터수를 같이 기록하게 변경.
8.5.4 보호 모드 커널의 C 언어 엔트리 포인트 파일 수정
Main() 함수 수정
코드 보기
8.5.5 빌드와 실행

마치며
끝!