보호 모드 (protected mode) 변환 및 구축
리얼모드에 비해서 오백배는 어려운거 같은 보호모드를 구축해보자.
1. 보호모드란?
- 사용자가 OS의 메모리 공간을 막 다룰 수 있는 리얼모드(16bit)와는 다르게
- 자체적으로 메모리 보호 기능을 가지고 있는 모드(32bit)
- 현재 상용화된 대부분의 OS가 보호모드에 해당됨
왜 굳이 보호모드를 사용해야 하는가?
- 리얼 모드는 램의 물리주소를 1MB 까지 밖에 사용할 수 없음. -> 하이메모리는 커녕 로우메모리도 못 채우겠다.
- 사용자가 OS 영역을 침범하는게 불가능하니까 민감한 데이터를 건드릴 수 없으므로, 훨씬 안정성 있다.
- 복잡했던 리얼 모드의 세그먼트:오프셋 계산 방식에서 탈피할 수 있다(세그먼트:오프셋을 안 쓴다는게 아님) -> 프로그래머가 세그먼트 시작 주소 지정 가능
- EAX, EBX ... 와 같은 32bit 확장 레지스터를 사용할 수 있다.
- 잘못된 기계어 명령 필터링 가능
ㅎㅎ. 여기서부터 헬인듯
2. GDT (Global Descriptor Table)
세그먼트 영역에 대한 데이터를 일정한 디스크립터 형식으로 기술, 이를 테이블 형태로 모아둔 것
Segment Limit - 세그먼트의 크기/한계수치, 이후 속성 중 G비트에 따라 한계점이 달라짐
Base Address - 세그먼트의 물리 주소, 하이메모리와 로우메모리 나누어 적재
속성 필드 | 내용 |
P (Present) | 세그먼트와 메모리 존재 여부 |
DPL (Descriptor Privilege Level) | 커널 레벨인가 유저 레벨인가 (0:커널, 3:유저) |
S | (0:SS, 1:CS or DS) |
Type | 권한 설정 |
G | 세그먼트 단위 설정 (0: 바이트 단위, 1:4KB 단위) |
D (Default Operation Size) | (0:16bit 세그먼트, 1:32bit 세그먼트) |
0 | 별 의미 x. 그냥 0 |
AVL (Available and reserved bits) | 시스템 소프트웨어에 의해 사용되는 구간 |
코드로 구현하자면 다음과 같다.
위처럼 단축할 수 있다.
그럼 이제 gdt를 구현했으니 이걸 사용하겠다고 시스템에 전달만 하면 된다.
lgdt[gdtr]
LGDT : gdt의 베이스 어드레스와 세그먼트리미트 값을 인자값으로 전달받은 후, gdtr 레지스터에 파라미터로 전달
GDTR : gdt를 받아들이는 레지스터
다음과 같이 lgdt에 파라미터로 전달할 gdtr 코드를 짜준다.
유의 - 이 gdtr은 gdtr레지스터 그 자체의 코드가 아니라, gdtr에 인자를 전달하는 역할을 하는 ldgt에 데이터를 넘기기 위해 이름만 gdtr인 포인터를 구현한 것일 뿐이다.
3. 보호모드의 주소 지정 방법
먼저 세그먼트 레지스터의 구성에 대해 알아보자.
Visible Part : 세그먼트 셀렉터로, GDT를 가리키는 포인터라고 할 수 있겠다. (사용자가 확인할 수 있는 부분)
Hidden Part : Visible Part가 가리키는 대상, GDT 그 자체가 저장되는 부분이다.
세그먼트 셀렉터의 구성이다.
Index : 상위 13비트, 디스크립터를 찾기 위한 인덱스
TI (Table Indicator) : 0이면 GDT, 1이면 LDT
RPL(Requested Privilege Level) : 특권 권한으로, 0에서 3까지 가질 수 있다. 0이면 가장 강력한 레벨로 사용자가 커널에 접근할 수 있는 것이며, 3이면 user 레벨로 디스크립터 DPL이 0(커널 레벨) 이라면 접근할 수 없는 상태를 의미한다.
셀렉터는 다음과 같이 적용시킬 수 있다.
; Boot.asm
[org 0]
[bits 16]
jmp 0x07C0:start
start:
mov ax, cs
mov ds, ax
mov es, ax
mov ax, 0xB800
mov es, ax
mov byte[es:0], 'h'
mov byte[es:1], 0x09
mov byte[es:2], 'i'
mov byte[es:3], 0x09
read:
mov ax, 0x1000
mov es, ax
mov bx, 0 ; 0x1000:0000 주소로 읽어 => 물리주소 0x10000
mov ah, 2 ; 디스크에 있는 데이터를 es:bx의 주소로
mov al, 1 ; 1섹터를 읽을 것이다
mov ch, 0 ; 0번째 실린더
mov cl, 2 ; 2번째 섹터부터 읽기 시작한다
mov dh, 0 ; 헤드는 0
mov dl, 0 ; 플로피 디스크 읽기
int 13h
jc read ; 에러나면 다시
mov dx, 0x3F2 ;플로피디스크 드라이브의
xor al, al ; 모터를 끈다
out dx, al
cli
lgdt[gdtr]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp $+2
nop
nop
mov bx, DataSegment
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
mov ss, bx
jmp dword CodeSegment:0x10000
gdtr:
dw gdt_end - gdt - 1
dd gdt+0x7C00
gdt:
dd 0,0 ; NULL 세그
CodeSegment equ 0x08
dd 0x0000FFFF, 0x00CF9A00 ; 코드 세그
DataSegment equ 0x10
dd 0x0000FFFF, 0x00CF9200 ; 데이터 세그
VideoSegment equ 0x18
dd 0x8000FFFF, 0x0040920B ; 비디오 세그
gdt_end:
times 510-($-$$) db 0
dw 0xAA55
;Sector2.asm
CodeSegment equ 0x08
DataSegment equ 0x10
VideoSegment equ 0x18
[org 0x10000]
[bits 32]
mov ax, VideoSegment
mov es, ax
mov byte[es:0x08], 'P'
mov byte[es:0x09], 0x09
jmp $
times 512-($-$$) db 0
대략적으로 과정을 설명하자면,
- Boot.asm에서 hi를 찍는다.
- 하드디스크 읽기를 통해 섹터 2를 0x10000에 적재
- 16비트 -> 32비트로 전환
- Sector2.asm으로 점프
- P 출력
위 코드를 새로 컴파일한 img 파일들을 합쳐 하나의 disk.img 파일로 만들어준다.
이 파일을 가상머신에 넣어주고 실행시키면
위와 같이 뜨면 잘 된 것이다.
이렇게 리얼모드에서 보호모드로 변환을 마쳤다.
참고 URL
[OS 개발 9] 32비트 커널 로더(3) - GDT의 개념과 적용
1. GDT의 개념 앞서 포스팅을 마치면서 잠깐 GDT에 대해 언급하였다. 16비트 리얼 모드에서 32비트 보호 모드로 운영 모드를 바꿔야 하며, 이를 위해 GDT를 사용한다고 말이다. GDT가 뭘까? 풀어쓰자면 글로벌 디..
itguava.tistory.com
[OS만들기 5강] 보호모드(Protected Mode)
https://github.com/SIMHANGSUB/HoGoS/tree/master/55강 소스코드입니다. 이제 16비트 리얼모...
blog.naver.com