chore: refactor to x86_64 (wip)@@ -80,20 +80,18 @@ expanded_main:
call try_enable_a20 ; Try to enable A20 line for accessing memory above 1MB
- call describe_gdt ; Describe the GDT (so we can load it later in real mode)
- call describe_idt ; Describe the IDT (so we can load it later in real mode)
+ call describe_gdt ; Describe the GDT so we can switch modes safely
+ call describe_idt ; Describe the IDT so protected mode has a valid IDT
call load_kernel ; Load the kernel from disk into memory
- enable_protected_mode
- ; enable_real_mode
-
- jmp KERNEL_FLAT_ADDRESS ; Jump to the kernel's entry point (assuming it's loaded at KERNEL_FLAT_ADDRESS)
+ call enter_long_mode
jmp $
%include "a20-utility-inl.asm"
%include "idt-utility-inl.asm"
%include "kernel-load-utility-inl.asm"
+%include "long-mode-utility-inl.asm"
blankLine: db " ", CR, LF,0
enableProtectedModeMessage: db "Info: Enabling protected mode!", CR, LF, 0
@@ -1,7 +1,7 @@
bits 16
gdtEntry:
- .size: dw 40
+ .size: dw 55
.pointer: dd 0x7000
; void describe_gdt()
@@ -32,7 +32,7 @@ describe_gdt:
mov [es:di + 5], byte 0x98 ; Access (present and executable are set)
mov [es:di + 6], byte 0x00 ; Flags (granularity and size) and limit
mov [es:di + 7], byte 0x00 ; More base bits
-
+
add di, 8
; Data segment descriptor (16-bit)
@@ -54,7 +54,7 @@ describe_gdt:
mov [es:di + 5], byte 0x9a ; Access (present, executable, and readable are set)
mov [es:di + 6], byte 0xcf ; Flags (granularity and size) and limit
mov [es:di + 7], byte 0x00 ; More base bits
-
+
add di, 8
; Data segment descriptor (32-bit)
@@ -66,8 +66,27 @@ describe_gdt:
mov [es:di + 6], byte 0xcf ; Flags (granularity and size) and limit
mov [es:di + 7], byte 0x00 ; More base bits
- add di, 8
+ add di, 8
+
+ ; Code segment descriptor (64-bit)
+ gdtCode64Offset: equ 40
+ mov [es:di], word 0x0000
+ mov [es:di + 2], word 0x0000
+ mov [es:di + 4], byte 0x00 ; Base bits
+ mov [es:di + 5], byte 0x9a ; Access (present and executable are set)
+ mov [es:di + 6], byte 0xa0 ; Flags: granularity=1, long mode
+ mov [es:di + 7], byte 0x00 ; Base bits
+
+ add di, 8
+ ; Data segment descriptor (64-bit)
+ gdtData64Offset: equ 48
+ mov [es:di], word 0x0000
+ mov [es:di + 2], word 0x0000
+ mov [es:di + 4], byte 0x00 ; Base bits
+ mov [es:di + 5], byte 0x92 ; Access (present and writable are set)
+ mov [es:di + 6], byte 0x80 ; Flags: granularity=1
+ mov [es:di + 7], byte 0x00 ; Base bits
.cleanup:
pop es
ret
@@ -8,56 +8,33 @@ load_kernel:
mov si, loadingKernelMessage
call print_string
- ; Temporary buffer will be 32 sectors at 0x2000
- tempBufferSegment: equ 0x0200
maxSectorsPerRead: equ 32
-
sectorsAlreadyRead: dw 0
numberOfSectorsToReadNext: dw 0
.calculate_number_of_sectors:
- mov dx, word [kernelNumberOfSectors]
- sub dx, word [sectorsAlreadyRead]
- cmp dx, maxSectorsPerRead
+ mov ax, word [kernelNumberOfSectors]
+ sub ax, word [sectorsAlreadyRead]
+ cmp ax, maxSectorsPerRead
jle .do_read
- mov dx, maxSectorsPerRead
+ mov ax, maxSectorsPerRead
.do_read:
- mov [numberOfSectorsToReadNext], dx
+ mov [numberOfSectorsToReadNext], ax
+
+ ; Compute destination offset relative to 1MB using a real-mode segment we can actually encode.
+ movzx eax, word [sectorsAlreadyRead]
+ shl eax, 9
+ mov bx, 0x1000
+ add bx, ax
+ mov ax, 0xf000
+ mov es, ax
- ; Sets es:bx to be destination
- mov dx, tempBufferSegment
- mov es, dx
- xor bx, bx
-
mov cx, word [kernelStartSector]
add cx, word [sectorsAlreadyRead]
mov ax, word [numberOfSectorsToReadNext]
call read_disk
- .copy_to_real_location:
- enable_protected_mode
-
- ; Source location
- mov esi, tempBufferSegment * 0x10
-
- ; Destination location
- movzx eax, word [sectorsAlreadyRead]
- mov ecx, 512
- mul ecx
- mov edi, KERNEL_FLAT_ADDRESS
- add edi, eax
-
- ; We are moving ((512 / 4) * number of sectors) DWORDs
- mov eax, 128
- movzx ecx, word [numberOfSectorsToReadNext]
- mul ecx
- mov ecx, eax
-
- rep movsd
- enable_real_mode
-
- .read_again_or_finish:
mov ax, word [sectorsAlreadyRead]
add ax, word [numberOfSectorsToReadNext]
mov word [sectorsAlreadyRead], ax
@@ -71,4 +48,4 @@ load_kernel:
ret
loadingKernelMessage: db "Info: Loading kernel...", CR, LF, 0
-loadedKernelMessage: db "Info: Loaded kernel!", CR, LF, 0
+loadedKernelMessage: db "Info: Loaded kernel!", CR, LF, 0
@@ -0,0 +1,73 @@
+bits 32
+
+PML4_ADDR: equ 0x7200
+PDPT_ADDR: equ 0x7300
+PD_ADDR: equ 0x7400
+
+; void setup_page_tables()
+setup_page_tables:
+ ; Zero PML4 table (4096 bytes)
+ mov edi, PML4_ADDR
+ xor eax, eax
+ mov ecx, 1024
+ rep stosd
+
+ ; Zero PDPT table (4096 bytes)
+ mov edi, PDPT_ADDR
+ xor eax, eax
+ mov ecx, 1024
+ rep stosd
+
+ ; Zero PD table (4096 bytes)
+ mov edi, PD_ADDR
+ xor eax, eax
+ mov ecx, 1024
+ rep stosd
+
+ ; PML4[0] -> PDPT
+ mov eax, PDPT_ADDR
+ or eax, 0x03
+ mov [PML4_ADDR], eax
+ mov dword [PML4_ADDR + 4], 0
+
+ ; PDPT[0] -> PD
+ mov eax, PD_ADDR
+ or eax, 0x03
+ mov [PDPT_ADDR], eax
+ mov dword [PDPT_ADDR + 4], 0
+
+ ; PD[0] -> 2MiB identity mapped page
+ mov dword [PD_ADDR], 0x00000083
+ mov dword [PD_ADDR + 4], 0
+
+ ret
+
+; Enters long mode and jumps to the 64-bit kernel entry point.
+enter_long_mode:
+ ; enable protected mode first
+ enable_protected_mode
+
+ call setup_page_tables
+
+ ; Enable PAE
+ mov eax, cr4
+ or eax, 0x20
+ mov cr4, eax
+
+ ; Enable long mode in EFER
+ mov ecx, 0xC0000080
+ rdmsr
+ or eax, 0x100
+ wrmsr
+
+ ; Load page tables
+ mov eax, PML4_ADDR
+ mov cr3, eax
+
+ ; Enable paging
+ mov eax, cr0
+ or eax, 0x80000000
+ mov cr0, eax
+
+ ; Jump into the 64-bit kernel entry at 1MB
+ jmp 0x28:0x00100000
@@ -72,7 +72,7 @@ read_disk:
; (ch cylinder, cl sector, dh head) calculate_chs(cx LBA-sector)
calculate_chs:
- sectorsPerTrack: equ 36
+ sectorsPerTrack: equ 18
headsPerCylinder: equ 2
.calculate_sector:
@@ -4,7 +4,7 @@
kernelCompileFlags="-ffreestanding -nostdinc -nostdinc++ \
-Wall -Wextra \
- -o ./bin/kernel.bin -target i386-pc-none-elf \
+ -o ./bin/kernel.bin -target x86_64-unknown-linux-elf \
-I ./kernel/"
kernelLinkFlags="-nostdlib -Wl,--oformat=binary,-T./kernel/linker.ld"
kernelFiles="./kernel/unityBuild.cpp ./build/kernelEntryPoint.o"
@@ -18,24 +18,34 @@ rm *.bin *.img *.iso *.o *.vmdk 2> /dev/null
echo -e "\nBuilding..."
nasm ./boot/bootloader.asm -I ./boot/ -o ./bin/bootloader.bin || exit 1
-nasm -felf32 ./kernel/entry.asm -o ./build/kernelEntryPoint.o || exit 1
+nasm -f elf64 ./kernel/entry.asm -o ./build/kernelEntryPoint.o || exit 1
use_crystal_kernel=true
if [ "$use_crystal_kernel" = true ]
then
- CC=clang LD=ld crystal build --no-debug -Dkernel --cross-compile --target i386-unknown-linux-elf --prelude=empty --link-flags="-nostdlib -T./kernel/linker.ld" -o ./build/kernel ./kernel/kernel.cr
- # The object file should be at ./build/kernel.o, now link it with the entry point
- ld -nostdlib -T ./kernel/linker.ld -o ./bin/kernel.bin ./build/kernelEntryPoint.o ./build/kernel.o || exit 1
+ crystal build --no-debug -Dkernel --cross-compile --target x86_64-unknown-linux-elf --prelude=empty -o ./build/kernel ./kernel/kernel.cr
+ ld -m elf_x86_64 -nostdlib -T ./kernel/linker.ld -o ./build/kernel.elf ./build/kernelEntryPoint.o ./build/kernel.o || exit 1
+ objcopy -O binary ./build/kernel.elf ./bin/kernel.bin || exit 1
+
+ bootloader_size=$(stat -c%s ./bin/bootloader.bin)
+ bootloader_extra_sectors=$(( (bootloader_size + 511) / 512 - 1 ))
+ kernel_size=$(stat -c%s ./bin/kernel.bin)
+ kernel_number_of_sectors=$(( (kernel_size + 511) / 512 ))
+ kernel_start_sector=$((1 + bootloader_extra_sectors))
+
+ printf '%b' "\\x$(printf '%02x' "$((bootloader_extra_sectors & 0xff))")\\x00" | dd of=./bin/bootloader.bin bs=1 seek=504 conv=notrunc status=none
+ printf '%b' "\\x$(printf '%02x' "$((kernel_start_sector & 0xff))")\\x00" | dd of=./bin/bootloader.bin bs=1 seek=506 conv=notrunc status=none
+ printf '%b' "\\x$(printf '%02x' "$((kernel_number_of_sectors & 0xff))")\\x00" | dd of=./bin/bootloader.bin bs=1 seek=508 conv=notrunc status=none
+ echo "Patched bootloader sector info: extra=$bootloader_extra_sectors start=$kernel_start_sector sectors=$kernel_number_of_sectors"
else
clang++ $kernelCompileFlags $kernelLinkFlags $kernelFiles || exit 1
fi
-# touch ./bin/kernel.bin
./tools/genVDisk --output "crystalos.img" --floppy \
- --bootloader ./bin/bootloader.bin --kernel ./bin/kernel.bin
+ --bootloader ./bin/bootloader.bin --kernel ./bin/kernel.bin
echo -e "\nRunning..."
-qemu-system-i386 -drive if=floppy,index=0,format=raw,file=crystalos.img || exit 1
+qemu-system-x86_64 -drive if=floppy,index=0,format=raw,file=crystalos.img || exit 1
-exit 0
+exit 0
@@ -1,13 +1,15 @@
-bits 32
+bits 64
-extern kmain
+default rel
-section .entry
+extern kmain
-; void start()
global _start
+section .text
+
_start:
- mov esp, kernelStackStart
+ xor rbp, rbp
+ lea rsp, [rel kernelStackEnd]
call kmain
.hang:
@@ -15,6 +17,7 @@ _start:
hlt
jmp .hang
+;section .bss
align 16
kernelStackEnd:
times 16384 db 0
@@ -22,22 +22,48 @@ fun kearly(info_ptr: LibBootstrap::StartInfo*)
end
fun kmain
- # IDT.enable_interrupts
- # kprint "Hello from Fluorite."
+ screen = Pointer(UInt16).new(0xb8000_u64)
- address = 0xb800 # Pointer(UInt8).reinterpret(0xb8000)
-
- string = "[CrystalOS Kernel] Hello from Crystal"
- string_size = 33 # string.size
-
- # Loop over each character in the string
- # [0...string_size];each do |i|
- # # Write the character
- # address.value = string[i].to_u8
- # address += 1
- # # Write the attribute byte
- # address.value = 0xA0.to_u8
- # address += 1
- # end
+ screen.value = 0x1f43_u16 # C
+ screen += 1
+ screen.value = 0x1f72_u16 # r
+ screen += 1
+ screen.value = 0x1f79_u16 # y
+ screen += 1
+ screen.value = 0x1f73_u16 # s
+ screen += 1
+ screen.value = 0x1f74_u16 # t
+ screen += 1
+ screen.value = 0x1f61_u16 # a
+ screen += 1
+ screen.value = 0x1f6c_u16 # l
+ screen += 1
+ screen.value = 0x1f4f_u16 # O
+ screen += 1
+ screen.value = 0x1f53_u16 # S
+ screen += 1
+ screen.value = 0x1f20_u16 # space
+ screen += 1
+ screen.value = 0x1f6b_u16 # k
+ screen += 1
+ screen.value = 0x1f65_u16 # e
+ screen += 1
+ screen.value = 0x1f72_u16 # r
+ screen += 1
+ screen.value = 0x1f6e_u16 # n
+ screen += 1
+ screen.value = 0x1f65_u16 # e
+ screen += 1
+ screen.value = 0x1f6c_u16 # l
+ screen += 1
+ screen.value = 0x1f20_u16 # space
+ screen += 1
+ screen.value = 0x1f72_u16 # r
+ screen += 1
+ screen.value = 0x1f65_u16 # e
+ screen += 1
+ screen.value = 0x1f61_u16 # a
+ screen += 1
+ screen.value = 0x1f64_u16 # d
end
@@ -1,34 +1,33 @@
-OUTPUT_FORMAT(elf32-i386)
+OUTPUT_FORMAT("elf64-x86-64")
ENTRY(_start)
SECTIONS
{
/* The kernel begins at the 1MB physical mark. */
. = 0x00100000;
- .text ALIGN(4k) : AT(ADDR(.text))
+ .text ALIGN(4k) :
{
*(.entry)
*(.text .text.*)
}
- .rodata ALIGN(4k) : AT(ADDR(.rodata))
+ .rodata ALIGN(4k) :
{
*(.rodata .rodata.*)
}
- .data ALIGN(4k) : AT(ADDR(.data))
+ .data ALIGN(4k) :
{
*(.data .data.*)
}
- /* Not actually *in* the image. */
- .bss ALIGN(4k) : AT(ADDR(.bss))
+ .bss ALIGN(4k) :
{
*(COMMON)
*(.bss .bss.*)
}
- .modules ALIGN(4k) : AT(ADDR(.modules))
+ .modules ALIGN(4k) :
{
PROVIDE(__modules_start = .);
*(.modules*)