How to start 64-bit kernel using grub2 in its module option

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
TYDQSoft
Posts: 8
Joined: Sun Mar 17, 2024 9:11 am
Libera.chat IRC: TYDQSoft

How to start 64-bit kernel using grub2 in its module option

Post by TYDQSoft »

I have complied two kernel which is for 32-bits to boot the 64-bits kernel and the 64-bit kernel,I know grub2 cannot boot 64-bit kernel but can boot 32-bit kernel,so I am trying to adding the 64-bit kernel by booting 32-bit kernel and place the 64-bit kernel in grub.cfg,This are my codes(code is written in pascal and assembly):

boot/entry.asm:
global start
extern long_mode_start

section .text
bits 32
start:
mov esp, stack_top

call check_multiboot
call check_cpuid
call check_long_mode

call setup_page_tables
call enable_paging

lgdt [gdt64.pointer]
jmp gdt64.code_segment:long_mode_start

hlt

check_multiboot:
cmp eax, 0x36d76289
jne .no_multiboot
ret
.no_multiboot:
mov al, "M"
jmp error

check_cpuid:
pushfd
pop eax
mov ecx, eax
xor eax, 1 << 21
push eax
popfd
pushfd
pop eax
push ecx
popfd
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
mov al, "C"
jmp error

check_long_mode:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb .no_long_mode

mov eax, 0x80000001
cpuid
test edx, 1 << 29
jz .no_long_mode

ret
.no_long_mode:
mov al, "L"
jmp error

setup_page_tables:
mov eax, page_table_l3
or eax, 0b11 ; present, writable
mov [page_table_l4], eax

mov eax, page_table_l2
or eax, 0b11 ; present, writable
mov [page_table_l3], eax

mov ecx, 0 ; counter
.loop:

mov eax, 0x200000 ; 2MiB
mul ecx
or eax, 0b10000011 ; present, writable, huge page
mov [page_table_l2 + ecx * 8], eax

inc ecx ; increment counter
cmp ecx, 512 ; checks if the whole table is mapped
jne .loop ; if not, continue

ret

enable_paging:
; pass page table location to cpu
mov eax, page_table_l4
mov cr3, eax

; enable PAE
mov eax, cr4
or eax, 1 << 5
mov cr4, eax

; enable long mode
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr

; enable paging
mov eax, cr0
or eax, 1 << 31
mov cr0, eax

ret

error:
; print "ERR: X" where X is the error code
mov dword [0xb8000], 0x4f524f45
mov dword [0xb8004], 0x4f3a4f52
mov dword [0xb8008], 0x4f204f20
mov byte [0xb800a], al
hlt

section .bss
align 4096
page_table_l4:
resb 4096
page_table_l3:
resb 4096
page_table_l2:
resb 4096
stack_bottom:
resb 4096 * 4
stack_top:

section .rodata
gdt64:
dq 0 ; zero entry
.code_segment: equ $ - gdt64
dq (1 << 43) | (1 << 44) | (1 << 47) | (1 << 53) ; code segment
.pointer:
dw $ - gdt64 - 1 ; length
dq gdt64 ; address

boot/entry64.asm:
global long_mode_start
extern kernel_main

section .text
bits 64
long_mode_start:
; load null into all data segment registers
mov ax, 0
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax

call kernel_main
hlt

boot/header.asm:
section .multiboot_header
header_start:
; magic number
dd 0xe85250d6 ; multiboot2
; architecture
dd 0 ; protected mode i386
; header length
dd header_end - header_start
; checksum
dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start))

; end tag
dw 0
dw 0
dd 8
header_end:

kernel/kernel.pas:
unit kernel;

interface

procedure kernel_main;

implementation

procedure kernel_main;[public,alias:'kernel_main'];
begin
while(True) do;
end;

end.

kernel/system.pas:
unit system;

{$MODE FPC}

interface

type
cardinal = 0..$FFFFFFFF;
hresult = cardinal;
dword = cardinal;
integer = longint;
pinteger = ^integer;
pdword = ^dword;
pchar = ^char;

{ That part comes from fpc 3.2.0 as nessesary}
TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat, tkSet,
tkMethod, tkSString, tkLString, tkAString, tkWString, tkVariant, tkArray,
tkRecord, tkInterface, tkClass, tkObject, tkWChar, tkBool, tkInt64, tkQWord,
tkDynArray, tkInterfaceRaw, tkProcVar, tkUString, tkUChar, tkHelper, tkFile,
tkClassRef, tkPointer);

jmp_buf = packed record
rbx, rbp, r12, r13, r14, r15, rsp, rip: QWord;
{$IFDEF CPU64}
rsi, rdi: QWord;
xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15: record
m1, m2: QWord;
end;

mxcsr: LongWord;
fpucw: word;
padding: word;
{$ENDIF CPU64}
end;

Pjmp_buf = ^jmp_buf;
PExceptAddr = ^TExceptAddr;
TExceptAddr = record
buf: Pjmp_buf;
next: PExceptAddr;
{$IFDEF CPU16}
frametype: SmallInt;
{$ELSE CPU16}
frametype: LongInt;
{$ENDIF CPU16}
end;

PGuid = ^TGuid;
TGuid = packed record
case Integer of
1:
(Data1: DWord;
Data2: word;
Data3: word;
Data4: array [0 .. 7] of byte;
);
2:
(D1: DWord;
D2: word;
D3: word;
D4: array [0 .. 7] of byte;
);
3:
( { uuid fields according to RFC4122 }
time_low: DWord; // The low field of the timestamp
time_mid: word; // The middle field of the timestamp
time_hi_and_version: word;
// The high field of the timestamp multiplexed with the version number
clock_seq_hi_and_reserved: byte;
// The high field of the clock sequence multiplexed with the variant
clock_seq_low: byte; // The low field of the clock sequence
node: array [0 .. 5] of byte; // The spatially unique node identifier
);
end;

{ --- End of nessesary part --- }

implementation

end.

linker.ld:
ENTRY(start)

SECTIONS
{
. = 0x100000;

.boot :
{
KEEP(*(.multiboot_header))
}

.text :
{
*(.text)
}
.rodata :
{
*(.rodata)
}
.bss :
{
*(.bss)
}
}

linker32.ld:
OUTPUT_FORMAT(elf32-i386)
SECTIONS
{
. = 0x100000;

.boot :
{
KEEP(*(.multiboot_header))
}

.text :
{
*(.text)
}
.rodata :
{
*(.rodata)
}
.bss :
{
*(.bss)
}
}

Makefile:
# Makefile - life is too short for running commands manually

OBJECTS32=boot/header.o boot/entry.o boot/entry64.o
OBJECTS64=kernel/kernel.o kernel/system.o
PPCFLAGS=-Aelf -n -Si -Sc -Sg -Xd -CX -XXs -Px86_64 -Rintel -Tlinux
TMPISO=iso
TMPBOOT=$(TMPISO)/boot
TMPGRUB=$(TMPBOOT)/grub
TMPCFG=$(TMPGRUB)/grub.cfg

all:_kernel _boot kernel.obj makeiso qemu2

_kernel:
fpc $(PPCFLAGS) kernel/kernel.pas

_boot:
nasm -f elf32 boot/entry.asm -o boot/entry.o
nasm -f elf32 boot/entry64.asm -o boot/entry64.o
nasm -f elf32 boot/header.asm -o boot/header.o

kernel.obj:
ld -melf_x86_64 -Tlinker.ld $(OBJECTS64) -o kernel64.obj
ld -melf_i386 -Tlinker32.ld $(OBJECTS32) -o kernel.obj

makeiso:
mkdir $(TMPISO)
mkdir $(TMPBOOT)
mkdir $(TMPGRUB)
cp kernel.obj $(TMPBOOT)/kernel.obj
cp kernel64.obj $(TMPBOOT)/kernel64.obj
echo 'set timeout=0' >> $(TMPCFG)
echo 'set default =0' >> $(TMPCFG)
echo 'set gfxmode=320*200' >> $(TMPCFG)
echo '' >> $(TMPCFG)
echo 'menuentry "Pascal OS" {' >> $(TMPCFG)
echo ' multiboot2 /boot/kernel.obj' >> $(TMPCFG)
echo ' module /boot/kernel64.obj' >> $(TMPCFG)
echo ' boot' >> $(TMPCFG)
echo '}' >> $(TMPCFG)
grub2-mkrescue --output=pascal-kernel.iso iso
rm -rf $(TMPISO)

qemu1:
qemu-system-x86_64 --kernel kernel.obj -d in_asm

qemu2:
qemu-system-x86_64 -cdrom pascal-kernel.iso -d in_asm

clean:
rm -rf kernel.obj
rm boot/*.o
rm kernel/*.ppu
rm kernel/*.o
rm *.iso

All of code is above,but I got a total failure when I test my iso file and I don't know why.Can you solve my problem please?
nullplan
Member
Member
Posts: 1779
Joined: Wed Aug 30, 2017 8:24 am

Re: How to start 64-bit kernel using grub2 in its module opt

Post by nullplan »

I suggest putting the code into some kind of online repository, like github. Then people can peruse it at their leisure.

I also suggest using the

Code: Select all

 tag next time, but here it really wouldn't have helped much.
Carpe diem!
Post Reply