[64비트 멀티코어 OS 원리와 구조 1] - 목차 기준으로 흐름 요약
3장. 64비트 프로세서의 이모저모
< 3장의 목적 >
- x86-64 프로세서는 호환성을 위해 16비트 모드부터 부팅 시작.
- 따라서 16비트, 32비트를 거쳐 64비트 모드로 전환이 필요.
- 각 모드 별 레지스터의 의미와 메모리 접근 방식이 다르므로, 상세 사항을 알지 못하면 전환을 할 수 X.
▶ 개괄 : 1) 운영모드에는 어떤 것이 있으며, 각 모드마다 2) 레지스터 의미가 무엇인지, 그리고 3) 메모리 관리가 어떻게 다른지 알아본다.
4장. 내 PC를 부팅하자!
< 4장의 목적 >
- OS의 진입점(entry point)인 부트로더(boot loader)가 어떻게 시작되는지 아는 것이 4장의 목적.
- x86 프로세서와 OS의 진입점 사이에는 BIOS가 있다.
- BIOS가 부트로더를 어떻게 실행하는지 모르면 부트로더를 작성할 수 없으므로 4장의 내용은 필수.
▶ 개괄 : x86 프로세서가 시작된 후 실행 흐름은 BIOS로 넘어간다. 1) BIOS가 부팅을 위해 어떤 역할을 수행하는지 알아보고, 2) 부트로더를 어떻게 찾고 실행하는지 알아보자. 그리고 이 정보를 토대로 3) 간단한 부트로더 코드를 작성한다. 여기선 비디오 메모리를 사용해 간단한 문자열을 출력한다.
5장. 플로피 디스크에서 OS 이미지를 부팅하자!
< 5장의 목적 >
- 부트로더의 역할인 'OS 이미지를 메모리에 로드하고 엔트리 포인트로 점프'하는 코드 구현.
- OS를 코딩하지 않았으므로 여기서 사용하는 이미지는 더미 OS.
▶ 개괄 : 1) BIOS API를 이용해 특정 섹터 수(변수에 저장)만큼 메모리에 로드하는 기능을 구현하고, 콘솔에 메시지를 출력하는 기존의 코드를 함수로 전환하기 위해 2) 스택 설정과 호출 규약 개념에 대해 알아본다. 마지막으로 3) 1024섹터 크기의 더미 OS를 임시로 만들어 부트로더를 완성한다.
6장. 32비트 보호모드로 전환하자!
< 6장 목적 >
- 64비트로 전환하기 위해서는 32비트 보호모드를 거쳐야 한다.
- 6장의 최종 목적은 1섹터 크기의 32비트 커널 엔트리 포인트 코드를 작성하는 것.
- 필요한 자료구조를 준비하고 보호모드로 전환 후 메시지를 출력하는 결과.
▶ 개괄 : OS 이미지의 첫 섹터에는 32비트 보호모드 커널의 엔트리 포인트가 있다. 여기서 보호모드로 전환해야 한다. 가장 먼저 자료구조를 준비하기 위해 진입점 코드의 끝부분에 1) 커널/데이터 세그먼트 디스크립터 데이터를 작성하고, 2) GDT를 작성한다. 그리고 실제로 전환하기 위해 3) 보호모드 전환 수행 코드(GDTR과 CR0 설정 및 CS 교체를 위한 jmp)를 작성한다. 보호모드 진입 후, 4) 세그먼트 설렉터 레지스터들을 설정하고 보호모드용 메시지 출력 함수를 설계해 완료 메시지를 출력한다.
7장. C언어로 커널을 빌드하자!
< 7장 목적 >
- 32비트 OS 이미지는 [엔트리 포인트], [실제 커널]로 구성된다.
- 실제 커널은 C언어로 작성할 것이다.
- 따라서 C언어 소스코드를 32비트 커널 위치에 빌드하는 세팅법을 소개하는 것이 7장의 내용.
▶ 개괄 : 보호모드 커널의 C언어 빌드 환경을 조성하고, 이미지 메이커로 OS 이미지 빌드를 자동화하자.
● C언어로 작성된 커널을 알맞게 빌드하기 위해서는 다음 조건을 지켜야 한다.
1) 표준 라이브러리 사용을 피해야 하고,
2) 특정 위치(0x10200)에 위치해야 하고,
3) 파일 포맷이 순수한 바이너리여야 한다.
각각을 해결하기 위해
1) 라이브러리를 사용하지 않도록 컴파일러 옵션을 설정한다.
2) 링커 스크립트를 설정한다.
3) objcopy.exe 프로그램으로 실행 파일 포맷을 순수한 바이너리로 만든다.
● 마지막으로 메시지를 출력하는 간단한 C언어 커널을 작성하고 빌드한다. 보호모드 엔트리 포인트에는 C언어 커널로 jmp하는 명령어를 추가한다.
● 유틸리티로 이미지 메이커 프로그램을 사용한다. 부트로더, 엔트리 포인트, C언어 커널 등 각종 파일을 섹터 단위로 하나의 OS 이미지에 통합하고, 부트로더의 섹터 크기 변수를 조정한다.
8장. A20 게이트를 활성화하자!
< 8장 목적 >
- 64비트 모드 전환 전, 메모리와 관련하여 아래의 요소를 체크해야 한다.
(체크 안 하면 64비트 커널을 실행할 수 없을수도..)
1) 1MB 영역 이상에 접근할 수 있는가?
2) 메모리 크기가 최소한 64MB 이상은 되는가?
- 위 두 가지를 만족하면 64비트 커널이 사용하는 공간(1MB ~ 6MB)을 초기화한다.
▶ 개괄 : x86 프로세서는 리얼모드 하위 호환성을 위해 A20 라인의 활성화를 옵션으로 제공한다. 따라서 1) 보호모드 엔트리 포인트에서 A20을 활성화(BIOS API or 시스템 컨트롤 포트)하고, 2) 최소 요구 메모리 크기(64MB)를 만족하는지 32비트 커널에서 체크한다. 이후 덩치가 큰 64비트 커널과 데이터 영역을 위해 3) 특정 메모리 영역(1MB ~ 6MB)를 초기화한다. (※ 이전에 로드된 64비트 커널을 2MB에 복사하는 코드는 10장에서..)
9장. 페이징 기능을 활성화해 64비트 전환을 준비하자!
< 9장 목적 >
- 64비트 모드는 페이징을 사용하므로 페이징을 위한 테이블 자료구조를 메모리에 준비한다.
▶ 개괄 : 1) 4단계 페이징을 사용할 것이다. 2MB 페이지로 64GB 메모리를 매핑한다. PML4 테이블, 페이지 디렉터리 포인터 테이블, 페이지 디렉터리 테이블을 생성해야 하며 2) 엔트리의 각 필드를 소개한다. 이들의 메모리 위치는 1MB ~. 이러한 정보를 토대로 3) [Page.c] 파일에 엔트리 필드 설정 함수와 페이지 테이블을 메모리에 생성하는 함수를 작성한다. (※ 아직 페이지 자료구조만 메모리에 생성했을 뿐, 프로세서로 페이징 기능을 활성화한 것은 아니다.)
10장. 64비트 모드로 전환하자!
< 10장 목적 >
- 64비트 모드로 전환한다.
- 전환 후 간단한 동작을 위해 64비트 커널 엔트리 포인트 파일과 main()을 준비해 이미지에 통합한다.
▶ 개괄 : CPUID 명령어로 프로세서가 1) 64비트를 지원해주는지 검사하는 함수를 작성한다. 64비트에서도 코드/데이터 디스크립터는 있어야 하므로 2) 64비트용 디스크립터를 보호모드 엔트리포인트에 추가해준다. 64비트 커널 이미지를 1MB~ 영역에 복사하는 함수를 작성한다. 이를 위해 부트로더와 이미지 메이커를 수정한다. 부트로더에 보호모드 커널 크기 변수를 추가해 복사 함수가 쉽게 64비트 커널의 시작주소를 찾는 것.. 4) 64비트 모드로 전환하는 어셈블리어 함수를 작성한다. 이 함수는 모드 전환 후 2MB 영역으로 jmp한다. 5) 64비트 커널의 엔트리 포인트 코드와 C언어 main() 함수를 작성한다. 빌드를 위해 링커 스크립트를 수정한다.
11장. 키보드 디바이스 드라이버를 추가하자!
< 11장 목적 >
- 키보드에서 scan 코드를 읽어서 데이터의 종류에 따라 알맞은 처리를 하는 키보드 드라이버를 작성한다.
▶ 개괄 : 1) 키보드 컨트롤러의 하드웨어적 구성을 알아본다. 저레벨 수준에서 2) x86의 IN, OUT 명령어를 활용하는 C언어 함수를 작성한다. 이를 이용해 3) 키보드와 키보드 컨트롤러를 활성화하는 함수를 작성한다. 다양하고 복잡한 함수들이 있지만, ASCII 코드로 변환하거나 ctrl/shift..와 같은 제어 키를 관리하거나 LED를 활성화하는 등, 키보드에서 scan 코드를 읽어서 종류에 따라 처리하는 것이 핵심. 64비트 main() 커널에서 키보드를 활성화한 후 4) 간단한 셸 코드를 작성한다.
※ 셸에서 while 문으로 매번 키보드 컨트롤러에 직접 접근해 키를 읽는 폴링 방식이라 인터럽트로 전환 필요.
12장. GDT, IDT 테이블, TSS 세그먼트를 등록해 인터럽트에 대비하자!
< 12장 목적 >
- 인터럽트 시스템을 위해 필요한
▶ 개괄 :