본문 바로가기
  • _^**_
무근본 IT 지식 공유/무근본 운영체제(OS)

[무근본 OS 만들기] 현재까지의 과정(부트로더-> 커널 엔트리 포인트-> C언어. Kernel32)

by 크리드로얄워터 2023. 3. 26.
반응형

 ▶ 현재 디렉토리 구성

 

1. 부트로더(00. BootLoader)

▶ 디렉토리 구성

 

1-1. BootLoader.asm

- .text 섹션.

- BIOS가 부트로더를 찾아 0x7C00 번지에 올려줌.

- 코드 세그먼트, 데이터 세그먼트를 0x07C0 으로 초기화.

- 스택을 0x0000:0000~0x0000:FFFF 영역에 64KB 크기로 생성

 

 

- 부트 로더를 제외한 MINT64 OS 이미지 크기의 섹터를 정의한다.

- BIOS 인터럽트 콜을 사용해 디스크에서 OS 이미지를 로딩한다.

   + http://cotkdrl1.blog.me/10148806030 참조.

  0 번 트랙, 0번 헤드, 2번 섹터에서부터 시작하여

  이후에 존재하는 모든 데이터를 메모리로 읽어온다.

  메모리에 올리는.. 그 위치는 0x10000.

- 읽어오는 과정이 끝나면, 로딩한 OS 이미지를 실행한다.

 

 0x10000번지에 이미 올려져 있으므로 그리로 점프하여 실행.

=> Build시 MINT64/Kernel32/Makefile에서 EntryPoint를 앞쪽에(0x10000) 배치시켜 놓았으므로 EntryPoint.bin부터 실행된다.

 

 

1-2. 00.BootLoader/makefile

- BootLoader.asm을 컴파일하여 바이너리 파일(BootLoader.bin)로 만든다. (00.BootLoader/BootLoader.bin)

- MINT64 폴더의 makefile에서는(MINT64/makefile) 이를(

00.BootLoader/makefile)

실행한다.

 

 

2. 커널(01. Kernel32)

▶ 디렉토리 구성

 

2-1. EntryPoint.s

- 보호 모드 엔트리 포인트의 시작 어드레스(0x10000)를 세그먼트 레지스터(cs, ds) 값으로 변환한다.

- GDT 테이블을 구성하고 GDTR 자료구조를 프로세서에 설정하여 GDT 테이블을 로드한다.

- CR0 컨트롤 레지스터에 값을 설정한다.

 => GDT를 등록하고 CR0 레지스터를 설정하여 스위치를 켜준다.( 보호모드로 넘어간다. ) 

 

 

- 세그먼트 셀렉터의 값을 0x08로 변경.

   셀렉터의 값이 0x08 이라는 것은

   8은 0000000000001000

   이므로 TI=0 GDT,

   RPL = 0, Index=1 이므로

  GDT 테이블에서 첫번째 디스크립터가 가리키는

  기준주소(Base address) + 0x10000 으로 점프함.

 

  

-> 데이터 세그먼트 디스크립터를 가리키도록 셀렉터들을 설정.

-> 스택을 64KB 크기로 생성

- >보호모드 엔트리 이후에 C로 작성한 커널 코드가 존재하므로(보호모드 엔트리포인트는 512바이트이다).. 0x10200으로 점프!

=> 결국 엔트리 포인트의 역할은 32비트 보호모드로 전환하고 C 커널 코드로 점프하는 일!

밑 부분에는 한 섹터 크기로 만들기 위한 코드가 존재.

=>보호모드 엔트리포인트는 512바이트이다

 

2-2. Main.c

- 일단 Main.c 의 구성은 간단하다. 메인함수가 있고, 글자를 출력하는 kPrintString 함수가 선언 돼 있다.

- 문제는 C언어로 작성된 이것을 어떻게 우리의 운영체제에서 이해할 수 있도록 하냐는 것이다.

   이런 걱정을 하는 이유는

   첫째, 나는 윈도우7 기반에서 gcc를 사용해 이 C 코드를 컴파일할 것이기 때문인데,

   컴파일 결과로 나온 바이너리 코드는 일단 윈도우에서만 이해할 수 있는 바이너리 코드로 구성된다.

   무슨 얘기냐면 일반적으로 C언어를 컴파일하여 실행 파일을 생성하면 ELF 파일 포맷이나 PE 파일 포맷과 같이 특정 OS에서만 실행 가능한 포맷으로 실행파일이 구성이 된다는 것

   이다. 

(어셈블리어로 코드를 구성할 때에는 이런 걱정을 하지 않았다. 왜냐면 어셈블리어로 짜서 컴파일한 바이너리 코드는 Intel CPU를 가진 어떤 컴퓨터에서든 인식이 가능하기 때문이다.)

   둘째, C 코드를 컴파일 하면 보통 라이브러리가 실행 파일에 딸려 들어오게 된다. 그러나 우리의 운영체제에서 부팅된 후 보호 모드 커널이 실행되면 C라이브러리가 없으므로 라이

  브러리에 포함된 함수를 호출할 수 없다.

   셋째, 0x10200 위치에서 실행되게끔 빌드를 해줘야 한다. 0x10000의 위치에는 한 섹터 크기의 보호 모드 엔트리 포인트(01.Kernel32/Source/EntryPoint.s)가 존재한다.

  그러므로 결합된 C코드는 512바이트 이후인 0x10200에 위치하게 된다. 따라서 C로 작성한 커널 부분은 빌드할 때 0x10200 위치에서 실행되는 것을 전제로 해야 하며,

  해당 위치의 코드는 C 코드 중에 가장 먼저 실행되어야 하는 함수(엔트리포인트)가 위치해야 한다.

  ☞ 커널이 실행되는 어드레스가 중요한 이유

  : 선형 주소를 참조하게 생성된 코드나 데이터 때문.  C언어에서 전역 변수의 어드레스나 함수의 어드레스를 참조하는 경우, 실제로 존재하는 선형 주소로 변환된다. 따라서 메모리에 로딩되는 어드레스가 변한다면, 이러한 값들 역시 변경해줘야 정상적으로 동작한다.

 

글이 길어졌다. 다음으로 넘기겠다.

 

반응형

댓글