>

%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
,