I can only gather then you might have a version of GRUB that may not understand 64-bit ELF executables. What does the command
grub-mkconfig --version show for output? There is a way around this hassle though. I'm not sure it is captured in the Wiki articles or not (but it is the method I first used when I started my first 64-bit kernel when the 64-bit processors started becoming popular). You can use the MULTIBOOT AOUT KLUDGE (enabled by setting bit 16 in the multiboot flags) but it also requires a proper AOUT section right after the header that defines the start of the multiboot section, the beginning of the text section, the end of the data section, the end of the BSS section and the address of the entry point.
I'd modify your makefile to be this:
Code: Select all
ifeq ($(OS), Windows_NT)
$(warning Compiling in Windows? Wow you're insane)
$(warning If you are too lazy to install an GNU/Linux distro, use WSL!)
endif
CFLAGS = -g -c -std=gnu99 -ffreestanding -mno-red-zone -mno-sse -mno-sse2 -mno-sse3 -O3 -Wall -fomit-frame-pointer
# Compile and link everything
all:
$(info Compiling GryphusOS... Please wait.)
@nasm -f elf64 -Fdwarf -g bootg.asm -o bootg.o
@nasm -f elf64 -Fdwarf -g bootg2.asm -o bootg2.o
@nasm -f elf64 -Fdwarf -g api_test.asm -o api_test.o
@x86_64-elf-gcc $(CFLAGS) -masm=intel kmain.c -o kmain.o
@x86_64-elf-gcc $(CFLAGS) kernel/timer.c -o kernel/timer.o
@x86_64-elf-gcc $(CFLAGS) kernel/terminal.c -o kernel/terminal.o
@x86_64-elf-gcc $(CFLAGS) kernel/serial.c -o kernel/serial.o
@x86_64-elf-gcc $(CFLAGS) kernel/pic.c -o kernel/pic.o
@x86_64-elf-gcc $(CFLAGS) kernel/irq.c -o kernel/irq.o
@x86_64-elf-gcc $(CFLAGS) kernel/isrs.c -o kernel/isrs.o
@x86_64-elf-gcc $(CFLAGS) kernel/idt.c -o kernel/idt.o
@x86_64-elf-gcc $(CFLAGS) kernel/keyboard.c -o kernel/keyboard.o
@x86_64-elf-gcc $(CFLAGS) kernel/mouse.c -o kernel/mouse.o
@x86_64-elf-gcc $(CFLAGS) kernel/panic.c -o kernel/panic.o
@#x86_64-elf-gcc $(CFLAGS) -I../osproject kernel/paging.c -o kernel/paging.o
@x86_64-elf-gcc $(CFLAGS) kernel/cpu.c -o kernel/cpu.o
@x86_64-elf-gcc $(CFLAGS) kernel/cmos.c -o kernel/cmos.o
@x86_64-elf-gcc $(CFLAGS) kernel/fpc.c -o kernel/fpc.o
@x86_64-elf-gcc $(CFLAGS) kernel/rtc.c -o kernel/rtc.o
@x86_64-elf-gcc -std=c99 -nostartfiles -nostdlib -Tlink.ld -lgcc -z max-page-size=4096 -o os.elf \
bootg.o bootg2.o api_test.o kmain.o kernel/timer.o kernel/terminal.o kernel/pic.o kernel/serial.o kernel/idt.o kernel/irq.o kernel/isrs.o \
kernel/keyboard.o kernel/mouse.o kernel/panic.o kernel/cpu.o kernel/cmos.o kernel/fpc.o kernel/rtc.o
@x86_64-elf-objcopy -O binary os.elf os.bin
@mkdir -p isodir/boot/grub
@cp os.bin isodir/boot/os.bin
@cp grub.cfg isodir/boot/grub/grub.cfg
@grub-mkrescue -o os.iso isodir
clear:
rm *.o *.elf
# Compile and link only the bootloader
bootloader:
nasm -f elf64 -Fdwarf -g bootg.asm -o bootg.o
#yasm -f elf32 bootg.asm -o bootg.o
x86_64-elf-gcc -std=c99 -nostartfiles -nostdlib -Tlink.ld -lgcc -o os.elf \
bootg.o api_test.o kmain.o kernel/timer.o kernel/terminal.o kernel/pic.o kernel/serial.o kernel/idt.o kernel/irq.o kernel/isrs.o \
kernel/keyboard.o kernel/mouse.o kernel/panic.o kernel/paginga.o kernel/paging.o kernel/cpu.o kernel/cmos.o kernel/fpc.o kernel/rtc.o
run:
@qemu-system-x86_64 -cdrom os.iso
An important note is that I create os.elf and os.bin . The ELF file is a normal 64-bit elf executable. os.bin is the binary version of os.elf. You then modify grub.cfg to use the bin file:
Code: Select all
set timeout=10
menuentry "Gryphus" {
echo Gryphus is loading. Please wait.
multiboot /boot/os.bin
}
submenu "About GryphusOS..."{
echo Gryphus aims to be a modern and an monolithic kernel.
echo Maybe later I have more info to put here :D
echo "Gitlab repo: gitlab.com/hextakatt/gryphus/"
read
}
You also have to modify the linker script to create symbols that point to the needed memory locations required by a mulitboot AOUT kludge. I have also added * to .rodata and .text since it is possible for there to be a number of sections that start with .text and .rodata:
Code: Select all
/* The bootloader will look at this image and start execution at the symbol
designated as the entry point. */
ENTRY(start)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* Begin putting sections at 1 MiB, a conventional place for kernels to be
loaded at by the bootloader. */
. = 1M;
/* First put the multiboot header, as it is required to be put very early
early in the image or the bootloader won't recognize the file format.
Next we'll put the .text section. */
.text BLOCK(4K) : ALIGN(4K)
{
__text_start = .;
*(.multiboot)
*(.text*)
__text_end = .;
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
{
__data_start = .;
*(.rodata*)
}
/* Read-write data (initialized) */
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
__data_end = .;
}
/* Read-write data (uninitialized) and stack */
.bss BLOCK(4K) : ALIGN(4K)
{
__bss_start = .;
*(COMMON)
*(.bss)
__bss_end = .;
__endkernel = .;
}
/* The compiler may produce other sections, by default it will put them in
a segment with the same name. Simply add stuff here as needed. */
/* Remove unnecessary sections */
/DISCARD/ : {
*(.eh_frame);
*(.comment);
}
}
You then have to modify bootg.asm so that it enables the AOUT kludge and provides the required extra fields with information the multiboot loader (GRUB) needs to parse the binary:
Code: Select all
; Gryphus GRUB bootstrapper, hextakatt 2019
%include "gdt.inc"
global start
extern kernelmain
extern kputs
extern long_mode_start
extern __text_start
extern __data_end
extern __bss_end
bits 32
; This is the GRUB multiboot header
section .multiboot
align 4
mboot:
MULTIBOOT_PAGE_ALIGN equ 1 << 0
MULTIBOOT_MEMORY_INFO equ 1 << 1
MULTIBOOT_VIDEO_MODE equ 1 << 2
MULTIBOOT_AOUT_KLUDGE equ 1 << 16
MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd MULTIBOOT_CHECKSUM
dd mboot
dd __text_start
dd __data_end
dd __bss_end
dd start
section .text
check_cpuid:
pushfd
pop eax
mov ecx, eax
; Flip the ID bit
xor eax, 1 << 21
; Copy EAX to FLAGS via the stack
push eax
popfd
; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported)
pushfd
pop eax
; Restore FLAGS from the old version stored in ECX (i.e. flipping the
; ID bit back if it was ever flipped).
push ecx
popfd
; Compare EAX and ECX. If they are equal then that means the bit
; wasn't flipped, and CPUID isn't supported.
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
;mov ecx, no_cpuid_str
;push ecx
;call kputs
;pop ecx
cli
hlt
check_long_mode:
; test if extended processor info in available
mov eax, 0x80000000 ; implicit argument for cpuid
cpuid ; get highest supported argument
cmp eax, 0x80000001 ; it needs to be at least 0x80000001
jb .no_long_mode ; if it's less, the CPU is too old for long mode
; use extended info to test if long mode is available
mov eax, 0x80000001 ; argument for extended processor info
cpuid ; returns various feature bits in ecx and edx
test edx, 1 << 29 ; test if the LM-bit is set in the D-register
jz .no_long_mode ; If it's not set, there is no long mode
ret
.no_long_mode:
;mov ecx, no_long_str
;push ecx
;call kputs
;pop ecx
cli
hlt
set_up_page_tables:
; map first P4 entry to P3 table
mov eax, p3_table
or eax, 0b11 ; present + writable
mov [p4_table], eax
; map first P3 entry to P2 table
mov eax, p2_table
or eax, 0b11 ; present + writable
mov [p3_table], eax
mov ecx, 0
.map_p2_table:
; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx
mov eax, 0x200000 ; 2MiB
mul ecx ; start address of ecx-th page
or eax, 0b10000011 ; present + writable + huge
mov [p2_table + ecx * 8], eax ; map ecx-th entry
inc ecx ; increase counter
cmp ecx, 512 ; if counter == 512, the whole P2 table is mapped
jne .map_p2_table ; else map the next entry
ret
enable_paging:
; load P4 to cr3 register (cpu uses this to access the P4 table)
mov eax, p4_table
mov cr3, eax
; enable PAE-flag in cr4 (Physical Address Extension)
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
; set the long mode bit in the EFER MSR (model specific register)
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
; enable paging in the cr0 register
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
ret
start:
mov esp, _sys_stack_t
push eax
push ebx
; Check for CPUID support
call check_cpuid
call check_long_mode
call set_up_page_tables
call enable_paging
pop ebx
pop eax
lgdt [gdt64.pointer]
jmp gdt64.code:long_mode_start
section .data:
;no_cpuid_str: db "CPUID is not supported.", 0x0A, 0x0D, 0
;no_long_str: db "Long mode (x86-64) not supported", 0x0A, 0x0D, 0
section .bss
align 4096
p4_table:
resb 4096
p3_table:
resb 4096
p2_table:
resb 4096
_sys_stack_b:
resb 64
_sys_stack_t:
If you haven't already done so you should consider making the other bugfixes I mentioned in bootg2.asm:
Code: Select all
; Gryphus GRUB bootstrapper, hextakatt 2019
global long_mode_start
extern kernelmain
bits 64
%include "idt.inc"
section .text
long_mode_start:
; All segment registers to zero
mov cx, 0
mov ss, cx
mov ds, cx
mov es, cx
mov fs, cx
mov gs, cx
call idt_load
; Push GRUB registers
mov edi, ebx
mov esi, eax
; Calls kernel entry point
call kernelmain
; Jumps to the end routine, thats halts the system
jmp end
; Suspends the processor, disable interrupts and halt CPU
end:
; Clear interrupts and halt the processor
cli
hlt