Booting non-ELF kernel with GRUB tutorial
Posted: Tue Dec 01, 2009 11:08 am
Here are 2 basic tutorials to make GRUB booting non-ELF kernel, one for flat binary and one for PE format kernel.
Both have been tested on windows
Prerequisites
- A correct floppy with GRUB installed to match this menu.cfg
- nasm
- Visual C++ [Express edition is enough and free]
Flat binary kernel
the code init.asm
-Assemble with nasm : nasm -f bin -o init.bin init.asm
-copy init.bin at the floppy root
-test
PE Kernel
This is a bit more complicated
First you must setup a Visual C++ project named init like as describe in the famous BrokenThorn tutorials Setup Visual c++
Some modificatione are required
C/C++
in code generation set Enable Function level linking to yes (/Gy)
Linker
in command line set /ALIGN=1024
and add in Optimisation-> Functions order : @order.txt (@ is to avoid LNK warning)
Add a txt file order.txt (without @), leave blank
Add init .h file
Add init.cpp file
Build
Take a look at your map file, it should be like this :
Put this in your order.txt ?multiboot_entry@@YAXXZ
This ensure that when your kernel will growing, the multiboot_entry function will be at the beginning of file
- build
- copy the output file in the floppy root
- test
This is not a perfect tutorial but it is quite good to begin
Please report me any error, suggestion and your test result
Both have been tested on windows
Prerequisites
- A correct floppy with GRUB installed to match this menu.cfg
Code: Select all
default=0
timeout=0
title DNA Workstation
root (fd0)
kernel /init.bin
- Visual C++ [Express edition is enough and free]
Flat binary kernel
the code init.asm
Code: Select all
[map all init.map] ;Nasm directive to produce map file
bits 32 ;necessary to avoid Nasm to produce 16 bits code
MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 ;magic number, GRUB search for it in the first 8k
;of the specified file in GRUB menu
MULTIBOOT_HEADER_FLAGS equ 0x00010000 ;FLAGS[16] say to GRUB we are not
;an ELF executable and the fields
;header adress, load adress, load end adress;
;bss end adress and entry adress will be available
;in Multiboot header
CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
LOADBASE equ 0x00100000 ;Must be >= 1Mo
STACK_SIZE equ 0x4000
_start:
jmp multiboot_entry ;if you want use you own bootloader
align 4 ;Multiboot header must be 32
;bits aligned to avoid error 13
multiboot_header:
dd MULTIBOOT_HEADER_MAGIC ;magic number
dd MULTIBOOT_HEADER_FLAGS ;flags
dd CHECKSUM ;checksum
dd LOADBASE + multiboot_header ;header adress
dd LOADBASE ;load adress
dd 00 ;load end adress : not necessary
dd 00 ;bss end adress : not necessary
dd LOADBASE + multiboot_entry ;entry adress
multiboot_entry:
mov esp, stack + STACK_SIZE ;Setup the stack
push 0 ;Reset EFLAGS
popf
push ebx ;Push magic number and
push eax ;multiboot info adress
;which are loaded in registers
;eax and ebx before jump to
;entry adress
;[LOADBASE + multiboot_entry]
;call EXT_C(main) ; !! commented
;main (unsigned long magic, unsigned long addr)
;your kernel entry point
mov edi, 0xB8000 ;Msg to check the boot was OK
mov esi, hello
add esi, LOADBASE ;hello is just an offset
msg:
mov byte al, [esi]
cmp al, '\0'
je loop
mov ah, 0xa0
mov word [edi], ax
add edi, 2
inc esi
jmp msg
loop: hlt ;Halt processor
jmp loop
_edata:
hello:
db "Hello world from GRUB and flat binary kernel !\0"
align 4
times (128) db 0x00 ;foo data
stack:
align 4
times (STACK_SIZE) db 00
_end:
-copy init.bin at the floppy root
-test
PE Kernel
This is a bit more complicated
First you must setup a Visual C++ project named init like as describe in the famous BrokenThorn tutorials Setup Visual c++
Some modificatione are required
C/C++
in code generation set Enable Function level linking to yes (/Gy)
Linker
in command line set /ALIGN=1024
and add in Optimisation-> Functions order : @order.txt (@ is to avoid LNK warning)
Add a txt file order.txt (without @), leave blank
Add init .h file
Code: Select all
/* _emit is DB equivalent but not DD equivalent exist
so we define it ourself */
#define dd(x) \
__asm _emit (x) & 0xff \
__asm _emit (x) >> 8 & 0xff \
__asm _emit (x) >> 16 & 0xff \
__asm _emit (x) >> 24 & 0xff
#define KERNEL_STACK 0x00104000
/* This is the one of most important thing to be able to load a PE kernel
with GRUB. Because PE header are in the begining of the file, code section
will be shifted. The value used to shift the code section is the linker
align option /ALIGN:value. Note the header size sum is larger than 512,
so ALIGN value must be greater */
#define ALIGN 0x400
/* Must be >= 1Mo for GRUB
Base adress from advanced libker option
*/
#define LOADBASE 0x100000
#define HEADER_ADRESS LOADBASE+ALIGN
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
#define MULTIBOOT_HEADER_FLAGS 0x00010003
#define STACK_SIZE 0x4000
#define CHECKSUM -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
void main(unsigned long, unsigned long);
Code: Select all
// init.cpp : définit le point d'entrée pour l'application console.
//
#include "init.h"
__declspec(naked) void multiboot_entry(void)
{
__asm{
align 4
multiboot_header:
dd(MULTIBOOT_HEADER_MAGIC) ; magic number
dd(MULTIBOOT_HEADER_FLAGS) ; flags
dd(CHECKSUM) ; checksum
dd(HEADER_ADRESS) ; header address
dd(LOADBASE) ; load address
dd(00) ; load end address : not used
dd(00) ; bss end addr : not used
dd(HEADER_ADRESS + 0x20) ; entry_addr : equ kernel entry
; 0x20 is the size of multiboot heeader
kernel_entry:
mov esp, KERNEL_STACK ;Setup the stack
push 0 ;Reset EFLAGS
popf
push ebx ;Push multiboot info adress
push eax ;and magic number
;which are loaded in registers
;eax and ebx before jump to
;entry adress
;[HEADER_ADRESS + 0x20]
call main ;kernel entry
halt:
jmp halt ; halt processor
}
}
void main(unsigned long magic, unsigned long addr)
{
char *string = "Hello World from GRUB and PE kernel !", *ch;
unsigned short *vidmem = (unsigned short *) 0xB8000;
int i;
if (magic == MULTIBOOT_BOOTLOADER_MAGIC){
for(ch = string, i = 0; *ch; ch++, i++)
vidmem[i] = (unsigned char) *ch | 0xA000;
}else
{
// DO SOMETHING
}
}
Take a look at your map file, it should be like this :
Code: Select all
init
Timestamp is 4b1549e9 (Tue Dec 01 17:52:57 2009)
Preferred load address is 00100000
Start Length Name Class
0001:00000000 000000a5H .text CODE
0002:00000000 00000026H .rdata DATA
Address Publics by Value Rva+Base Lib:Object
0000:00000000 ___safe_se_handler_count 00000000 <absolute>
0000:00000000 ___safe_se_handler_table 00000000 <absolute>
0001:00000000 ?multiboot_entry@@YAXXZ 00100400 f init.obj
0001:00000040 _main 00100440 f init.obj
0002:00000000 ??_C@_0CG@IINAHCGJ@Hello?5World?5from?5GRUB?5and?5PE?5ker@ 00100800 init.obj
entry point at 0000:00000000
Static symbols
This ensure that when your kernel will growing, the multiboot_entry function will be at the beginning of file
- build
- copy the output file in the floppy root
- test
This is not a perfect tutorial but it is quite good to begin
Please report me any error, suggestion and your test result