반응형

IA-32e 모드로 전환할 준비가 끝났으므로 본격적으로 IA-32e 모드 커널을 생성합니다.

 

IA-32e 모드 커널은 보호 모드 커널과 크게 다르지 않기에 보호 모드 커널과 같은 소스 파일 구조로 되어 있습니다.

 

그렇기에 보호 모드 커널의 소스 파일을 기반으로 02.Kernel64 디렉터리에 IA-32e 모드 커널의 소스 파일을 생성합니다.

 


 

→ 커널 엔트리 포인트 파일 생성

 

IA-32e 모드 커널 엔트리 포인트의 역할보호 모드의 커널 엔트리와 거의 유사하지만, 보호 모드 커널에 IA-32e 모드로 전환하는 코드가 포함되었으므로 IA-32e 모드 커널 엔트리 포인트는 단순히 세그먼트 레지스터를 교체하고 C 언어 커널 엔트리 포인트 함수를 호출하는 역할만 합니다.

 

이전 코드에서는 C 언어에서 어셈블리어 함수를 호출할 수 있게 global 지시어를 사용했지만 지금은 반대로 어셈블리어 함수에서 C 함수를 호출하기 위해서 해당 함수가 외부에 있다는 것을 알리는 extern 지시어를 사용합니다.

 

IA-32e 모드 커널의 C 언어 엔트리 포인트 파일 역시 보호 모드와 마찬가지로 Main() 함수를 엔트리 포인트로 사용할 것이므로 엔트리 포인트 파일의 상단에 extern Main과 같이 입력합니다.

 

아래는 IA-32e 모드 커널의 엔트리 포인트 소스 파일(02.Kernel64/Source/EntryPoint.s)을 나타낸 것입니다.

 

보호 모드 엔트리 포인트 코드와의 차이점으로는 C 언어 엔트리 포인트 함수인 Main() 함수를 Call 명령을 통해 호출하고, call 명령어 아래에서 무한 루프(jmp $)를 수행하도록 한 것입니다.

 

call 명령어 다음에 jmp 명령으로 무한 루프를 수행하고 있으므로, Main() 함수에서 굳이 무한 루프를 돌지 않아도 됩니다.

 

IA-32e 모드 커널의 엔트리 포인트 소스 코드(02.Kernel64/Source/EntryPoint.s)

 


 

→ C 언어 엔트리 포인트 파일 생성

 

C 언어 코드보호 모드든 IA-32e 모드든 큰 차이가 없으므로, 보호 모드의 C 언어 엔트리 포인트 파일 코드를 그대로 옮겨도 동작하는 데 문제가 없습니다.

 

다만, 보호 모드용 C 언어 엔트리 포인트 파일은 IA-32e 모드 전환을 위해 여러 함수들을 포함하고 있으므로 Main() 함수와 kPrintString() 함수만 정리하여 옮깁니다.

 

아래는 정리한 C 언어 엔트리 포인트 소스 파일(02.Kernel64/Source/Main.c)을 나타낸 것입니다.

 

IA-32e 모드 전환이 성공적으로 완료되면 그것을 화면에 표시하도록 했습니다.

 

IA-32e 모드 커널의 C언어 엔트리 포인트 소스 코드(02.Kernel64/Source/Main.c)

 

 

IA-32e 모드 커널용 타입 정의 파일(02.Kernel64/Source/Types.h)은 보호 모드의 파일과 같으므로, 보호 모드용 파일을 복사합니다.

 


 

→ 링크 스크립트 파일 생성

 

IA-32e 커널 역시 보호 모드 커널과 마찬가지로 라이브러리를 사용하지 않도록 빌드해야 하고, 0x200000(2MB) 주소로 복사되어 실행될 것이므로, 커널 이미지를 생성할 때 이를 고려해야 합니다.

 

링크 스크립트 파일/usr/lib/x86_64-linux-gnu/ldscripts(Windows : C:\cygwin\usr\cross\x86_64-pc-linux\lib\ldscripts) 디렉터리에 있으며, IA-32e 모드용 커널을 빌드해야 하므로 64bit 이미지에 관련된 elf_x86_64.x 파일을 기반으로 합니다.

 

보호 모드와 마찬가지로 .text, .data, .bss에 관련된 필수 섹션을 앞쪽으로 이동하고, .text 섹션의 시작 주소를 0x200000(2MB)로 변경하면 되며, 보호 모드와 마찬가지로 데이터 섹션의 시작을 섹터 단위로 맞추어 정렬함으로써 디버깅에 편리하도록 합니다.

 

아래는 섹션 배치 및 섹션 정렬이 적용된 링커 스크립트 파일이며 내용이 꽤 많으므로 직접 입력하기가 어렵다면 github.com/sean-baek/64bit_multicore_os/blob/main/02.Kernel64/elf_x86_64.x 파일을 복사해서 사용하면 됩니다.

 

섹션 배치와 크기 정렬을 위해 수정된 IA-32e 모드 커널용 링커 스크립트 1

 

섹션 배치와 크기 정렬을 위해 수정된 IA-32e 모드 커널용 링커 스크립트 2

 

섹션 배치와 크기 정렬을 위해 수정된 IA-32e 모드 커널용 링커 스크립트 3

 

섹션 배치와 크기 정렬을 위해 수정된 IA-32e 모드 커널용 링커 스크립트 4

 

섹션 배치와 크기 정렬을 위해 수정된 IA-32e 모드 커널용 링커 스크립트 5

 


 

→ makefile(02.Kernel64/makefile) 생성

 

보호 모드 커널과 달리 IA-32e 모드 커널은 커널 엔트리 포인트와 C 언어 커널 엔트리 포인트가 개별적으로 빌드되어 합쳐지는 형태가 아닙니다.

 

IA-32e 모드 커널의 커널 엔트리 포인트 파일은 오브젝트 파일의 형태로 컴파일되어 C 언어 커널과 함께 링크되기 때문에 보호 모드의 makefile을 기반으로 사용하되, C 언어 엔트리 포인트 파일이 아니라 커널 엔트리 포인트 파일이 링크 목록의 가장 앞에 위치하도록 수정해야 합니다.

 

수정해야 하는 또 다른 한 가지는 생성되는 이미지의 포맷과 관련된 부분입니다.

 

보호 모드 커널은 32bit이기 때문에 '-m32'나 '-f elf32'와 같이 32bit에 관련된 옵션을 사용했지만, IA-32e 모드는 64bit이기 때문에 그에 맞는 '-m64'와 '-f elf64' 옵션으로 변경해야 합니다.

 

아래는 보호 모드 makefile을 기반으로 변경 사항이 적용된 makefile을 나타낸 것입니다.

 

보호 모드 커널과 마찬가지로 elf 파일을 변환하여 바이너리 포맷의 Kernel64.bin 파일을 생성합니다.

 

IA-32e 모드 커널의 makefile(02.Kernel64/makefile) 1

 

IA-32e 모드 커널의 makefile(02.Kernel64/makefile) 2

 


 

→ 보호 모드 커널과 IA-32e 모드 커널 통합

 

IA-32e 모드로 전환하는 작업 및 IA-32e 모드용 커널 이미지 준비가 모두 완료되었으니, 이제 보호 모드 커널과 IA-32e 모드 커널을 통합하여 하나의 OS 이미지로 통합합니다.

 


 

→ 최상위 makefile(FS64 OS/makefile) 수정

 

IA-32e 모드 커널이 추가되었으니 최상위 makefile을 수정하여 02.Kernel64 디렉터리에서 make를 수행하도록 변경해야 합니다.

 

보호 모드 커널 디렉터리를 추가했던 방식과 같으며, 빌드 목록에 Kernel64를 추가하고 Kernel64의 커맨드 목록에 'make -C 02.Kernel64'를 추가하면 됩니다.

 

그리고 clean 작업을 수행할 때 IA-32e 모드 커널도 같이 처리할 수 있게 clean의 커맨드 목록에 'make -C 02.Kernel64 clean'을 추가합니다.

 

IA-32e 모드 커널 빌드가 추가된 최상위 디렉터리의 makefile 1

 

IA-32e 모드 커널 빌드가 추가된 최상위 디렉터리의 makefile 2

 

수정된 makefile에서 OS 이미지 파일을 생성하는 데 사용하는 ImageMaker 프로그램은 부트 로더 이미지 파일과 보호 모드 커널 이미지 파일 이 두 가지만 결합할 수 있습니다.

 

또한 IA-32e 모드 커널을 0x200000(2MB) 주소의 위치로 복사하려면 IA-32e 모드 커널에 대한 위치 정보가 필요하다는 문제도 있습니다.

 

이러한 문제를 해결하기 위해 ImageMaker 프로그램을 수정하여 IA-32e 모드 커널 파일을 입력으로 받아들이게 하고, 커널의 총 섹터 수 외에 보호 모드 커널의 섹터 수를 추가로 기록하도록 수정합니다.

 

그리고 보호 모드 커널은 부트 로더나 보호 모드 이미지에 기록된 정보를 이용하여 IA-32e 모드 커널을 0x200000 영역으로 이동시켜야 합니다.

 

아래에서 부트 로더와 ImageMaker 프로그램을 수정하여, IA-32e 모드 커널을 이동하는 데 필요한 정보를 추가합니다.

 


 

→ 부트 로더 파일 수정

 

먼저 부트 로더를 수정하여, 보호 모드 커널의 섹터 수를 위한 공간을 할당합니다.

 

부트 로더 영역에는 2byte 크기의 TOTALSECTORCOUNT가 있으며, ImageMaker 프로그램은 이 영역에 부트 로더를 제외한 나머지 영역의 섹터 수를 기록합니다.

 

그렇기에 부트 로더의 TOTALSECTORCOUNT 영역 이후에 2byte를 할당하여 보호 모드 커널 섹터 수를 저장하면, ImageMacker 프로그램에서 쉽게 찾을 수 있으며 보호 모드 커널에서도 쉽게 접근할 수 있습니다.

 

아래는 부트 로더 영역에 보호 모드 커널의 섹터 수를 저장하기 위한 공간을 추가한 코드입니다.

 

앞부분에 섹터 수를 저장할 공간을 추가한 것 외에는 이전의 부트 로더 코드와 완전히 같으므로 수정된 부분만 표시합니다.

 

수정된 부트 로더 파일(Bootloader.asm)

 


 

→ 이미지 메이커 프로그램 수정

 

ImageMaker 프로그램에서 수정해야 할 부분은 크게 두 가지입니다.

 

1. 파라미터를 전달받아서 각 함수를 호출하는 main() 함수

2. 생성된 OS 이미지에 부트 로더를 제외한 총 섹터 수를 기록하는 WriteKernelInformation() 함수

 

▶ main() 함수

 

수정해야 할 첫 번째 부분으로는 명령행으로 넘어온 인자(Argument)를 검사하는 부분입니다.

 

기존에는 부트 로더와 보호 모드 커널 밖에 없었지만, 지금은 IA-32e 모드 커널까지 추가되었으므로 3개 이상의 인자를 받아들이도록 수정합니다.

 

두 번째 부분으로는 IA-32e 모드 커널 이미지를 OS 이미지 파일에 쓰는 부분입니다.

 

이 부분은 보호 모드 커널 이미지를 쓸 때 만들어 놓은 함수가 있으므로, 이 함수를 활용하여 같은 방식으로 작성합니다.

 

마지막 부분커널 이미지에 정보를 기록하는 WriteKernelInformation() 함수와 이를 호출하는 부분입니다.

 

총 섹터 수와 보호 모드 커널 섹터 수를 같이 기록해야 하므로, 호출하는 부분은 두 파라미터를 넘겨주도록 수정해야 합니다.

 

 

▶ WriteKernelInformation()

 

수정할 부분파라미터로 넘겨받은 보호 모드 커널의 섹터 수를 OS 이미지 파일에 기록하는 부분입니다.

 

부트 로더 이미지에서 보호 모드 커널의 섹터 수 영역은 총 섹터 수 영역의 바로 이후에 위치하므로, 총 섹터 수 정보에 이어서 2byte를 기록하도록 수정합니다.

 

 

 

아래는 위의 수정사항이 반영된 ImageMaker 프로그램의 소스 파일입니다.

 

양이 꽤 많으므로 직접 입력하기 힘들다면 github.com/sean-baek/64bit_multicore_os/blob/main/04.Utility/00.ImageMaker/ImageMaker.c 파일을 참고하시면 됩니다.

 

보호 모드 커널의 섹터 수를 기록하는 기능이 추가된 ImageMaker 소스 코드(04.Utility/00.ImageMaker/ImageMaker.c) 1

 

보호 모드 커널의 섹터 수를 기록하는 기능이 추가된 ImageMaker 소스 코드(04.Utility/00.ImageMaker/ImageMaker.c) 2

 

보호 모드 커널의 섹터 수를 기록하는 기능이 추가된 ImageMaker 소스 코드(04.Utility/00.ImageMaker/ImageMaker.c) 3
보호 모드 커널의 섹터 수를 기록하는 기능이 추가된 ImageMaker 소스 코드(04.Utility/00.ImageMaker/ImageMaker.c) 4

 

보호 모드 커널의 섹터 수를 기록하는 기능이 추가된 ImageMaker 소스 코드(04.Utility/00.ImageMaker/ImageMaker.c) 5

 

위의 코드를 적용한 뒤 'gcc -o ImageMaker ImageMaker.c와 같이 입력하여 빌드하거나, 최상위 디렉토리에서 "make clean"를 한 후 "make"를 입력하여 빌드합니다.

 

gcc로 빌드했을 경우는 정상적으로 빌드가 끝나면 ImageMaker 파일이 생성될 것이며, 이것을 최상위 디렉터리로 복사하여 새로 생성된 ImageMaker 프로그램이 사용되도록 합니다.

 


 

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

 

보호 모드 커널의 C 언어 엔트리 포인트 코드를 수정해 보겠습니다.

 

C 언어 엔트리 포인트 코드를 수정하는 과정IA-32e 모드 지원 여부 판단하는 부분, IA-32e 모드 커널을 0x200000 주소로 복사하는 부분, 그리고 IA-32e 모드로 전환하는 코드를 호출하는 부분으로 나눌 수 있습니다.

 

IA-32e 모드의 지원 여부를 판단하는 방법은 앞에서 살펴보았으므로 커널을 0x200000 주소로 복사하는 부분과 모드 전환 코드를 호출하는 부분을 봐보겠습니다.

 

▶ IA-32e 모드 커널 이미지 복사

 

IA-32e 모드 커널 이미지를 0x200000 주소로 복사하려면 먼저 IA-32e 모드 커널의 시작 주소부터 알아야 합니다.

 

앞서 ImageMaker 프로그램과 부트 로더를 수정하여, 커널 영역의 총 섹터 수(부트 로더를 제외한 나머지 영역의 섹터 수)와 보호 모드 커널의 섹터 수를 저장했습니다.

 

OS 이미지 파일 내에서 IA-32e 모드 커널은 보호 모드 커널의 직후에 위치합니다.

 

그리고 부트 로더는 OS 이미지 파일의 내용 그대로 0x10000(64Kbyte)에 옮겨주므로 이를 통해 IA-32e 모드 커널의 시작 주소와 크기를 계산할 수 있습니다.

 

커널의 총 섹터 수가 7이고 보호 모드 커널의 섹터 수가 4라면, IA-32e 모드 커널의 크기는 3섹터이며 시작 주소는 0x10000에서 4 섹터만큼 떨어진 (0x10000 + 512byte x 4)이 됩니다.

 

아래는 이러한 과정을 그림으로 나타낸 것입니다.

 

IA-32e 모드 커널 이미지의 시작 주소와 크기 계산 과정

 

부트 로더 영역에 있는 총 커널 수 정보와 보호 모드 커널 수 정보는 간단한 포인터 연산으로 접근할 수 있습니다.

 

이미 ImageMaker 프로그램을 통해 부트 로더의 시작 주소에서 5byte 떨어진 위치에 TOTALSECTORCOUNT가 존재한다는 것을 알고, 2byte 더 떨어진 영역에 KERNEL32SECTORCOUNT가 위치하므로, 실제로 두 값이 위치하는 주소는 부트 로더의 시작 주소에 각각 0X5 및 0x7을 더한 0x7C05와 0x7C07이 됩니다.

 

이제 IA-32e 모드 커널의 시작 주소 및 섹터 수를 모두 구했으므로, 남은 작업은 시작 주소부터 섹터 수만큼을 0x200000 주소에 복사하는 것입니다.

 

IA-32e 모드 커널을 이동시키는 코드는 아래 코드의 kCopyKernel64ImageTo2MB() 함수에 나와있으니 예제 코드를 참고합니다.

 

 

▶ Main() 함수 수정

 

IA-32e 모드로 전환하기 위한 과정들 중에 힘들고 어려운 과정은 모두 끝나고, 마지막으로 지금까지 만들었던 함수들은 호출하는 과정만 남았습니다.

 

단지 보호 모드 커널의 C 언어 엔트리 포인트 함수 뒤쪽에 함수 호출 코드를 몇 줄 추가하고, 그 성공 여부를 화면에 표시하기만 하면 됩니다.

 

아래는 모든 사항이 적용된 보호 모드 커널의 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

 

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

 


 

→ 빌드와 실행

 

지금까지의 과정을 잘 따라왔다면, make 입력하여 빌드했을 때 별다른 이상 없이 OS 이미지 파일(Disk.img)이 생성될 것입니다.

 

새로 빌드된 이미지로 QEMU를 실행하면 아래 사진과 같이 표시될 것입니다.

 

1차 정리 결과와 큰 차이는 없지만 "Switch To IA-32e Mode Success~!!" 메시지가 표시되고 그 아래에 "IA-32e C Language Kernel Start..."가 표시된 것으로 보아 성공적으로 IA-32e로 전환했다는 것을 알 수 있습니다.

 

sudo qemu-system-x86_64 -m 64 -fda ./Disk.img -rtc base=localtime -M pc

 

반응형

+ Recent posts