일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 오버워치 세이버메트릭스
- BEST of the BEST
- libtins
- Find
- Overwatch League SaberMetrics
- #IntelManual
- #MINT64 #Sqix
- libpcap
- command
- ftz
- Sqix
- KASAN
- >
- vi
- linux
- vi 외부 명령어
- Network
- #
- #Qt Creator
- C++11
- FTZ 레벨2
- 인터럽트
- #IntelManual #segment Descriptor #세그먼트 디스크립터 #MINT64 #Sqix
- 오버워치
- #Best of the Best #OS #MINT64 #Sqix
- vim
- Today
- Total
Sqix
64bit 멀티코어 OS 제작하기 [5] - 3 : 보호 모드의 커널 이미지를 작성하자 본문
이제 우리는 보호 모드에서 사용할 코드를 작성해야 합니다. 16비트의 레지스터를 32비트로 변경해 주고, 스택 크기 역시 4바이트로 크기가 증가하였기 때문에 파라미터들에 대한 오프셋 역시 +4n으로, 즉 4의 배수로 늘려주어야 합니다. 이전에 플래그 부분에서 언급했던 내용이기도 하지만, 32비트 오프셋을 이용하기 때문에 우리는 4GB의 메모리 영역에 마음껏 접근할 수 있기 때문에, 비디오 메모리를 접근할 때 굳이 ES 레지스터를 사용할 필요가 없기도 합니다.
지금까지 우리가 한 과정을 Linux Boot Protocol을 기준삼아서 정리해 보겠습니다. 부트 로더를 이용해서 코드 영역, 데이터 영역, 스택 영역을 설정 및 초기화해 주고, 16비트 커널 이미지를 로드하여 주었습니다. 이제 우리가 할 일은 32비트 커널 이미지를 로드하기 위해서 32비트 커널 엔트리 포인트를 작성하고, 커널 이미지를 작성하여 이를 로드해 주면 됩니다.
그렇다면, 엔트리 포인트를 먼저 생성하여 보도록 합시다.
01.Kernel32 디렉토리 아래에 EntryPoint.s 파일을 추가합니다(.s 파일은 어셈블리 파일입니다). 그리고 다음 코드를 작성합니다.
[ORG 0x00] ; Start address : 0x00
[BITS 16] ; 16bit code
SECTION .text ; text section(segment)
START:
mov ax, 0x1000 ; EP Start Addr(0x10000)
mov ds, ax
mov es, ax
cli ; Inhibit Interrupt
lgdt [ GDTR ] ; Load GDT Table (load gdt inst)
; Disable Paging, Caching, Align check, use Internal FPU, enable 32bit
mov eax, 0x400000eB ; PG, AM, NW, WP, EM = 0 / CD, NE, ET, TS, MP = 1
mov cr0, eax ; set CR0's flag
; change code segment & set EIP(CS segment selector) 0x00
jmp dword 0x08: ( PROTECTMODE - $$ + 0x10000 )
[BITS 32] ; 32bit code
PROTECTMODE:
mov ax, 0x10 ; 32bit data segment discriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Make Stack on 0x00000000 ~ 0x0000FFFF with 64KB size
mov ss, ax
mov esp, 0xFFFE
mov ebp, 0xFFFE
; to use memory efficiently, use 0x0000FFFE(intel processor optimization)
; print message to notify that we changed to 32bit mode
push ( SWITCHSUCCESSMESSAGE - $$ + 0x10000 ) ; push the message's addr
push 2 ; Y location
push 0 ; X location
call PRINTMESSAGE
add esp, 12 ; calling convention
jmp $ ; inf loop
PRINTMESSAGE:
;Stack Frame
push ebp
mov ebp, esp
;Save Registers to recover after return
push esi
push edi
push eax
push ecx
push edx
;Calculate Line address with Y location
mov eax, dword [ ebp + 12 ] ; set Param2(line num) on EAX reg
mov esi, 160 ; line bytes : 2 * 80 == 160
mul esi ; line num * line bytes
mov edi, eax ; set Y addr on EDI reg
;Calculate Video Memory Address with X, Y Location
mov eax, dword [ ebp + 8 ] ; set Param1(X Location) on EAX reg
mov esi, 2 ; set character's byte(2)
mul esi ; X location * sizeof(char)
add edi, eax ; X + Y -> Video Memory Addr
mov esi, dword [ ebp + 16 ] ; Param3 (string addr)
.MESSAGELOOP:
mov cl, byte [ esi ] ; copy 1 byte on cl
cmp cl, 0 ; check string end
je .MESSAGEEND
mov byte [ edi + 0xB8000 ], cl ; print character
add esi, 1 ; go to next index
add edi, 2 ; go to next location(addr)
jmp .MESSAGELOOP ; loop
.MESSAGEEND:
; Recover Register
pop edx
pop ecx
pop eax
pop edi
pop esi
; Stack Frame
pop ebp
ret
; DATA SECTION
; to align GDT data with 8byte
align 8, db 0
; to align GDTR
dw 0x0000
GDTR:
dw GDTEND - GDT - 1 ; whole size of GDT Table
dd ( GDT - $$ + 0x10000 ) ; base address of GDT Table
GDT:
NULLDESCRIPTOR:
dw 0x0000
dw 0x0000
db 0x00
db 0x00
db 0x00
db 0x00
CODEDESCRIPTOR:
dw 0xFFFF ; Limit [ 15 : 00 ]
dw 0x0000 ; Base [ 15 : 00 ]
db 0x00 ; Base [ 23 : 16 ]
db 0x9A ; P = 1, DPL = 0, Code Segment, Execute / Read
db 0xCF ; G = 1, D = 0, L = 0, Limit [ 19 : 16 ]
db 0x00 ; Base [ 31 : 24 ]
DATADESCRIPTOR:
dw 0xFFFF ; Limit [ 15 : 00 ]
dw 0x0000 ; Base [ 15 : 00 ]
db 0x00 ; Base [ 23 : 16 ]
db 0x92 ; P = 1, DPL = 0, Data Segment, R/W
db 0xCF ; G = 1, D = 1, L = 0, Limit [ 19 : 16 ]
db 0x00 ; Base [ 31 : 24 ]
GDTEND:
SWITCHSUCCESSMESSAGE: db 'Successfully Switched To Protected Mode.'
times 512 - ( $ - $$ ) db 0x00 ; fill empty space with 0x00
코드 작성을 완료하였습니다. 이제 makefile을 수정해서 이를 빌드합시다. 또한, 생성한 커널 이미지의 섹터의 수가 1개 뿐이니 bootloader.asm의 TOTALSECTIONCOUNT를 1로 수정하여 줍시다.
all: Kernel32.bin
Kernel32.bin: Source/EntryPoint.s
nasm -o Kernel32.bin $<
clean:
rm -f Kernel32.bin
$<는 상위 의존성 부분의 첫 번째 파일(Source/EntryPoint.s)을 의미하는 매크로입니다. 또한, 혼란을 주기 위해 VirtualOS 파일은 삭제합시다.
#rm -rf VirtualOS*
이제 상위 폴더의 makefile 역시 수정합시다.
all: BootLoader Kernel32 Disk.img
BootLoader:
@echo
@echo ============== Build Boot Loader ==============
@echo
make -C 00.BootLoader
@echo
@echo ============== Build Complete ==============
@echo
Kernel32:
@echo
@echo ============== Build 32bit Kernel =============
@echo
make -C 01.Kernel32
@echo
@echo ============== Build Complete ==============
@echo
Disk.img: 00.BootLoader/BootLoader.bin 01.Kernel32/Kernel32.bin
@echo
@echo ============== Disk Image Build Start ==============
@echo
cat 00.BootLoader/BootLoader.bin 01.Kernel32/Kernel32.bin > Disk.img
@echo
@echo ============== All Build Complete ==============
@echo
clean:
make -C 00.BootLoader clean
make -C 01.Kernel32 clean
rm -f Disk.img
바뀐 부분은 Disk.img 부분입니다. 이제 빌드하고 QEMU를 실행해봅시다.
# make
# qemu-system-x86_64 -m 64 -fda ./Disk.img -localtime -M pc
이제 32비트로 전환했으니, 커널을 작성해 보도록 합시다!
'MINT64 OS ' 카테고리의 다른 글
64bit 멀티코어 OS 제작하기 [5] - 2 : 16bit에서 32bit로 전환하기 (0) | 2018.06.04 |
---|---|
64bit 멀티코어 OS 제작하기 [5] - 1 : 세그먼트 디스크립터 (0) | 2018.06.04 |
Qt Creator 환경에서 진행하기 (0) | 2018.05.29 |
64bit 멀티코어 OS 제작하기 [4] - 3 : 부트 로더 테스트용 가상 이미지 제작 (0) | 2018.05.28 |
64bit 멀티코어 OS 제작하기 [4] - 2 : OS 이미지 로딩을 위한 부트로더 기능 추가 (2) (0) | 2018.05.28 |