I followed the tutorial series on Write your own operating system in 1 hour to create a basic OS to print just "Hello World" with just 4 files : Makefile
, kernel.cpp
, loader.s
and linker.ld
.
I am creating a mykernel.iso
file but when I boot it into VirtualBox, I get the error "Could not read from boot medium : System halted". I confirmed that the .iso
file is linked with my machine instance. Looks like there is some other problem in the code perhaps.
Here is my Makefile:
#we need to tell the compiler to stop assuming that this will be executed inside an OS
CPPPARAMS = -m32 -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -fno-exceptions -fno-leading-underscore
ASPARAMS = --32
LDPARAMS = -melf_i386
objects = loader.o kernel.o
%.o: %.cpp
g++ $(CPPPARAMS) -o $@ -c $<
%.o: %.s
as $(ASPARAMS) -o $@ $<
mykernel.bin: linker.ld $(objects)
ld $(LDPARAMS) -T $< -o $@ $(objects)
install: mykernel.bin
sudo cp $< /boot/mykernel.bin
mykernel.iso: mykernel.bin
mkdir iso
mkdir iso/boot
mkdir iso/boot/grub
cp $< iso/boot/
echo 'set default=0' > iso/boot/grub/grub.cfg
echo 'set timeout=0' >> iso/boot/grub/grub.cfg
echo '' >> iso/boot/grub/grub.cfg
echo 'menuentry "My Personal OS" {' >> iso/boot/grub/grub.cfg
echo 'multiboot /boot/mykernel.bin' >> iso/boot/grub/grub.cfg
echo 'boot' >> iso/boot/grub/grub.cfg
echo '}' >> iso/boot/grub/grub.cfg
grub-mkrescue --output $@ iso
rm -rf iso
clean:
rm -rf iso
rm *.o
rm mykernel.iso
rm mykernel.bin
Here is the kernel.cpp
void printf(char *str)
{
unsigned short *VideoMemory = (unsigned short*)0xb8000;
for(int i=0;str[i] != '\0';i++)
VideoMemory[i] = (VideoMemory[i] & 0xFF00) | str[i];
}
typedef void (*constructor)();
extern "C" constructor start_ctors;
extern "C" constructor end_ctors;
extern "C" void callConstructors()
{
for(constructor * i=&start_ctors;i!=&end_ctors;i++)
(*i)();
}
extern "C" void kernelMain(void * multiboot_structure, unsigned int magic_number)
{
printf("Hello World!");
//we do not want to exit from the kernel
while(1);
}
Here is the loader.s
:
.set MAGIC, 0x1badb002
.set FLAGS, (1<<0 | 1<<1)
.set CHECKSUM, -(MAGIC + FLAGS)
.section .multiboot
.long MAGIC
.long FLAGS
.long CHECKSUM
.section .text
.extern kernelMain
.extern callConstructors
.global loader
loader:
mov $kernel_stack, %esp
call callConstructors
push %eax #AX register has the pointer of multiboot structure stored by bootloader
push %ebx #BX register has the magic number
call kernelMain
#double check to not come out of the kernel, creating one more loop
_stop:
cli
hlt
jmp _stop
.section .bss
.space 2*1024*1024 #2MB for stack to grow towards left side
kernel_stack:
Here is the linker.ld
:
ENTRY(loader)
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386:i386)
SECTIONS
{
. = 0x100000;
.text :
{
*(.multiboot)
*(.text*)
*(.rodata)
}
.data :
{
start_ctors = .;
KEEP(*(.init_array));
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)));
end_ctors = .;
*(.data)
}
.bss :
{
*(.bss)
}
/DISCARD/ :
{
*(.fini_array*)
*(.comment)
}
}
My development environment is Linux Mint 18.1 64-bit with virtualbox installed. My code almost matches the code of the tutor in the series, still I am not able to boot in the virtual machine.
EDIT
I tried with qemu-system-i386 -kernel mykernel.bin
and it works fine with a message Hello World
.That means there is some problem with VirtualBox environment and configurations.
I have no official source for this answer. It is actually based on experience and other questions I have seen on Stackoverflow and some findings I have made.
It appears if you create large kernel bootstrap stacks in the BSS segment it causes GRUB to crash in some environments and not others. This often happens when the total size of the BSS segment seems to reach about 2mb. Virtualbox seems to be a particular case where the issue seems to arise. Issues in Virtualbox seems to vary depending on the version and the virtual hardware configuration being used.
The stack you create in your loader.s
to bootstrap your C++ environment doesn't need to be all that big. Once you get your memory management and allocators in place you can reserve area for a larger kernel stack and set SS:ESP to it at that time.
To that end you should consider changing:
.section .bss
.space 2*1024*1024 #2MB for stack to grow towards left side
kernel_stack:
To something 1mb or smaller. I'd probably go with something like 64kb:
.section .bss
.space 64*1024 #64KB for stack to grow towards left side
kernel_stack: