본문 바로가기
OS

[32bit] 보호 모드로 진입하기

by 올리고당 2021. 7. 27.

목차

 


보호 모드로 진입하기

에러 없이 보호 모드로 진입하기 위해선, 다음과 같은 작업이 필요합니다.

 

  1.  제어를 통해 막을 수 있는 인터럽트는 모두 막는다.
  2.  GDTR에 GDT 정보를 로드한다.
  3.  CR0의 최하위 비트인 PE flag를 1로 만들어 CPU를 보호 모드로 바꾼다.
  4.  CR0를 바꾼 후, far jump를 반드시 실행한다. [각주:1]
  5.  세그먼트 레지스터가 리얼 모드에서 썼던 값을 가지고 있기 때문에, far jump로 바뀐 CS를 제외한 나머지 세그먼트 레지스터의 값을 보호 모드에 맞게 바꾼다.

1 ~ 4번 과정을 부트로더에서, 5번 과정을 커널에서 처리하도록 하겠습니다.

 

boot.asm 파일을 다음과 같이 수정해주세요.

( line 1~ 36 )는 이전 코드와 동일합니다.

 

boot.asm


boot.asm 코드 설명

( line 37 )

cli : 인터럽트 플래그를 0으로 만들기. ( clear interrupt flag )

인터럽트 플래그가 0이면, CPU는 막을 수 있는 외부 인터럽트를 모두 무시합니다.

 

( line 39, 50 ~ 52 )

lgdt [ gdtr ] : 메모리의 gdtr이 가리키는 번지에서부터 48비트를 GDTR에 로드해라. ( load GDT )

GDTR의 LSB에 gdtr이 가리키는 번지가, MSB에 48번째 비트가 들어갑니다. gdtr이 가리키는 번지부터 2바이트만큼 GDT의 크기가 저장되어 있고, 그 이후 번지부터 4바이트만큼 GDT의 시작 주소가 메모리에 담겨있습니다. GDT의 크기는 GDT의 맨 처음 번지를 가리키는 gdt포인터와 GDT의 마지막의 다음 번지를 가리키는 gdt_end포인터를 뺀 값으로 구할 수 있습니다.

 

dd 오퍼랜드 : 해당 메모리 위치에 4바이트( double word )크기로 오퍼랜드를 기록함.

 

( line 41 ~ 43 )

cr0는 CPU를 제어하는 컨트롤 레지스터입니다. cr0의 LSB를 PE비트라고도 합니다. PE비트가 1이면 CPU가 보호 모드로 동작하고, 0이면 리얼 모드로 동작합니다.

 

( line 45 )

SysCodeSelector : 0x10000 : 해당 세그먼트 셀렉터가 가리키는 디스크립터가 가지고 있는 세그먼트 시작 주소와 오프셋을 더해 물리 주소를 계산합니다.

 

( line 55 ~ 82 )

GDT입니다. 0번째 디스크립터로 항상 Null 디스크립터가 있어야합니다. 임의의 디스크립터를 테이블에서 가져올 때, 제일 처음 저장되어 있는 값이 세그먼트 디스크립터의 LSB로, 마지막에 저장되어 있는 값이 MSB로 들어갑니다.


보호 모드에서 동작하는 커널

 

kernel.asm

( line 14 )

( 전체 열의 갯수 ) * 2 * 써야할 행 번호 + 2 * 써야할 열 번호

전체 열의 갯수는 80개입니다.

화면의 한 글자는 문자 1바이트, 색표현 1바이트로 총 2바이트입니다.

 


이번 글에서는 보호 모드로 진입하는 방법에 대해 알아보았습니다.

다음 글에서는 복잡하고 길어진 코드를 좀 더 간소화하는 방법에 대해 알아보겠습니다.

감사합니다.

 


 

  1. 매뉴얼 vol.3 9-17 페이지에 "The JMP or CALL instruction immediately after the MOV CR0 instruction changes the flow of execution and serializes the processor"라고 나와 있습니다. 매뉴얼에서 정확히 언급된 부분을 찾기 어렵습니다만, 참고 도서 저자의 추측대로, 파이프 라인을 비워줘야 하는 건은 맞다고 생각합니다. 다만, 저자는 nop을 2개 삽입하는 과정을 통해 파이프 라인을 비워준다고 저술하였지만, 20년 전의 CPU 파이프라인 단계를 생각해 보아도, 10단계가 넘으므로 잘못된 방식으로 생각됩니다. 제 추측으로는 far jump를 하는 과정에서 파이프라인을 CPU가 스스로 비우는 것 같습니다. [본문으로]