Page 1 of 1

Booting 64bit kernel

Posted: Sun May 24, 2020 11:39 am
by antoni
I'm just starting to program operating systems. I have already created a 32-bit OS that had a shell and was able to display the date or read data from the disk. Now I'd like to create a 64bit system. I saw article about this in osdev wiki - https://wiki.osdev.org/Creating_a_64-bit_kernel . However, I can't create code that GRUB2 is able to run. In my 32bit systems I used this code:

init.asm:

Code: Select all

global init

; some useful macro values
FLAGS		equ	0		; this is the multiboot 'flag' field
MAGIC		equ	0x1BADB002	; 'magic number' lets bootloader find the header
CHECKSUM	equ	-(MAGIC + FLAGS); checksum required
STACKSIZE	equ	0x4000		; 16 KiB for stack

section .text
align 4

; setting multiboot header
multiboot_header:

	dd	MAGIC
   	dd	FLAGS
   	dd	CHECKSUM

init:

	hlt         ;It was code calling kernel here.
	jmp init

section .bss
align 4

	stack:
   	resb 	STACKSIZE		; reserve stack space
make.sh:

Code: Select all

#!/bin/bash

nasm -f elf32 init.asm -o init.o
x86_64-elf-ld -T init.ld init.o -o init.bin -nostdlib -melf_i386

mkdir iso
cd iso
mkdir boot
cd boot
mkdir grub

cd ..
cd ..

cp init.bin iso/boot/grub
rm init.bin

cd iso
cd boot
cd grub

touch grub.cfg
printf "menuentry \'MY OS\' {\nmultiboot /boot/grub/init.bin\n}" > grub.cfg

cd ..
cd ..
cd ..

grub-mkrescue -o init.iso iso
rm -r iso
I replaced the kernel calling code with halt because my problem is only with booting. In the article it was written that I have to make a 32bit bootstrap, which I think means that I should give the BITS 32 directive and compile the code to elf64 like following:

Code: Select all

nasm -f elf64 init.asm -o init.o
x86_64-elf-ld -T init.ld init.o -o init.bin -nostdlib
instead of:

Code: Select all

nasm -f elf32 init.asm -o init.o
x86_64-elf-ld -T init.ld init.o -o init.bin -nostdlib -melf_i386
After this changes, I start OS with the command:

Code: Select all

qemu-system-x86_64 -hda init.iso
32bit code works. 64bit code shows the GRUB window, and after pressing "MY OS" information appears that GRUB didn't find multiboot header. Maybe 64bit multiboot header looks different? How to boot elf64 kernel with GRUB2?

Re: Booting 64bit kernel

Posted: Sun May 24, 2020 5:32 pm
by nexos
GRUB2 can only load 32 bit kernels. The common solution is to have boot.asm set up 64 bit mode. This requires a fair amount of overhead. You must set up 64 bit paging, a 64 bit Global Descriptor Table, and other things. I would recommend reading the [intel manuals/url], and look at the wiki article at [url]https://wiki.osdev.org/Long_Mode. I haven't implemented long mode before, however, so that is all I know.

Re: Booting 64bit kernel

Posted: Mon May 25, 2020 4:11 am
by antoni
nexos wrote:GRUB2 can only load 32 bit kernels.
I know. But it is compatible with elf64. For now, I just want to boot 32bit code in elf64 format. How to do it with GRUB2?

That's how I understand "32bit bootstrap". As 32bit code in elf64 format. May be I'm wrong?

Re: Booting 64bit kernel

Posted: Mon May 25, 2020 4:53 am
by iansjack
Why not make the 32-bit code ELF32 and the 64-bit code ELF64. You then load the 64-bit code using the "module" command in "grub.cfg". That way you're not trying to force new wine into old bottles.

Re: Booting 64bit kernel

Posted: Mon May 25, 2020 6:39 am
by antoni
I don't know much about GRUB and I didn't know it's possible. Do linux also loads this way? It looks like a trick, not method used in real operating systems.

Re: Booting 64bit kernel

Posted: Mon May 25, 2020 6:42 am
by pvc
AFAIK GRUB can load 64 bit ELF files perfectly fine, as long as physical load addresses fit in 32 bits. Your problem might be related to your linker script. You have to make sure that your multiboot_header symbol is located in the first 8KiB of the ELF file (open it in hex editor and try to locate 0x1BADB002 value manually).

For x86-64, ld often likes to align some sections to 4MiB instead of 4KiB. That could push multiboot_header way farther than required 8K. Try adding -z max-page-size=4096 to your ld invocation.

Re: Booting 64bit kernel

Posted: Mon May 25, 2020 6:56 am
by antoni
You're right. The problem was caused by linker script that looked like this:

Code: Select all

ENTRY (init)

SECTIONS {
    . = 0x00100000;
}
After changing to this:

Code: Select all

ENTRY (init)

SECTIONS {
    . = 0x001000;
}
I managed to boot. What address is a good starting point for kernel?

Re: Booting 64bit kernel

Posted: Mon May 25, 2020 7:20 am
by pvc
I would rather avoid anything below 1MiB, since there is quite some stuff going on there (IVT, BDA, EBDA, Video RAM window, BIOS, option ROM, part ot the GRUB itself, multiboot structures, SMP trampoline and maybe even more I forgot). 1MiB (0x100000) would be a good load address. You just need to make sure that multiboot_header is in the right place (it has to be below 8K and 4 byte aligned in the file, not in the memory).

Re: Booting 64bit kernel

Posted: Mon May 25, 2020 7:31 am
by MichaelPetch
pvc wrote:I would rather avoid anything below 1MiB, since there is quite some stuff going on there (IVT, BDA, EBDA, Video RAM window, BIOS, option ROM, part ot the GRUB itself, multiboot structures, SMP trampoline and maybe even more I forgot). 1MiB (0x100000) would be a good load address. You just need to make sure that multiboot_header is in the right place (it has to be below 8K and 4 byte aligned in the file, not in the memory).
I recently answered a [url=https://stackoverflow.com/questions/61904276/qemu-doesnt-reboot-my-os-after-resetting-via-the-8042-ps-2-controller/61944341#61944341] question on Stackoverflow
[/url] that caused some unusual behaviour when someone went to reboot QEMU. The problem was that they had told GRUB to load their multiboot header at 0x500. I wholeheartedly agree that you shouldn't be letting GRUB load below of 0x100000.

That's not to say that once GRUB is loaded you can't use the available (non-reserved, EBDA etc) and move code and data into that area afterwards.

Re: Booting 64bit kernel

Posted: Mon May 25, 2020 7:40 am
by pvc
MichaelPetch wrote:That's not to say that once GRUB is loaded you can't use the available (non-reserved, EBDA etc) and move code and data into that area afterwards.
Of course you can do that, if you know what you're doing. But that could possibly create unnecessary complications for a beginner. IMO, better to just avoid it for now.

Re: Booting 64bit kernel

Posted: Mon May 25, 2020 12:18 pm
by iansjack
antoni wrote:I don't know much about GRUB and I didn't know it's possible. Do linux also loads this way? It looks like a trick, not method used in real operating systems.
So why do you think GRUB allows the loading of modules?

Re: Booting 64bit kernel

Posted: Mon May 25, 2020 9:06 pm
by MichaelPetch
Assemble and Compile all your assembly and C/C++ (or whatever language you are using) to 64-bit ELF objects. Link them all together into a 64-bit static ELF executable. Use objcopy to convert the 64-bit ELF executable to a 32-bit ELF executable. If you use Multiboot1 the result should be loadable by GRUB legacy and GRUB 2, and QEMU's `-kernel` option.