>

- A20 게이트 

과거 8086 CPU는 어드레스 라인이 20개 뿐이라서 최상위 1비트는 표현되지 않는다. 

80286이 도입되면서 CPU는 Protected Mode 라는 기능을 가지게 되고 어드레스 라인도 24개로 늘어났다. 하지만 여전히 8086 소프트웨어와 호환될 필요가 있었기 때문에 Real Mode라는 방식을 두어 8086과 호환이 가능하게 했고 8086에서도 1MB 이상의 주소를 지정하기 위해 20번 어드레스를 키보드 컨트롤러 칩인 8042의 하나의 핀과 AND 게이트로 묶었다.

이러한 방법은 Pentium 4 까지 오면서도 사용되고 있다. 즉 A20 게이트를 켜준다는 말의 의미는 키보드 컨트롤러의 특정 핀을 1로 세트해주는 것이라고 할 수 있다.

A20 게이트를 키면 1MB 이상의 주소 지정도 무리없이 할 수 있다.


- 페이징

세그멘테이션 기법과 더불어 페이징 기법을 사용하면 한 프로그램이 4GB 의 메모리 영역을 사용하게 할 수 있다.

논리주소 -> 선형주소 -> 물리주소

이런 식으로 물리주소에 매핑이 되고 선형주소를 분해하여 물리주소를 찾아 매핑하는 기법이 페이징이다.



CR3 레지스터는 페이지 디렉토리의 주소를 갖고 있다. 

선형주소의 상위 10비트는 페이지 디렉토리 엔트리의 오프셋을 나타낸다.

중간 10비트는 페이지 테이블에서의 페이지 테이블 엔트리 오프셋을 나타낸다.

하위 12비트는 해당 페이지의 오프셋 즉 물리 주소의 오프셋을 나타낸다.

CR3레지스터에 있는 페이지 디렉토리 주소와 디렉토리 엔트리 주소를 참조하여 각 페이지 테이블을 찾고 마찬가지로 페이지 테이블과 테이블 엔트리 주소를 참조하여 해당 페이지를 찾는다. 그 후에 그 페이지에 오프셋을 더하여 물리 주소에 접근하게 된다.



* 이미지 출처 : http://viralpatel.net/taj/tutorial/paging.php

페이지 디렉토리는 시스템 상에 하나이며 1024개의 페이지 디렉토리 엔트리를 갖는다.

페이지 디렉토리 엔트리는 페이지 테이블의 주소를 10비트로 갖고있다. (나머지는 Flags)

페이지 테이블 또한 1024개의 페이지 테이블 엔트리를 갖으며 페이지 주소를 10비트로 갖는다. (나머지는 Flags)

각 페이지는 4kb 크기이며 1024 * 1024 * 4096 = 4294967296 즉, 4GB 의 메모리를 가상으로 사용할 수 있게 한다.

'Study > Kenrel' 카테고리의 다른 글

여러개의 Task 일때 Task Switching  (0) 2015.10.01
유저모드 Task Switching  (0) 2015.10.01
Protected Mode에서의 보호  (0) 2015.09.30
Task Switching in Kernel  (0) 2015.09.25
PIC 초기화  (0) 2015.09.24
Posted by Mungsul
,

x86 어셈블리어 분기

Memo 2015. 10. 2. 00:36

행여나 내가 어셈블리어와 멀리 떨어져있어서 어셈 분기 구문을 헷갈려할 때 다시 기억을 되살리기 위한 용도로 메모를 해둔다.


jxx operand1 operand2 라고 했을 때, Intel에서는 operand1이 operand2보다 xx할때 이고

AT&T에서는 그 반대이다.


e : equal

n : not

a : above (unsigned)

g : greater (signed)

b : below (unsigned)

l : less (signed)

z : zero => if zero 라서 equal과 같은 역할을 한다.

Posted by Mungsul
,

%include "init.inc"


[org 0x10000]

[bits 16]


start:

cld

mov ax, cs

mov ds, ax

xor ax, ax

mov ss, ax


xor eax, eax

lea eax, [tss]

add eax, 0x10000

mov [descriptor4+2], ax

shr eax, 16

mov [descriptor4+4], al

mov [descriptor4+7], ah ; TSS 디스크립터 설정


xor eax, eax

lea eax, [printf]

add eax, 0x10000

mov [descriptor7], ax

shr eax, 16

mov [descriptor7+6], al

mov [descriptor7+7], ah ; 콜게이트 설정


cli

lgdt[gdtr] ; GDT 등록


mov eax, cr0

or eax, 0x00000001

mov cr0, eax


jmp $+2

nop

nop


jmp dword SysCodeSelector:PM_Start ; 간다 Protected Mode


[bits 32]

times 80 dd 0 ; 스택 영역


PM_Start:

mov bx, SysDataSelector

mov ds, bx

mov es, bx

mov fs, bx

mov gs, bx

mov ss, bx


lea esp, [PM_Start]


cld

mov ax, SysDataSelector

mov es, ax

xor eax, eax

xor ecx, ecx

mov ax, 256 ; IDT 영역에 256개의 빈 디스크립터 복사

mov edi, 0


loop_idt:

lea esi, [idt_ignore]

mov cx, 8 ; 디스크립터 하나가 8바이트이다.

rep movsb

dec ax

jnz loop_idt


mov edi, 8*0x20 ; 타이머 IDT 디스크립터 복사

lea esi, [idt_timer]

mov cx, 8

rep movsb


mov edi, 8*0x21 ; 키보드 IDT 디스크립터 복사

lea esi, [idt_keyboard]

mov cx, 8

rep movsb


mov edi, 8*0x80 ; 트랩 IDT 디스크립터(소프트웨어 인터럽트)

lea esi, [idt_soft_int]

mov cx, 8

rep movsb


lidt [idtr] ; IDT 등록


mov al, 0xfc ; 막아두었던 인터럽트 중(부트스트랩에서 막아둠)

out 0x21, al ; 타이머와 키보드만 유효하게

sti


mov ax, TSSSelector

ltr ax


mov eax, [CurrentTask] ; Task Struct 리스트를 만든다.

add eax, TaskList

lea edx, [User1regs]

mov [eax], edx

add eax, 4

lea edx, [User2regs]

mov [eax], edx

add eax, 4

lea edx, [User3regs]

mov [eax], edx

add eax, 4

lea edx, [User4regs]

mov [eax], edx

add eax, 4

lea edx, [User5regs]

mov [eax], edx


mov eax, [CurrentTask] ; 첫번째 Task 선택

add eax, TaskList

mov ebx, [eax]

jmp sched


scheduler:

lea esi, [esp]


xor eax, eax

mov eax, [CurrentTask]

add eax, TaskList


mov edi, [eax]


mov ecx, 17

rep movsd

add esp, 68


add dword [CurrentTask], 4

mov eax, [NumTask]

mov ebx, [CurrentTask]

cmp eax, ebx

jne yet

mov byte [CurrentTask], 0


yet:

xor eax, eax

mov eax, [CurrentTask]

add eax, TaskList

mov ebx, [eax]


sched:

mov [tss_esp0], esp ; 커널 영역의 스택 주소를 TSS에 기입


lea esp, [ebx] ; EBX에는 다음 태스크의 정보가 있다.


popad ; 레지스터 복원

pop ds

pop es

pop fs

pop gs ; 세그먼트들 복원


iret ; 다음 Task로 분기


CurrentTask dd 0 ; 현재 실행 중인 태스크 번호

NumTask dd 20 ; 모든 태스크의 수

TaskList: times 5 dd 0 ; 각 태스크 저장 영역의 포인터 배열


; subroutines


printf:

push eax

push es

mov ax, VideoSelector

mov es, ax


printf_loop:

mov al, byte [esi]

mov byte [es:edi],al

inc edi

mov byte [es:edi], 0x06

inc esi

inc edi

or al, al

jz printf_end

jmp printf_loop


printf_end:

pop es

pop eax

ret


;UserProcess routine


user_process1:

mov eax, 80*2*2+2*5

lea ebx, [msg_user_process1_1]

int 0x80

mov eax, 80*2*3+2*5

lea ebx, [msg_user_process1_2]

int 0x80

inc byte [msg_user_process1_2]

jmp user_process1


msg_user_process1_1 db "User Process", 0

msg_user_process1_2 db ".I'm running now", 0


user_process2:

mov eax, 80*2*2+2*35

lea ebx, [msg_user_process2_1]

int 0x80

mov eax, 80*2*3+2*35

lea ebx, [msg_user_process2_2]

int 0x80

inc byte [msg_user_process2_2]

jmp user_process2


msg_user_process2_1 db "User Process2", 0

msg_user_process2_2 db ".I'm running now", 0


user_process3:

mov eax, 80*2*5+2*5

lea ebx, [msg_user_process3_1]

int 0x80

mov eax, 80*2*6+2*5

lea ebx, [msg_user_process3_2]

int 0x80

inc byte [msg_user_process3_2]

jmp user_process3


msg_user_process3_1 db "User Process3", 0

msg_user_process3_2 db ".I'm running now", 0


user_process4:

mov eax,80*2*5+2*35

lea ebx, [msg_user_process4_1]

int 0x80

mov eax, 80*2*6+2*35

lea ebx, [msg_user_process4_2]

int 0x80

inc byte [msg_user_process4_2]

jmp user_process4


msg_user_process4_1 db "User Process4", 0

msg_user_process4_2 db ".I'm running now", 0


user_process5:

mov eax, 80*2*9+2*5

lea ebx, [msg_user_process5_1]

int 0x80

mov eax, 80*2*10+2*5

lea ebx, [msg_user_process5_2]

int 0x80

inc byte [msg_user_process5_2]

jmp user_process5


msg_user_process5_1 db "User Process5", 0

msg_user_process5_2 db ".I'm running now", 0


;data area


gdtr:

dw gdt_end-gdt-1

dd gdt


gdt:

dd 0, 0

dd 0x0000ffff, 0x00cf9a00

dd 0x0000ffff, 0x00cf9200

dd 0x8000ffff, 0x0040920b


descriptor4: ; TSS 디스크립터

dw 104

dw 0

db 0

db 0x89

db 0

db 0

dd 0x0000ffff, 0x00fcfa00 ; 유저 코드 세그먼트

dd 0x0000ffff, 0x00fcf200 ; 유저 데이터 세그먼트


descriptor7: ; 콜게이트 디스크립터

dw 0

dw SysCodeSelector

db 0x02

db 0xec

db 0

db 0

gdt_end:


tss:

dw 0,0 ; 이전 태스크로의 Back Link


tss_esp0:

dd 0 ; ESP0

dw SysDataSelector, 0 ; SS0, 사용안함

dd 0 ; ESP1

dw 0, 0 ; SS1, 사용안함

dd 0 ; ESP2

dw 0, 0 SS2, 사용안함

dd 0


tss_eip:

dd 0, 0 ; EIP, EFLAGS

dd 0, 0, 0, 0


tss_esp:

dd 0, 0, 0, 0 ; ESP, EBP, ESI, EDI

dw 0, 0 ; ES, 사용안함

dw 0, 0 ; CS,사용 안함

dw 0, 0 ; SS,사용 안함

dw 0, 0 ; DS, 사용 안함

dw 0, 0 ; FS, 사용 안함

dw 0, 0 ; GS, 사용 안함

dw 0, 0 ; LDT, 사용 안함

dw 0, 0 ; 디버그용 T비트 IO 허가 비트맵

;; TSS 구조 참고

; user1 task structure


times 63 dd 0 ; 유저 스택 영역

User1Stack:

User1regs:

dd 0, 0, 0, 0, 0, 0, 0, 0 ; EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX


dw UserDataSelector, 0 ; DS

dw UserDataSelector, 0 ; ES

dw UserDataSelector, 0 ; FS

dw UserDataSelector, 0 ; GS


dd user_process1 ; EIP

dw UserCodeSelector, 0 ; CS

dd 0x200 ; EFLAGS

dd User1Stack ; ESP 

dw UserDataSelector, 0 ; SS


; user2 task structure


times 63 dd 0

User2Stack:

User2regs:

dd 0, 0, 0, 0, 0, 0, 0, 0


dw UserDataSelector, 0

dw UserDataSelector, 0

dw UserDataSelector, 0

dw UserDataSelector, 0


dd user_process2

dw UserCodeSelector, 0

dd 0x200

dd User2Stack

dw UserDataSelector, 0


; user3 task structure


times 63 dd 0

User3Stack:

User3regs:

dd 0, 0, 0, 0, 0, 0, 0, 0


dw UserDataSelector, 0

dw UserDataSelector, 0

dw UserDataSelector, 0

dw UserDataSelector, 0


dd user_process3

dw UserCodeSelector, 0

dd 0x200

dd User3Stack

dw UserDataSelector, 0


; user4 task structure


times 63 dd 0

User4Stack:

User4regs:

dd 0, 0, 0, 0, 0, 0, 0, 0


dw UserDataSelector, 0

dw UserDataSelector, 0

dw UserDataSelector, 0

dw UserDataSelector, 0


dd user_process4

dw UserCodeSelector, 0

dd 0x200

dd User4Stack

dw UserDataSelector, 0


; user5 task structure


times 63 dd 0

User5Stack:

User5regs:

dd 0, 0, 0, 0, 0, 0, 0, 0


dw UserDataSelector, 0

dw UserDataSelector, 0

dw UserDataSelector, 0

dw UserDataSelector, 0


dd user_process5

dw UserCodeSelector, 0

dd 0x200

dd User5Stack

dw UserDataSelector, 0


idtr:

dw 256*8-1

dd 0


;interrupt service routines


isr_ignore:

push gs

push fs

push es

push ds

pushad


mov ax, SysDataSelector

mov DS, ax

mov ES, ax

mov FS, ax

mov GS, ax


mov al, 0x20

out 0x20, al


mov edi, (80*2*0)

lea esi, [msg_isr_ignore]

call printf

inc byte [msg_isr_ignore]


jmp ret_from_int


isr_32_timer:

push gs

push fs

push es

push ds

pushad


mov ax, SysDataSelector

mov DS, ax

mov ES, ax

mov FS, ax

mov GS, ax


mov al, 0x20

out 0x20, al


mov edi, 80*2*0

lea esi, [msg_isr_32_timer]

call printf

inc byte [msg_isr_32_timer]


jmp ret_from_int


isr_33_keyboard:

push gs

push fs

push es

push ds

pushad


mov ax, SysDataSelector

mov DS, ax

MOV ES, ax

mov FS, ax

mov GS, ax


in al, 0x60


mov al, 0x20

out 0x20, al

mov edi, (80*2*0)+(2*35)

lea esi, [msg_isr_33_keyboard]

call printf

inc byte [msg_isr_33_keyboard]


jmp ret_from_int


isr_128_soft_int:

push gs

push fs

push es

push ds

pushad


push eax

mov ax, SysDataSelector

mov DS, ax

mov ES, ax

MOV FS, ax

mov GS, ax

pop eax


mov edi, eax

lea esi, [ebx]

call printf


jmp ret_from_int


ret_from_int:

xor eax, eax

mov eax, [esp+52] ; 인터럽트가 발생되었을 때 CS 값을 가져옴

and eax, 0x00000003 ; CS 값에 포함된 RPL 을 얻을 수 있다.

xor ebx, ebx

mov bx, cs ; 현재 CS를 가져와서(커널 코드 세그먼트 셀렉터 값)

and ebx, 0x00000003 ; CPL을 가져오고

cmp eax, ebx ; 비교해서

ja scheduler ; eax가 크면 유저 영역 실행 중 인터럽트 발생한 것으로 판별


popad

pop ds

pop es

pop fs

pop gs


iret ; 커널영역 루틴 실행중에 발생한 인터럽트라면 그대로 복귀


msg_isr_ignore db "This is an ignorable interrupt", 0

msg_isr_32_timer db ".This is the timer interrupt", 0

msg_isr_33_keyboard db ".This is the keyboard interrupt", 0

msg_isr_128_soft_int db ".This is the soft_int interrupt", 0


; idt


idt_ignore:

dw isr_ignore

dw 0x08

db 0

db 0x8e

dw 0x0001


idt_timer:

dw isr_32_timer

dw 0x08

db 0

db 0x8e

dw 0x0001


idt_keyboard:

dw isr_33_keyboard

dw 0x08

db 0

db 0x8e

dw 0x0001


idt_soft_int:

dw isr_128_soft_int

dw 0x08

db 0

db 0xef

dw 0x0001


times 4608-($-$$) db 0


이 프로그램의 핵심은 Task Switching이 일어날 경우는 유저 영역에서 Interrupt가 발생되었을 때라는 것과 CurrentTask 값을 증가시켜가면서 계속 해당 Task를 실행시키고 마지막 Task까지 실행시키면 다시 처음 Task를 실행시키면서 반복한다는 것이다.

시작 후 처음은 Task1이 실행되고 그 Task1에서 int 0x80으로 콜게이트를 통한 인터럽트를 호출하면 그에 해당하는 인터럽트 핸들러 수행 후 ret_from_int 함수로 넘어가는데, 이곳에서 특권레벨을 비교하여 유저 영역에서 실행되었다고 판별이 되면 scheduler로 넘어간다. 

scheduler에서는 각각의 프로세스에 대한 스택을 되돌려 놓고 CurrentTask 값을 증가시킨다. 증가시키면서 NumTask(총 Task 수)와 비교하여 같으면 CurrentTask를 0으로 초기화 한다. 다르면 CurrentTask에 해당하는 Task로 분기한다.

'Study > Kenrel' 카테고리의 다른 글

a20게이트 & 페이징  (0) 2015.10.06
유저모드 Task Switching  (0) 2015.10.01
Protected Mode에서의 보호  (0) 2015.09.30
Task Switching in Kernel  (0) 2015.09.25
PIC 초기화  (0) 2015.09.24
Posted by Mungsul
,