앞서 배운 커널 구조들과 페이징을 구현하여 어떻게 소스코드가 구성되고 동작하는지를 알아보려한다.
앞서 배운 개념들에 대한 소스 코드는 거의 동일하고 페이징 기능 위주로 정리하였다.
init.inc
SysCodeSelector equ 0x08
SysDataSelector equ 0x10
VideoSelector equ 0x18
TSSSelector equ 0x20
UserCodeSelector equ 0x28+3
UserDataSelector equ 0x30+3
IDT_BASE equ 0xC0001000
TSS_ESP0_WHERE equ 0x90000
boot_paging.asm
%include "init.inc"
[org 0]
jmp 07C0h:start
%include "a20.inc"
start:
mov ax, cs
mov ds, ax
mov es, ax
mov ax, 0xB800
mov es, ax
mov di, 0
mov ax, word [msgBack]
mov cx, 0x7FF
paint:
mov word [es:di], ax
add di, 2
dec cx
jnz paint
read:
mov ax, 0x9000
mov es, ax
mov bx, 0
mov ah, 2
mov al, 2
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, 0
int 13h
jc read
read_kernel:
mov ax, 0x8000
mov es, ax
mov bx, 0
mov ah, 2
mov al, 7
mov ch, 0
mov cl, 4
mov dh, 0
mov dl, 0
int 13h
jc read_kernel
read_user1:
mov ax, 0x7000
mov es, ax
mov bx, 0
mov ah, 2
mov al, 1
mov ch, 0
mov cl, 11
mov dh, 0
mov dl, 0
int 13h
jc read_user1
read_user2:
mov ax, 0x7000
mov es, ax
mov bx, 0x200
mov ah, 2
mov al, 1
mov ch, 0
mov cl, 12
mov dh, 0
mov dl, 0
int 13h
jc read_user2
read_user3:
mov ax, 0x7000
mov es, ax
mov bx, 0x400
mov ah, 2
mov al, 1
mov ch, 0
mov cl, 13
mov dh, 0
mov dl, 0
int 13h
jc read_user3
read_user4:
mov ax, 0x7000
mov es, ax
mov bx, 0x600
mov ah, 2
mov al, 1
mov ch, 0
mov cl, 14
mov dh, 0
mov dl, 0
int 13h
jc read_user4
read_user5:
mov ax, 0x7000
mov es, ax
mov bx, 0x800
mov ah, 2
mov al, 1
mov ch, 0
mov cl, 15
mov dh, 0
mov dl, 0
int 13h
jc read_user5
mov dx, 0x3F2
xor al, al
out dx, al
cli
call a20_try_loop
mov al, 0x11
out 0x20, al
dw 0x00eb, 0x00eb
out 0xA0, al
dw 0x00eb, 0x00eb
mov al, 0x20
out 0x21, al
dw 0x00eb, 0x00eb
mov al, 0x28
out 0xA1, al
dw 0x00eb, 0x00eb
mov al, 0x04
out 0x21, al
dw 0x00eb, 0x00eb
mov al, 0x02
out 0xA1, al
dw 0x00eb, 0x00eb
mov al, 0x01
out 0x21, al
dw 0x00eb, 0x00eb
out 0xA1, al
dw 0x00eb, 0x00eb
mov al, 0xFF
out 0xA1, al
dw 0x00eb, 0x00eb
mov al, 0xFB
out 0x21, al
jmp 0x9000:0000
msgBack db '.', 0x67
times 510-($-$$) db 0
dw 0AA55h
a20.inc
; IF CPU is AMD ELAN
;
; mov al, 0x02
; out 0x92, al
; a20_elan_wait:
; call a20_test
; jz a20_elan_wait
; jmp a20_done
;
A20_TEST_LOOPS equ 32
A20_ENABLE_LOOPS equ 255
A20_TEST_ADDR equ 4*0x80
a20_try_loop:
; Check the A20 gate exists on the computer
a20_none:
call a20_test
jnz a20_done
a20_bios:
mov ax, 0x2401
pushfd
int 0x15
popfd
call a20_test
jnz a20_done
a20_kbc:
call empty_8042
call a20_test
jnz a20_done
mov al, 0xD1
out 0x64, al
call empty_8042
mov al, 0xDF
out 0x60, al
call empty_8042
a20_kbc_wait:
xor cx, cx
a20_kbc_wait_loop:
call a20_test
jnz a20_done
loop a20_kbc_wait_loop
a20_fast:
in al, 0x92
or al, 0x02
and al, 0xFE
out 0x92, al
a20_fast_wait:
xor cx, cx
a20_fast_wait_loop:
call a20_test
jnz a20_done
loop a20_fast_wait_loop
dec byte [a20_tries]
jnz a20_try_loop
a20_die:
hlt
jmp a20_die
a20_tries:
db A20_ENABLE_LOOPS
a20_done:
ret
a20_test:
push cx
push ax
xor cx, cx
mov fs, cx
dec cx
mov gs, cx
mov cx, A20_TEST_LOOPS
mov ax, word [fs:A20_TEST_ADDR]
push ax
a20_test_wait:
inc ax
mov word [fs:A20_TEST_ADDR], ax
call delay
cmp ax, word [gs:A20_TEST_ADDR+0x10]
loop a20_test_wait
pop word [fs:A20_TEST_ADDR]
pop ax
pop cx
ret
empty_8042:
push ecx
mov ecx, 100000
empty_8042_loop:
dec ecx
jz empty_8042_end_loop
call delay
in al, 0x64
test al, 1
jz no_output
call delay
in al, 0x60
jmp empty_8042_loop
no_output:
test al, 2
jnz empty_8042_loop
empty_8042_end_loop:
pop ecx
ret
delay:
out 0x80, al
ret
setup.asm
%include "init.inc"
PAGE_DIR equ 0x100000
PAGE_TAB_KERNEL equ 0x101000
PAGE_TAB_USER equ 0x102000
PAGE_TAB_LOW equ 0x103000
[org 0x90000]
[bits 16]
start:
cld
mov ax, cs
mov ds, ax
xor ax, ax
mov ss, ax
xor eax, eax
lea eax, [tss]
add eax, 0x90000
mov [descriptor4+2], ax
shr eax, 16
mov [descriptor4+4], al
mov [descriptor4+7], ah
cli
lgdt [gdtr]
mov eax, cr0
or eax, 0x00000001
mov cr0, eax
jmp $+2
nop
nop
jmp dword SysCodeSelector:PM_Start
[bits 32]
PM_Start:
mov bx, SysDataSelector
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
mov ss, bx
lea esp, [PM_Start]
mov esi, 0x80000
mov edi, 0x200000
mov cx, 512*7
kernel_copy:
mov al, byte [ds:esi]
mov byte [es:edi], al
inc esi
inc edi
dec cx
jnz kernel_copy
mov esi, 0x70000
mov edi, 0x300000
mov cx, 512
user1_copy:
mov al, byte [ds:esi]
mov byte [es:edi], al
inc esi
inc edi
dec cx
jnz user1_copy
mov esi, 0x70200
mov edi, 0x301000
mov cx, 512
user2_copy:
mov al, byte [ds:esi]
mov byte [es:edi], al
inc esi
inc edi
dec cx
jnz user2_copy
mov esi, 0x70400
mov edi, 0x302000
mov cx, 512
user3_copy:
mov al, byte [ds:esi]
mov byte [es:edi], al
inc esi
inc edi
dec cx
jnz user3_copy
mov esi, 0x70600
mov edi, 0x303000
mov cx, 512
user4_copy:
mov al, byte [ds:esi]
mov byte [es:edi], al
inc esi
inc edi
dec cx
jnz user4_copy
mov esi, 0x70800
mov edi, 0x304000
mov cx, 512
user5_copy:
mov al, byte [ds:esi]
mov byte [es:edi], al
inc esi
inc edi
dec cx
jnz user5_copy
mov edi, PAGE_DIR
mov eax, 0
mov ecx, 1024
cld
rep stosd
mov edi, PAGE_DIR
mov eax, 0x103000
or eax, 0x01
mov [es:edi], eax
mov edi, PAGE_DIR+0x200*4
mov eax, 0x102000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_DIR+0x300*4
mov eax, 0x101000
or eax, 0x01
mov [es:edi], eax
mov edi, PAGE_TAB_KERNEL
mov eax, 0
mov ecx, 1024
cld
rep stosd
mov edi, PAGE_TAB_KERNEL+0x000*4
mov eax, 0x200000
or eax, 1
mov [es:edi], eax
mov edi, PAGE_TAB_KERNEL+0x001*4
mov eax, 0x201000
or eax, 1
mov [es:edi], eax
mov edi, PAGE_TAB_USER
mov eax, 0x00
mov ecx, 1024
cld
rep stosd
mov edi, PAGE_TAB_USER+0x000*4
mov eax, 0x300000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_TAB_USER+0x001*4
mov eax, 0x301000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_TAB_USER+0x02*4
mov eax, 0x302000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_TAB_USER+0x03*4
mov eax, 0x303000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_TAB_USER+0x04*4
mov eax, 0x304000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_TAB_LOW
mov eax, 0x00000
or eax, 0x01
mov cx, 256
page_low_loop:
mov [es:edi], eax
add eax, 0x1000
add edi, 4
dec cx
jnz page_low_loop
lea eax, [tss_esp0]
mov [TSS_ESP0_WHERE], eax
mov eax, PAGE_DIR
mov cr3, eax
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
jmp 0xC0000000
;-----------------------DATA AREA------------------------
gdtr:
dw gdt_end-gdt-1
dd gdt
gdt:
dd 0, 0
dd 0x0000FFFF, 0x00CF9A00
dd 0x0000FFFF, 0x00CF9200
dd 0x8000FFFF, 0x0040920B
descriptor4:
dw 104
dw 0
db 0
db 0x89
db 0
db 0
dd 0x0000FFFF, 0x00FCFA00
dd 0x0000FFFF, 0x00FCF200
gdt_end:
tss:
dw 0, 0
tss_esp0:
dd 0
dw SysDataSelector, 0
dd 0
dw 0, 0
dd 0
dw 0, 0
dd 0x0100000
tss_eip:
dd 0, 0
dd 0, 0, 0, 0
tss_esp:
dd 0, 0, 0, 0
dw 0, 0
dw 0, 0
dw UserDataSelector, 0
dw 0, 0
dw 0, 0
dw 0, 0
dw 0, 0
dw 0, 0
times 1024-($-$$) db 0
kernel_paging.asm
%include "init.inc"
[org 0xC0000000]
[bits 32]
mov esp, 0xC0000FFF
%include "idt0.inc"
lidt [idtr]
mov al, 0xFC
out 0x21, al
sti
mov ax, TSSSelector
ltr ax
mov eax, [CurrentTask]
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
add eax, 4
mov eax, [CurrentTask]
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 eax, [TSS_ESP0_WHERE]
mov [eax], esp
lea esp, [ebx]
popad
pop ds
pop es
pop fs
pop gs
iretd
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
; -------------------- Task Structure ---------------------
%include "user_task_structure.inc"
;---------------------- Interrupt Service Routines ----------------------------
%include "idt1.inc"
; ------------------------ IDT -------------------------
idtr:
dw 256*8-1
dd IDT_BASE
%include "idt2.inc"
times 512*7-($-$$) db 0
user_task_structure.inc
;----------------User1 Task_Structure-----------------
times 63 dd 0
User1Stack:
User1regs:
dd 0, 0, 0, 0, 0, 0, 0, 0
dw UserDataSelector, 0
dw UserDataSelector, 0
dw UserDataSelector, 0
dw UserDataSelector, 0
dd 0x80000000
dw UserCodeSelector, 0
dd 0x200
dd 0x80000FFF
dw UserDataSelector, 0
;----------------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 0x80001000
dw UserCodeSelector, 0
dd 0x200
dd 0x80001FFF
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 0x80002000
dw UserCodeSelector, 0
dd 0x200
dd 0x80002FFF
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 0x80003000
dw UserCodeSelector, 0
dd 0x200
dd 0x80003FFF
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 0x80004000
dw UserCodeSelector, 0
dd 0x200
dd 0x80004FFF
dw UserDataSelector, 0
idt0.inc
cld
mov ax, SysDataSelector
mov es, ax
xor eax, eax
xor ecx, ecx
mov ax, 256
mov edi, IDT_BASE
loop_idt:
lea esi, [idt_ignore]
mov cx, 8
rep movsb
dec ax
jnz loop_idt
mov edi, IDT_BASE+8*0x20
lea esi, [idt_timer]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*0x21
lea esi, [idt_keyboard]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*0x80
lea esi, [idt_soft_int]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*0
lea esi, [idt_exception00]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*1
lea esi, [idt_exception01]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*2
lea esi, [idt_exception02]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*3
lea esi, [idt_exception03]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*4
lea esi, [idt_exception04]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*5
lea esi, [idt_exception05]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*6
lea esi, [idt_exception06]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*7
lea esi, [idt_exception07]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*8
lea esi, [idt_exception08]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*9
lea esi, [idt_exception09]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*10
lea esi, [idt_exception10]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*11
lea esi, [idt_exception11]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*12
lea esi, [idt_exception12]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*13
lea esi, [idt_exception13]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*14
lea esi, [idt_exception14]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*15
lea esi, [idt_exception15]
mov cx, 8
rep movsb
mov edi, IDT_BASE+8*17
lea esi, [idt_exception17]
mov cx, 8
rep movsb
idt1.inc
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
mov cx, SysDataSelector
mov DS, cx
mov ES, cx
mov FS, cx
mov GS, cx
mov edi, eax
lea esi, [ebx]
call printf
jmp ret_from_int
isr_00:
mov edi, (80*12*2)
lea esi, [msg_isr_00_zero_devide]
call printf
jmp $
isr_01:
mov edi, (80*12*2)
lea esi, [msg_isr_01_debug]
call printf
jmp $
isr_02:
mov edi, (80*12*2)
lea esi, [msg_isr_02_nmi]
call printf
jmp $
isr_03:
mov edi, (80*12*2)
lea esi, [msg_isr_03]
call printf
jmp $
isr_04:
mov edi, (80*12*2)
lea esi, [msg_isr_04_into]
call printf
jmp $
isr_05:
mov edi, (80*12*2)
lea esi, [msg_isr_05_bound]
call printf
jmp $
isr_06:
mov edi, (80*12*2)
lea esi, [msg_isr_06_opcode]
call printf
jmp $
isr_07:
mov edi, (80*12*2)
lea esi, [msg_isr_07_coprocess]
call printf
jmp $
isr_08:
mov edi, (80*12*2)
lea esi, [msg_isr_08_double_fault]
call printf
jmp $
isr_09:
mov edi, (80*12*2)
lea esi, [msg_isr_09_coprocess_segment]
call printf
jmp $
isr_10:
mov edi, (80*12*2)
lea esi, [msg_isr_10_bad_tss]
call printf
jmp $
isr_11:
mov edi, (80*12*2)
lea esi, [msg_isr_11_segment_not_present]
call printf
jmp $
isr_12:
mov edi, (80*12*2)
lea esi, [msg_isr_12_stack_fault]
call printf
jmp $
isr_13:
mov edi, (80*12*2)
lea esi, [msg_isr_13_gpf]
call printf
jmp $
isr_14:
mov edi, (80*12*2)
lea esi, [msg_isr_14_page_fault]
call printf
jmp $
isr_15:
mov edi, (80*12*2)
lea esi, [msg_isr_15_coprocessor_error]
call printf
jmp $
isr_17:
mov edi, (80*12*2)
lea esi, [msg_isr_17_alignment_check]
call printf
jmp $
ret_from_int:
xor eax, eax
mov eax, [esp+52]
and eax, 0x00000003
xor ebx, ebx
mov bx, cs
and ebx, 0x00000003
cmp eax, ebx
ja scheduler
popad
pop ds
pop es
pop fs
pop gs
iret
msg_isr_00_zero_devide db "Zero Devide", 0
msg_isr_01_debug db "Debug", 0
msg_isr_02_nmi db "NMI", 0
msg_isr_03 db "int3", 0
msg_isr_04_into db "INTO", 0
msg_isr_05_bound db "Bound", 0
msg_isr_06_opcode db "Invalid Opcode", 0
msg_isr_07_coprocess db "Coprocess Not Available", 0
msg_isr_08_double_fault db "Double Fault", 0
msg_isr_09_coprocess_segment db "Coprocess Segment Overrun", 0
msg_isr_10_bad_tss db "Bad TSS", 0
msg_isr_11_segment_not_present db "Segment Not Present", 0
msg_isr_12_stack_fault db "Stack Fault", 0
msg_isr_13_gpf db "GPF", 0
msg_isr_14_page_fault db "Page Fault", 0
msg_isr_15_coprocessor_error db "Coprocess Error" , 0
msg_isr_17_alignment_check db "Alignment Check", 0
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_sofUnt db ".This is the soft_int interrupt", 0
idt2.inc
idt_exception00:
dw isr_00
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception01:
dw isr_01
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception02:
dw isr_02
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception03:
dw isr_03
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception04:
dw isr_04
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception05:
dw isr_05
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception06:
dw isr_06
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception07:
dw isr_07
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception08:
dw isr_08
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception09:
dw isr_09
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception10:
dw isr_10
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception11:
dw isr_11
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception12:
dw isr_12
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception13:
dw isr_13
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception14:
dw isr_14
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception15:
dw isr_15
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_exception17:
dw isr_17
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_ignore:
dw isr_ignore
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_timer:
dw isr_32_timer
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_keyboard:
dw isr_33_keyboard
dw 0x08
db 0
db 0x8E
dw 0xC000
idt_soft_int:
dw isr_128_soft_int
dw 0x08
db 0
db 0xEF
dw 0xC000
user_program1.asm
[org 0x80000000]
[bits 32]
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 process1", 0
msg_user_process1_2 db ".I'm running now.", 0
times 512-($-$$) db 0
user_program2.asm
[org 0x80001000]
[bits 32]
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
times 512-($-$$) db 0
user_program3.asm
[org 0x80002000]
[bits 32]
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
times 512-($-$$) db 0
user_program4.asm
[org 0x80003000]
[bits 32]
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
times 512-($-$$) db 0
user_program5.asm
[org 0x80004000]
[bits 32]
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
times 512-($-$$) db 0
위 파일을 작성한 뒤 asm 확장자 파일을 컴파일하여 부팅하면 다음과 같은 화면이 출력된다.

위 파일에서 태스크나 비디오 위치 출력이 잘못된 것 같은데, 그냥 넘어갔다.
소스코드 분석
boot_paging 각 파일들은 다음과 같은 물리 메모리 주소에 로드된다고 명시된다.
- setup.bin : 0x90000
- kernel.bin(kernel_paging.asm) : 0x80000 (주소 값 이동과 페이징을 거쳐서 kernel_paging.asm 파일의 시작 주소는 다른 주소로 명시되어있다.)
- user_program
- user_program1 : 0x70000
- user_program2 : 0x70200
- user_program3 : 0x70400
- user_program4 : 0x70600
- user_program5 : 0x70800
boot_paging에서 각 프로그램들을 메모리에 로드하고 jmp 0x9000:0000 명령어를 통해 setup.bin으로 점프하게 된다.
setup.bin
mov esi, 0x80000
mov edi, 0x200000
mov cx, 512*7
kernel_copy:
mov al, byte [ds:esi]
mov byte [es:edi], al
inc esi
inc edi
dec cx
jnz kernel_copy
setup.bin에서 16비트 리얼모드에서 32비트 보호모드로 변환 후 kernel 주소인 0x80000번지부터 0xE00(512*7)만큼의 주소의 데이터를 0x200000번지부터 0xE00만큼의 주소로 옮긴다.
user 주소 5개도 마찬가지로 데이터를 옮긴다.
mov edi, PAGE_DIR
mov eax, 0
mov ecx, 1024
cld
rep stosd
mov edi, PAGE_DIR
mov eax, 0x103000
or eax, 0x01
mov [es:edi], eax
mov edi, PAGE_DIR+0x200*4
mov eax, 0x102000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_DIR+0x300*4
mov eax, 0x101000
or eax, 0x01
mov [es:edi], eax
해당 코드는 페이지 디렉토리 엔트리의 인덱스와 포인터 값을 정의하는 소스코드이다.
PAGE_DIR은 0x100000의 값을 가지고 있고, 0x100000번지부터 1024번을 4바이트씩 0으로 초기화한다.
rep stosd를 통해 ecx의 수만큼 eax의 값을 edi부터 4바이트씩 초기화 하게 된다.
이후 페이지 디렉토리의 0번째 엔트리 (PAGE_DIR)의 값으로 0x103000 + 1(커널)을 저장한다.
페이지 디렉토리의 512번째(0x200) 엔트리에는 0x102000 +7(유저)을 저장하고,
페이지 디렉토리의 768번째(0x300) 엔트리에는 0x101000 + 1(커널)을 저장한다.
위 코드로 인해 현재 페이지 디렉터리 엔트리 3개를 생성하였다.
- 하위 메모리
- 유저 영역
- 커널 영역
하위 메모리란?
CR3에 주소를 넣고 페이징이 활성화 되는 순간 가상 주소를 참조하게 되어 0번째 엔트리에 매핑된 포인터 주소를 찾아간다.
이 때 저장된 주소 값이 없으면 프로그램이 실행되지 않는다.
mov edi, PAGE_TAB_KERNEL+0x000*4
mov eax, 0x200000
or eax, 1
mov [es:edi], eax
mov edi, PAGE_TAB_KERNEL+0x001*4
mov eax, 0x201000
or eax, 1
mov [es:edi], eax
페이지 테이블을 매핑하는 코드이다.
PAGE_TAB_KERNEL은 0x101000이다. 페이지 디렉터리의 768번째(0x300) 엔트리를 타고 왔다.
첫 번째(0번) 페이지 테이블 엔트리의 포인터에는 0x200000 + 1 값이 저장되어있다.
두 번째(1번) 페이지 테이블 엔트리의 포인터에는 0x201000 + 1 값이 저장되어있다.
커널 레벨이기 때문에 1값이 더해졌으며 해당 페이지 테이블 엔트리를 통해 4KB 페이지로 이동한다.
0x200000은 커널 관련 4KB 페이지이고, 0x201000은 IDT 관련 4KB 페이지이다.
mov edi, PAGE_TAB_USER+0x000*4
mov eax, 0x300000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_TAB_USER+0x001*4
mov eax, 0x301000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_TAB_USER+0x02*4
mov eax, 0x302000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_TAB_USER+0x03*4
mov eax, 0x303000
or eax, 0x07
mov [es:edi], eax
mov edi, PAGE_TAB_USER+0x04*4
mov eax, 0x304000
or eax, 0x07
mov [es:edi], eax
유저 테이블 페이지 엔트리의 포인터에는 0x300000부터 0x1000 단위씩 값들이 올라가면서 저장되어있다.
유저 권한이기 때문에 7을 더해준다.
mov edi, PAGE_TAB_LOW
mov eax, 0x00000
or eax, 0x01
mov cx, 256
page_low_loop:
mov [es:edi], eax
add eax, 0x1000
add edi, 4
dec cx
jnz page_low_loop
PAGE_TAB_LOW는 0x103000 값을 가지고 있으며 이는 하위 메모리 관련 페이지 테이블 시작 주소이다.
0x103000에 0x00000+1을 저장하고 0x103000부터 4바이트 간격으로 0x1000바이트씩 0xFF000까지 매핑되게 된다.
커널 단계에서는 페이징 구조를 먼저 구축한 뒤, 물리 주소 배치한 후 가상 주소와 매핑하게 된다.
가상 주소는 페이지 디렉토리나 페이지 테이블의 엔트리 번호를 통해서 설정된다.
페이지 디렉토리에서 커널 부분 엔트리는 0x300번째이다.
0x300은 2진수로 표현하면 11 0000 0000이다.
선형주소의 10비트는 페이지 디렉토리 엔트리의 인덱스를 나타내므로 선형 주소로 표현하면
1100 0000 0000 0000 0000 0000 0000 0000, 즉, 0xC0000000이 된다.
kernel_paging.asm의 베이스 주소는 0xC0000000이다.
페이지 디렉토리에서 유저 부분 첫 번째 엔트리는 0X200번째이다.
0X200은 2진수로 표현하면 10 0000 0000이고 선형 주소의 상위 10비트에 해당한다.
이를 선형주소인 32비트로 표현하면 2진수 값은 1000 0000 0000 0000 0000 0000 0000 0000이며 0x80000000이다.
유저 부분 첫 번째 엔트리는 user_program1을 의미하고 페이지 테이블 하나의 크기는 4KB이다.
따라서 user_program1의 가상 주소는 0x80000000, user_program2의 가상 주소는 0x80001000이 된다.
다음은 소스 코드에서 페이지 디렉토리와 페이지 테이블의 구성도이다.

mov eax, PAGE_DIR
mov cr3, eax
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
jmp 0xC0000000
cr3에 페이지 디렉토리 시작 주소가 저장된다.
그리고 기존 설정 값을 유지하기 위해 cr0 값과 0x80000000을 OR 연산한다.
cr0은 32비트이고 0x80000000는 2진수로 1000 0000 0000 0000 0000 0000 0000 0000이다.
31번째 비트는 PG라는 비트이고, 이 비트는 페이징 기능을 활성화 하는 비트이다.
이후 커널 페이지(0xC0000000)로 점프하게 된다.
user_program1 가상 주소와는 무관하다.
'OS > 만들면서 배우는 OS 커널의 구조와 원리' 카테고리의 다른 글
| 8-2. 페이징 - 페이징 구현 (0) | 2026.04.23 |
|---|---|
| 8-1. 페이징 - A20 게이트 (0) | 2026.04.21 |
| 7. 유저모드 Task Switching (0) | 2026.04.19 |
| 6. 보호 (0) | 2026.04.13 |
| 5. 태스크 스위칭, 문맥 교환 (0) | 2026.04.13 |