2부. 64비트 세상으로 점프

6장. 32비트 보호 모드로 전환하자

세그먼트 디스크립터

  • 세그먼트 디스크립터 (Segment Descriptor) : 세그먼테이션 기법에서 세드먼트의 정보를 나타내는 구조
  • 세그먼트 (Segment) : 메모리 공간을 임의의 크기로 나눈 영역
  • 세그먼트 디스크립터 종류

    • 코드 세그먼트 디스크립터
      • 실행 가능한 코드가 포함된 세그먼트에 대한 정보
      • CS 세그먼트 셀렉터 사용
    • 데이터 세그먼트 디스크립터
      • 데이터가 포함된 세그먼트에 대한 정보
      • CS 세그먼트 셀렉터를 제외한 나머지 셀렉터 사용

코드 세그먼트 디스크립터와 데이터 세그먼트 디스크립터 타입 설정

  • 비트 8
    • 접근 여부 비트
    • 프로세서에서 설정하는 비트로 프로세서는 해당 디스크립터가 참조될 때마다 비트 8을 1로 설정
  • 비트 10
    • 역방향 확장 비트 (데이터 세그먼트 타입인 경우)
      • 상위 어드레스에서 하위 어드레스로 자라는 스택을 위한 옵션
      • 세그먼트의 크기를 동적으로 확장 / 축소할 목적으로 사용
    • 접근 승인 비트 (코드 세그먼트 타입인 경우)
      • 권한에 관계없이 해당 코드 세그먼트에 접근할 수 있음을 나타냄

세그먼트 디스크립터의 구조와 메모리 공간, 어셈블리어 코드의 관계

GDT

  • GDT

    • Global Descriptor Table
    • 연속된 디스크립터의 집합
    • 널 티스크립터(NULL Descriptor)를 가장 앞부분에 추가해야 함

  • LDT

    • Local Descriptor Table
    • GDT와 비슷한 역할을 하는 테이블
    • GDT 크기 부족 문제 해결

보호 모드로 전환

  1. GDTR 레지스터 설정
  2. CR0 컨트롤 레지스터 설정
  3. jmp 명령 수행

CR0 컨트롤 레지스터

  • 외부 메모리의 내용과 캐시의 내용을 일치시키는 동기화 정책
    • Write-through 방식
      • 메모리에 쓰기를 수행할 때마다 캐시의 내용과 외부 메모리의 내용을 모두 갱신
    • Write-back 방식
      • 쓴 내용을 캐시에만 갱신하고 외부 메모리에 쓰는 작업을 최대한 뒤로 미룸
      • 캐시의 내용을 버릴 때 캐시의 내용을 외부 메모리에 갱신
    • 속도 면에서 Write-back 방식이 유리하지만 메모리 맵 I/O 방식(메모리 어드레스에 읽고 쓰는 데이터를 디바이스로 송수신하는 방식)을 사용하는 디바이스에는 부적절함(외부 메모리에 업데이트가 바로 되지 않아 문제 발생)

보호 모드 커널의 엔트리 포인트 소스 코드(EntryPoint.s)

  [ORG 0x00]
  [BITS 16]

  SECTION .text

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; 코드 영역
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  START:
      mov ax, 0x1000 ; 보호 모드 엔트리 포인트의 시작 어드레스(0x10000)를 세그먼트 레지스터 값으로 변환
      mov ds, ax
      mov es, ax

      cli ; 인터럽트가 발생하지 못하도록 설정
      lgdt [GDTR] ; GDTR 자료구조를 프로세서에 설정하여 GDT 테이블을 로드

      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ; 보호 모드로 진입
      ; Disable Paging, Disable Cache, Internel FPU, Disable Align Check, Enable ProtectedMode
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

      mov eax, 0x4000003B ; PG=0, CD=1, NW=0, AM=0, WP=0, NE=1, ET=1, TS=1, EM=0, MP=1, PE=1
      mov cr0, eax ; CR0 컨트롤 레지스터에 위에서 저장한 플래그를 설정하여 보호 모드로 전환

      ; 커널 코드 세그먼트를 0x00을 기준으로 하는 것으로 교체하고 EIP의 값을 0x00을 기준으로 재설정
      ; CS 세그먼트 셀렉터 : EIP
      jmp dword 0x08:(PROTECTEDMODE - $$ + 0x10000)

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; 보호 모드로 진입
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  [BITS 32]
  PROTECTEDMODE:
      mov ax, 0x10 ; 보호 모드 커널용 데이터 세그먼트 디스크립터를 AX 레지스터에 저장
      mov ds, ax
      mov es, ax
      mov fs, ax
      mov gs, ax

      ; 스택을 0x00000000 ~ 0x0000FFFF 영역에 64KB 크기로 생성
      mov ss, ax
      mov esp, 0xFFFE
      mov ebp, 0xFFFE

      ; 화면에 보호 모드로 전환되었다는 메시지를 찍는다.
      push (SWITCHSUCCESSMESSAGE - $$ + 0x10000)
      push 2 ; 화면 Y 좌표(2)
      push 0 ; 화면 X 좌표(0)
      call PRINTMESSAGE
      add esp, 12

      jmp $

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; 함수 코드 영역
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; 메시지를 출력하는 함수
  ; 스택에 x 좌표, y 좌표, 문자열
  PRINTMESSAGE:
      push ebp
      mov ebp, esp
      push esi
      push edi
      push eax
      push ecx
      push edx

      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ; X, Y의 좌표로 비디오 메모리의 어드레스를 계산함
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ; Y 좌표
      mov eax, dword [ebp + 12]
      mov esi, 160
      mul esi
      mov edi, eax

      ; X 좌표
      mov eax, dword [ebp + 8]
      mov esi, 2
      mul esi
      add edi, eax

      ; 출력할 문자열의 어드레스
      mov esi, dword [ebp + 16]

  .MESSAGELOOP:
      mov cl, byte [esi]

      cmp cl, 0
      je .MESSAGEEND

      mov byte [edi + 0xB8000], cl

      add esi, 1
      add edi, 2

      jmp .MESSAGELOOP

  .MESSAGEEND:
      pop edx
      pop ecx
      pop eax
      pop edi
      pop esi
      pop ebp
      ret

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; 데이터 영역
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ; 아래의 데이터를 8바이트에 맞춰 정렬하기 위해 추가
  align 8, db 0

  ; GDTR의 끝을 8byte로 정렬하기 위해 추가
  dw 0x0000

  ; GDTR 자료구조 정의
  GDTR:
      dw GDTEND - GDT - 1 ; GDT 테이블의 전체크기
       dd (GDT - $$ + 0x10000) ; GDT 테이블의 시작 어드레스

  ; GDT 테이블 정의
  GDT:
    ; 널 디스크립터, 반드시 0으로 초기화해야 함
      NULLDESCRIPTOR:
          dw 0x0000
          dw 0x0000
          db 0x00
          db 0x00
          db 0x00
          db 0x00

      ; 보호 모드 커널용 코드 세그먼트 디스크립터
      CODEDESCRIPTOR:
          dw 0xFFFF ; Limit[15:00]
          dw 0x0000 ; Base[15:00]
          db 0x00 ; Base[23:16]
          db 0x9A ; P=1, DPL=0, Code Segment, Execute/Read
          db 0xCF ; G=1, D=1, L=0, Limit[19:16]
          db 0x00 ; Base[31:24]

      ; 보호 모드 커널용 데이터 세그먼트 디스크립터
      DATAEDESCRIPTOR:
          dw 0xFFFF ; Limit[15:00]
          dw 0x0000 ; Base[15:00]
          db 0x00 ; Base[23:16]
          db 0x92 ; P=1, DPL=0, Data Segment, Read/Write
          db 0xCF ; G=1, D=1, L=0, Limit[19:16]
          db 0x00 ; Base[31:24]
  GDTEND:

  SWITCHSUCCESSMESSAGE: db 'Switch To Protected Mode Success~!!', 0

  times 512 - ($ - $$) db 0x00

01.Kernel32 디렉터리의 makefile

  all: Kernel32.bin

  Kernel32.bin: Source/EntryPoint.s
      nasm -o Kernel32.bin $<

  clean:
      rm -f Kernel32.bin
  • $< : Dependency(:의 오른쪽)의 첫 번째 파일을 의미하는 메크로

Wonix 디렉터리의 makefile 수정 부분

  Disk.img: 00.BootLoader/BootLoader.bin 01.Kernel32/Kernel32.bin
      @echo
      @echo ============== Disk Image Build Start ==============
      @echo

      cat $^ > Disk.img

      @echo
      @echo ============== All Build Complete ==============
      @echo
  • $^ : Dependency(:의 오른쪽)에 나열된 전체 파일을 의미하는 메크로

보호 모드 전환된 결과

보호 모드 커널 이미지의 크기가 512바이트(1섹터)이므로 부트로더 BootLoader.asm의 TOTALSECTORCOUNT를 다음과 같이 수정해야 한다.

  TOTALSECTORCOUNT: dw 1

results matching ""

    No results matching ""