Page 1 of 1

Booting non-ELF kernel with GRUB tutorial

Posted: Tue Dec 01, 2009 11:08 am
by gedd
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

Code: Select all

default=0
timeout=0
title   DNA Workstation
root    (fd0)
kernel  /init.bin
- nasm
- 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:
 
-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

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);
Add init.cpp file

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
	}
}
Build

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

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

Re: Booting non-ELF kernel with GRUB tutorial by a prick

Posted: Tue Dec 01, 2009 12:04 pm
by madeofstaples
Interesting, but I was a little disappointed that none of your comments or your tutorial in general made you sound like a prick, as promised by the thread title.

It looks like a useful tutorial, I admit I skimmed it a bit because I'm not currently working on anything related, but I may save a copy for reference later.

The only thing I did notice is that you should use the hlt instruction between the "halt" label and "jmp halt" command in init.cpp; perhaps disabling maskable interrupts with cli before the "halt" label.

Re: Booting non-ELF kernel with GRUB tutorial by a prick

Posted: Tue Dec 01, 2009 12:36 pm
by gedd
The prick was a reference from another thread wich have been locked.

Few year ago hlt instruction doesn't work fine in some virtualizer, it's an hold habit tu use infinite loop.
The forgoten CLI is clearly a mistake

Re: Booting non-ELF kernel with GRUB tutorial by a prick

Posted: Tue Dec 01, 2009 1:02 pm
by neon
Id remove that "reference" from the thread title. Its nice that you are sharing self written tutorials, I would keep it at that :)

Re: Booting non-ELF kernel with GRUB tutorial

Posted: Mon Jan 11, 2010 12:07 pm
by Love4Boobies
Can you really be far even as decided half as much to use go wish for that?

Re: Booting non-ELF kernel with GRUB tutorial

Posted: Tue Jan 12, 2010 2:37 am
by gedd
Sorry for my question but i'm not understand your sentence
I 'm not english, can you rewrite ?