How to start 64-bit kernel using grub2 in its module option
Posted: Fri Mar 29, 2024 9:31 am
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?
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?