I wrote a simple kernel that tries to write two characters to the frame buffer.
If I define a string literal in the kernel, I get the following output when loading:
Booting 'os' kernel /boot/kernel.elf Error 13: Invalid or unsupported executable format Press any key to continue...
Otherwise, if I define two characters, I get the following (note the βabβ at the beginning of the output):
abBooting 'os' kernel /boot/kernel.elf [Multiboot-elf, <0x100000:0x201:0x0>, <0x101000:0x0:0x1000>, shtab=0x102168, entry=0x1001f0]
Loader
I wrote the bootloader in the assembly:
global loader ; the entry symbol for ELF MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant FLAGS equ 0x0 ; multiboot flags CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum ; (magic number + checksum + flags should equal 0) KERNEL_STACK_SIZE equ 4096 ; size of stack in bytes section .text: ; start of the text (code) section align 4 ; the code must be 4 byte aligned dd MAGIC_NUMBER ; write the magic number to the machine code, dd FLAGS ; the flags, dd CHECKSUM ; and the checksum loader: ; the loader label (defined as entry point in linker script) mov eax, 0xCAFEBABE ; place the number 0xCAFEBABE in the register eax mov esp, kernel_stack + KERNEL_STACK_SIZE ; point esp to the start of the ; stack (end of memory area) extern run call run .loop: jmp .loop ; loop forever section .bss align 4 ; align at 4 bytes kernel_stack: ; label points to beginning of memory resb KERNEL_STACK_SIZE ; reserve stack for the kernel
The kernel is written in c
#include "io.h" #include "fb.h" void run() {
External headers
There are two external headers. One for I / O ports, called io.h, and one for writing to a frame buffer, called fb.h
Here io.h and implementation io.s
io.h:
#ifndef INCLUDE_IO_H #define INCLUDE_IO_H void outb(unsigned short port, unsigned char data); #endif
io.s:
global outb ; make the label outb visible outside this file ; outb - send a byte to an I/O port ; stack: [esp + 8] the data byte ; [esp + 4] the I/O port ; [esp ] return address outb: mov al, [esp + 8] mov dx, [esp + 4] out dx, al ret
fb.h
#include "io.h" // FRAME BUFFER ================================ // Text colors #define FB_BLACK 0 #define FB_BLUE 1 #define FB_GREEN 2 #define FB_CYAN 3 #define FB_RED 4 #define FB_MAGENTA 5 #define FB_BROWN 6 #define FB_LT_GREY 7 #define FB_DARK_GREY 8 #define FB_LT_BLUE 9 #define FB_LT_GREEN 10 #define FB_LT_CYAN 11 #define FB_LT_RED 12 #define FB_LT_MAGENTA 13 #define FB_LT_BROWN 14 #define FB_WHITE 15 // IO PORTS #define FB_COMMAND_PORT 0x3D4 #define FB_DATA_PORT 0x3D5 // IO PORT COMMANDS #define FB_HIGH_BYTE_COMMAND 14 // move cursor command low #define FB_LOW_BYTE_COMMAND 15 // move cursor command high /** fb_write_cell: * used to write a character to a cell in the framebuffer * * param i which cell to write to * param c the ascii char to write * param fg foreground color * param bf background color */ void fb_write_cell(unsigned int i, char c, unsigned char fg, unsigned char bg); /** fb_move_cursor: * used to move the cursor within the frame buffer * * param pos position within frame buffer to move cursor to */ void fb_move_cursor(unsigned short pos); /** fb_write: * write some text to the cursor * * param buf pointer to character string * param len length of string to write */ int fb_write(char *buf, unsigned int len);
fb.c
#include "fb.h" void fb_write_cell(unsigned int i, char c, unsigned char fg, unsigned char bg) { char *fb = (char *) 0x000B8000; fb[i*2] = c; fb[i*2 + 1] = ((fg & 0x0F) << 4) | (bg & 0x0F); } void fb_move_cursor(unsigned short pos) { outb(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND); outb(FB_DATA_PORT, ((pos>>8) & 0x00FF)); outb(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND); outb(FB_DATA_PORT, pos & 0x00FF); } int fb_write(char *buf, unsigned int len) { unsigned int i = 0; for(i = 0; i < len; i++) { fb_write_cell(i, buf[i], FB_BLACK, FB_WHITE); } return 0; }
Building it
I have a script builder called link.ld and Makefile. I am using gcc cross compiler for i386-elf. I compiled this guide ( http://wiki.osdev.org/GCC_Cross-Compiler ).
ENTRY(loader) SECTIONS { . = 0x00100000; .text ALIGN (0x1000) : { *(.text) } .rodata ALIGN (0x1000) : { *(.rodata*) } .data ALIGN (0x1000) : { *(.data) } .bss ALIGN (0x1000) : { sbss = .; *(COMMON) *(.bss) ebss = .; } }
And here is my makefile
OBJECTS = io.o fb.o loader.o kmain.o #CC = gcc CC = /home/albertlockett/opt/cross/bin/i386-elf-gcc CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \ -nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c LDFLAGS = -T link.ld -melf_i386 AS = nasm ASFLAGS = -f elf all: kernel.elf kernel.elf: $(OBJECTS) ld $(LDFLAGS) $(OBJECTS) -o kernel.elf os.iso: kernel.elf cp kernel.elf iso/boot/kernel.elf genisoimage -R \ -b boot/grub/stage2_eltorito \ -no-emul-boot \ -boot-load-size 4 \ -A os \ -input-charset utf8 \ -quiet \ -boot-info-table \ -o os.iso \ iso run: os.iso bochs -f bochsrc.txt -q %.o: %.c $(CC) $(CFLAGS) $< -o $@ %.o: %.s $(AS) $(ASFLAGS) $< -o $@ clean: rm -rf *.o kernel.elf os.iso
Run it
The make file creates from the contents of the iso directory. This folder contains the pre-configured version of grub I got here ( https://github.com/littleosbook/littleosbook/blob/master/files/stage2_eltorito ) and the menu.lst file for grub
menu.lst:
default=0 timeout=0 title os kernel /boot/kernel.elf
iso directory contents:
iso `-- boot |-- grub | |-- menu.lst | `-- stage2_eltorito `-- kernel.elf
The iso image is uploaded to bochs. Here is my bochsrc.txt file
megs: 32 display_library: term romimage: file=/usr/share/bochs/BIOS-bochs-latest vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest ata0-master: type=cdrom, path=os.iso, status=inserted boot: cdrom log: bochslog.txt clock: sync=realtime, time0=local cpu: count=1, ips=1000000 com1: enabled=1, mode=file, dev=com1.out
Does anyone know why the string literal in the kernel file causes an error when trying to load iso?