너무 많은 시간을 블로그에 신경을 못썼다. 바쁘다는 핑계도 있고 어쩌면 OS를 만들다가 지쳐 버린 것인지도 모르겠다. 내일 해야지.. 하고 미뤘던게 벌써 몇달이 지나 버렸다. ㅎㅎㅎ

지난 포스트 읽고 다시 복습중….



초기 바람OS는 부트로더에서 바로 커널을 호출 하는 방법을 사용하려고 했었다. OS 이미지 파일에서 부트로더 다음 섹터를 읽는 방법으로 커널을 호출했다. 여기에서 좀 더 확장해서 파일 시스템(FAT32)를 추가해서 부트로더가 KERNEL.SYS 파일을 찾아서 로딩 하는 형태로 구현하려고 했다. 그런데 부트로더의 512바이트 한계에서 파일시스템에서 KERNEL.SYS를 찾아서 로딩 하는 구현부까지는 무리라고 생각되었다. 또 보호모드 진입을 위한 처리를 하기 위해선 부트로더와 커널간 이 처리를 해줘야 하는 무언가가 필요하다고 판단하여 OS_LDR.SYS 라는 커널 로더를 추가 하기로 했다.

부트로더->커널 로더->커널

자… 커널 로더에서 보호 모드를 진입하고 커널을 읽어 주면 된다.

말만 쉬웠다.

보호 모드 진입이 이렇게 어려울 줄은 몰랐다. 또한 커널을 Visual Studio 툴로 개발 하려고 한 난 , 컴파일이 된 생성 파일이 PE포맷 이라는 사실에 좌절하게 되었다. 그래서 부랴 부랴 보호모드와 PE 포맷을 열공 했다. (~~라고 쓰고 그냥 이론서들을 읽는 수준…)

우선, 커널 로더에서 보호 모드로 진입을 해 봐야 겠다.

그전에, 커널 로더와 커널이 메모리 상에 어디에 적재 되는지 정의부터 해야 겠다.

기존 포스트를 보니

부트로더가 0x7C00에 적재되고 FAT 관련 TEMP 데이터는 0x9000을 이용했다. 그리고 커널로더를 0x8000에 저장해 놓은 형태이다. 그러면 음.. 커널을 0x100000 에 놓으면 될까??

구분 메모리 주소
부트로더 0x7C00
커널 로더 0x8000
커널 0x10000

이렇게 대충(?) 성의 없고 계획없는 정의는 해 놓고 커널 로더를 작성하잣!!!!!!!

 

 

- 보호 모드 진입

이제 보호모드 진입하자!!~~ 보호 모드 진입은 CR0 레지스터를 이용하면 진입 할 수 있다고 한다. 흠 CR0 레지스터 넌 또 뭐하는 놈이냐!!!

CR0 레지스터 : 32비트 레지스터로 운영 모드를 변경, 제어하는 컨트롤 레지스터 (X86 프로세서에는 CR0~CR4 등의 5개의 컨트롤 레지스터가 있으며 그중 하나)

Bit 설명 엑세스
31 페이징 활성화 R/W
30 캐시 비활성화 R/W
29 NOT-WriteThrough  이라는데 모르겠음. R/W
28~19 예약  
18 Alignment Mask 몰라. R/W
17 예약  
16 Write Protect (몰라) R/W
15~6 예약  
5 Numeric Error R/W
4 Extension Type R
3 Task Switched R/W
2 Emulation R/W
1 Monitor Coprocessor R/W
0 Protection Enabled (보호 모드 활성화) R/W

요렇게 구성된다는 데 아직 다~~~ 모르겠다. 다만, 0비트 Protection Enabled 여기를 1로 바꾸면 보호 모드로 진입한다는데… 쉽네?

or CR0, 1 하면 되나???

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

CR0은 컨트롤 레지스터로 요케 하면 안되고!! 다른 레지스터를 이용해서 하라는 삽질이 필요하단다 ㅋㅋ

mov EAX, CR0 ; CR0 을 eax 레지스터로 옮기고!
or EAX, 1         ; EAX 값에다가 1을 OR 연산 시킨다.
mov CR0, EAX ; 다시 CR0으로 복사.

 

그러면 한번 해볼까???

;; creator : cyj (mangg@manggong.org)
;; date : 2014. 3. 27
;; desc : 커널 로더.
;;        1. 보호모드로 진입한다.

org 0x8000
bits 16

OS_LDR_ENTRY:

	cli
	;; CS, DS, ES, & SS을 초기화 하자.
	xor eax, eax
	mov ds, ax
	mov ss, ax
	mov es, ax
	mov sp, 0xFFFF
	mov bp, sp
	sti

	mov EAX, CR0
	or  EAX, 1
	mov CR0, EAX

자, 이걸 가상 이미지 파일에 넣고!!!

 

짜잔~~~~~~~!!!

무한 재부팅이다! ㅋㅋㅋㅋ

아~ 재미없어…..

왜 안되지???

아~~~~~~~

GDT 테이블을 우선 GDT 레지스터에 등록해야 한다고 한다.

그럼 GDT 테이블 먼저 만들어야 겠네?.. 지난 포스트(http://manggong.org/41)에서 GDT레지스터에 테이블 위치 넘기는 부분에 대해서 간단히 언급한적이 있다.

struct
{
   unsigned short GDT_SIZE;
   unsigned int GDT_ADDRESS;
} GDT_INFO

LGDT GDT_INFO

GDT_INFO는 GDT 테이블의 크기와 GDT 테이블의 주소를 가지고 있는 구조체.

그러면 GDT_INFO는 이렇게 어셈으로 코딩하고…

LGDT GDT_INFO

GDT_INFO:

GDTSIZE      dw     GDT테이블 크기
GDTADDRESS   dd     GDT_TABLE

GDT 테이블만 만들어 주면 되겠네… GDT 테이블은 GDT 디스크립터 모음이라고 한다고 하니 디스크립터 배열인듯 하다. 전 포스트에 열심히 표로 그려놓은 디스크립터….

os11-1

8바이트로 구성 된 이상한 구조체…

막상 값을 넣으려고 봤더니 뭘 넣어 줘야 할지 막막하다…

그냥 예전에 설명 해 놓은 글을 참고로

Segment Limit(0:15)라는 놈은 세그먼트 크기를 나타낸다고 하고 4GB까지 가능하다고 하니 다 0xFFFF

Base Address(15:0) 라는 놈은 세그먼트 시작 주소라고 하니 음…음…뭘 넣어주지?? 커널 로더가 0x8000에서 시작하니까 그냥 세그먼트 시작 주소를 0x8000 넣어주면 되나??? 음… 아 모르겠다.. 그냥 0x0

Base Address(23:16) 이놈도 그냥 모르겠으니 0x0

Type : 이놈… 코드 세그먼트 인지 데이터 세그먼트 인지 접근 권한 뭐 이런거 있는데 이놈은 뭘 넣어줘야 할지 모르겠다. 실행되고 읽기 지원 하는 놈을 선택했다.(잘한것 같다. 검색해보니 이렇게 하더라 ㅡㅡ ) 0xA 그러고 보니 4비트군 ㅡㅡ;;

S : 이놈 모르겠다…시스템 디스크립터면 0, 걍 디스크립터(코드,데이터) 구분자면 1이라면 넌 그냥 1

DPL : 커널과 유저 접근권한 .. 그래도 너도 0이다.

P : 유효??? 하면 1 유효하지 않으면 0  …넌…넌… 너 모르는데…너도 그냥 1 모르겠다. 그냥 1주자…

그래서 Type,S,DPL, P 요넘들이 1바이트 이니까 전체 설정한 값을 보면 0x9A가 된다.

Segment Limit(19:16) 요놈..그냥 너도 0xF

AVL: 임의의 용도?? 그럼 0

L 요건 64비트면 1 32비트면 0 이라니 난 32비트 0

D/B 이것도 난 32비트 1

G 이건 내가 아까 4G까지 Segment Limit를 쓴다고 했으니 1로 설정해야 한다.

그러면 이것들(AVL, L, D/B, G,SegmentLimit) 조합해보면 11001111 = 0xCF

Base Address(31:24) 이것도 그냥 0

휴~~~ 값 넣기 힘들다…

그럼 다시 정리 하면

Segment Limit : 0xFFFF
Base Address(0~15) : 0x0000
Base Address(15~23) : 0x00
Type,S,DPL,P : 0x9A
Segment Limit, AVL, L, D/B, G : 0xCF
Base Address(24~32) : 0x00

S값을 1로 줬기 때문에 이 디스크립터는 코드 디스크립터 이고 실행과 읽기만 가능하다.

주소는 0~4G까지이다.

코드 디스크립터 이므로 CS 세그먼트에서 참조될 꺼다…..CS 외에 DS,ES 세그먼트에서 사용할 데이터 디스크립터도 만들다…

다 똑같고..

Segment Limit : 0xFFFF
Base Address(0~15) : 0x0000
Base Address(15~23) : 0x00
Type,S,DPL,P : 0x92
Segment Limit, AVL, L, D/B, G : 0xCF
Base Address(24~32) : 0x00

Type만 데이터 디스크립터임을 나타내고 읽고/쓰기 가능으로 바꿨다.

자~~ 이제 GDT 테이블을 다 완성했닷!!!!!!!!!!!!!

그러면..

GDT_TABLE:
                  dw 0xFFFF    ; Code
                  dw 0x0000
                  db  0x00
                  db  0x9A
                  db  0xCF
                  db  0x00
                  dw 0xFFFF    ; Data
                  dw 0x0000
                  db  0x00
                  db  0x9A
                  db  0xCF
                  db  0x00
GDT_TABLE_END

이렇게 GDT_TABLE은 구성될수 있겄다. 하지만… GDT_TABLE 구조체 첫번째 인덱스는 모두 NULL로 채워라고 했다.. (이게 이제 기억이 나네 ..흠…) 그래서 0으로 채우고 전체 소스는

;; creator : cyj (mangg@manggong.org)
;; date : 2014. 3. 27
;; desc : 커널 로더.
;;        1. 보호모드로 진입한다.

org 0x8000
bits 16

OS_LDR_ENTRY:

	;; CS, DS, ES, & SS을 초기화 하자.
	xor eax, eax
	mov ds, ax
	mov ss, ax
	mov es, ax
	mov sp, 0xFFFF
	mov bp, sp
	
    ; GDT 등록
    xor     eax, eax
    lgdt    [GDT_INFO]  
    cli
    mov     eax, cr0
    or      eax, 1
    mov     cr0, eax
	
	jmp		$    ; 무한루프.
	

GDT_INFO:

GDTSIZE      dw     GDT_TABLE_END-GDT_TABLE-1
GDTADDRESS   dd     GDT_TABLE

GDT_TABLE:
			dw 0x0000 ; null
			dw 0x0000
			db 0x00
			db 0x00
			db 0x00
			db 0x00
            dw 0xFFFF    ; Code
            dw 0x0000
            db 0x00
            db 0x9A
            db 0xCF
            db 0x00
            dw 0xFFFF    ; Data
            dw 0x0000
            db 0x00
            db 0x9A
            db 0xCF
            db 0x00
GDT_TABLE_END:

잘 되나 한번 확인!!!

반응이 없다…되었는지 안되었는지 모르겠지만 재부팅은 안된다..ㅎㅎㅎ

보호모드가 성공하면 화면에 출력하는 루틴을 넣어야 겠다.

어랏 , 보호모드에서는 BIOS 인터럽트를 호출 못하네?? 이제까지 int 0x10으로 처리했는데 안된단다.. 그럼 어떻게?? 흠… 전 포스트에서 리얼모드 설명하면 1M 가 다 예약 되어 있다는 글을 쓴적이 있다. 거기에 비디오 메모리 라고 해서.. 컬러, 흑백 주소가 있다고 했는데 거기 0xB8000 컬러 텍스트 비디모 메모리 영역에다가 알파벳 쓰면 출력된다고 한다. 그래?? 쉽겠군 .. 메모리 번지에 아스키 값만 던져주면 되니..

음….

32비트 보호모드로 바꿔 주고 나서 해줘야 하는게 있단다.

CPU 파이프 라인 때문에 mov cr0, eax 다음 명령어를 의미 없는 코드로 바꿔란다.. nop 요걸로..

왜?? CPU가 명령어를 받을 때 mov cr0, eax 명령 처리 한다음 다음 명령어를 읽는게 아니고 미리 명령어를 읽어 두기 때문에.. 그래서 보통

jmp $+2
nop
nop

요렇게 쓴단다.. 뭐 별수 있나.. 남들이 하니 해야지..

그리고 이제까지 리얼 모드에서 돌아가는 코드니깐 [bits 16] 이렇게 컴파일러 한테 알려줬는데 보호모드로 들어가면 32비트 이므로 [bits 32] 라고 해줘야 한단다.. 뭐 그까짓거 하면 되지 ~~~

그리고 제일 중요한데… 주소 어드레싱이 보호모드로 가면 틀려진단다..

리얼모드 일 때는 그냥 선형 주소라서 그냥 쓰면 되는데.. 이제 세그먼트(CS,DS,ES등등) 라고 부른던 놈을 셀렉터 라고 부르고 그 셀렉터는 위에서 정의한 GDT 테이블의 디스크립터를 가리킨다고 한다. 그럼 어떻게 가리킬까 생각 해 보다 GDT 테이블이 배열이고 그 배열 인덱스 번호를???

mov ax, 0x01
mov cs, ax

요렇게 말이지… ㅎㅎ 비슷한데 0x01이 아니란다…

os11-3

요게 셀렉터 모습인데 Index가 3~15비트 부터 이다.. 그래 0x01은 RPL 0비트에 1이라고 설정하는 것 뿐이다. 그러므로 GDT 테이블의 코드 디스크립터를 가리키려면

mov ax, 0x08
mov cs, ax

라고 위와 같이 해줘야 한단다..

os13-1

그리고 주소공간에는 위 그림과 같이 세그먼테이션 작업을 거치고 페이징 작업을 거치면 물리 주소 공간에 읽고/쓴다는데.. ㅎㅎㅎ 복잡하네..

그러니까 세그먼트 셀렉터는 GDT 테이블의 디스크립터를 가르키고 디스크립터는 선형 주소 공간에서 Base Address를 결정하고 다시 여기에서 Offset 만큼 떨어진 위치를 가르킨다. 맞나?? 맞나?? 맞겠지??

페이징은 패스… 아직 모르겠다.. 필요 할때 다시 찾아 봐야 할것 같다. 페이징은 다음에 하고 보호모드 진입하고 “OK” 단어 출력해 보는걸로 오늘은 마무리 해야겠다…

지친다…

;; creator : cyj (mangg@manggong.org)
;; date : 2014. 3. 27
;; desc : 커널 로더.
;;        1. 보호모드로 진입한다.
;;        2. OK 문자 화면에 출력

[bits	16]
org 0x8000

OS_LDR_ENTRY:
	;; CS, DS, ES, & SS을 초기화 하자.
	xor eax, eax
	mov ds, ax
	mov ss, ax
	mov es, ax
	mov sp, 0xFFFF
	mov bp, sp

    ; GDT 등록
    xor     eax, eax
    lgdt    [GDT_INFO]  

    cli
    mov     eax, cr0
    or      eax, 1
    mov     cr0, eax

    jmp     $+2
    nop
    nop

    jmp     0x08:Entry32


GDT_INFO:
			dw     GDT_TABLE_END - GDT_TABLE - 1
		    dd     GDT_TABLE

GDT_TABLE:
			dw 0x0000 ; null
			dw 0x0000
			db 0x00
			db 0x00
			db 0x00
			db 0x00
            dw 0xFFFF    ; Code
            dw 0x0000
            db 0x00
            db 0x9A
            db 0xCF
            db 0x00
            dw 0xFFFF    ; Data
            dw 0x0000
            db 0x00
            db 0x92
            db 0xCF
            db 0x00
GDT_TABLE_END:

[bits 32]
Entry32:
	xor		eax, eax
    mov     ax, 0x10
	mov		ds, ax
	mov		es, ax
	mov		ss, ax
    xor     esp, esp
    mov     esp, 0xFFFF

    mov     BYTE [0xB8000], 'O'
    mov     BYTE [0xB8002], 'K'

 hang:
	jmp hang

대충 이렇게 만들어 주고…

되나???

오~~~~~~~~~~~~잘된다..

 

 

'프로그래밍 > OS 만들기' 카테고리의 다른 글

OS 만들기 #15 커널-키보드 입력  (0) 2015.02.09
OS 만들기 #14 PE 재배치  (0) 2014.03.28
OS 만들기 #12 - PE 파일 포맷  (0) 2013.09.10
OS 만들기 #11 - 리얼모드 & 보호모드  (0) 2013.08.14
OS 만들기 #10  (0) 2013.08.14

+ Recent posts