================================================================
References
================================================================
This information was gleaned from my own experiments with GRUB,
from this GRUB tutorial:
	http://www.washingdishes.freeuk.com/grubtut.html
and from the following posts to alt.os.development:

Subject: Re: generic bootloader question
Newsgroups: alt.os.development
From: "Marv" <washingdishes@freeuk.com>
Date: Sat, 7 Apr 2001 23:35:20 +0100
References: <9antu8$glc$1@uni00nw.unity.ncsu.edu>
Message-ID: <986682856.680474@dionysos>

Subject: Re: Grub multiboot example
Newsgroups: alt.os.development
From: "Marv" <washingdishes@freeuk.com>
Date: Mon, 4 Jun 2001 17:21:17 +0100
References: <4a400d54.0106040458.5140872b@posting.google.com>
Message-ID: <eCOS6.56941$ML4.4674873@nnrp4.clara.net>

Subject: Re: Grub multiboot example
Newsgroups: alt.os.development
From: "Mike Wimpy" <mwimpy@pacifier.com.invalid>
Date: Thu, 7 Jun 2001 22:17:51 -0700
References: <4a400d54.0106040458.5140872b@posting.google.com> <3B1CDA6D.154ADD9D@127.0.0.1> <3B1CDAF9.300ED5E6@127.0.0.1> <3B204A02.14A11876@cisco.com> <3b2053e5_2@news.pacifier.com> <3B205CC8.1685DD4C@cisco.com>
Message-ID: <3b205ff8_2@news.pacifier.com>

Subject: Re: grub coff (solved it!)
Newsgroups: alt.os.development
From: "Mark & Candice White" <mhewii@home.com>
Date: Sun, 16 Sep 2001 10:57:34 GMT
References: <WQ_n7.105892$K6.42325710@news2>
Message-ID: <yA%o7.112386$K6.46237432@news2>

================================================================
Getting GRUB
================================================================
Home page:
        http://www.gnu.org/software/grub

Source code:
	ftp://alpha.gnu.org/gnu/grub/grub-0.90.tar.gz

Binaries:
	ftp://alpha.gnu.org/gnu/grub/grub-0.90-i386-pc.tar.gz

DOS and Windows users will need PARTCOPY or RAWRITE:
	http://my.execpc.com/~geezer/johnfine/index.htm#zero
	http://uranus.it.swin.edu.au/~jn/linux/rawwrite.htm
	http://www.tux.org/pub/dos/rawrite/

================================================================
Making a bootable floppy disk with the GRUB bootloader on it
================================================================
1. You will need
   - Two 1.44 meg floppy disks, one of them formatted with a
     filesystem that GRUB recognizes (e.g. FAT12 or ext2).
     The other floppy (the "unformatted" floppy) may contain
     a filesystem, but it will be destroyed.
   - The GRUB binaries: files "stage1" and "stage2"
   - A "menu.lst" configuration file for GRUB

2. On the formatted floppy disk, create the subdirectory
   "/boot/grub/", and copy the files "stage1", "stage2", and
   "menu.lst" into this subdirectory.

3. Concatenate the binary files "stage1" and "stage2" into a
   single binary file named "boot":
	(DOS/Windows):  copy /b stage1 + stage2 boot
	(Linux):        cat stage1 stage2 >boot

4. Write the file "boot" directly to the unformatted floppy.
   This is a sector-level write that does not use (and will
   destroy) any filesystem present on the disk:
	(DOS/Windows):  partcopy boot 0 168000 -f0
	(Linux):        cat boot >/dev/fd0

   PARTCOPY will display an error message because "boot" is
   much shorter than 0x168000 bytes, but this is OK. Don't
   use RAWRITE for this, because RAWRITE is not as reliable
   as PARTCOPY.

5. Boot your PC from the unformatted floppy disk.

6. After GRUB has started, eject the unformatted floppy and
   insert the formatted floppy, containing the "stage1",
   "stage2", and "menu.lst" files, all in the "/boot/grub/"
   subdirectory. Type:
	setup (fd0)

7. The formatted floppy is now bootable. Do not move, modify,
   or delete the file "/boot/grub/stage2" on this floppy.

================================================================
Making a Multiboot kernel
================================================================
Multiboot header
----------------
Whatever its file format, your kernel MUST have a Multiboot
header. This header
1. must be aligned on a dword (4-byte) boundary, and
2. must appear in the first 8K of the kernel file.

*** NOTE: An address within the first 8K of the .text section
is not necessarily within 8K of the start of the file.

ELF kernels
-----------
GRUB understands the ELF file format directly. If your kernel
is ELF, you can use the simple Multiboot header shown here:

    ; NASM syntax
    MULTIBOOT_PAGE_ALIGN   equ 1<<0
    MULTIBOOT_MEMORY_INFO  equ 1<<1

    MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
    MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
    CHECKSUM               equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

    ; The Multiboot header
        align 4
        dd MULTIBOOT_HEADER_MAGIC
        dd MULTIBOOT_HEADER_FLAGS
        dd CHECKSUM

Put this near the beginning of your kernel startup code, then
build your kernel. After the kernel is built, you should use
the GRUB "mbchk" utility to test if the kernel complies with
Multiboot.

*** NOTE: mbchk in GRUB v0.90 will sometimes report that a kernel
is Multiboot-compliant when it's not. This happens, for example,
if you use the Multiboot header, no aout kludge (see below), and
binary kernel file format.

Kernel load address
-------------------
GRUB reads the physical address (load address; LMA) of the
kernel from the ELF file. This value must be
1. at or above 1 meg, and
2. below the end of physical RAM

If the load address is below 1 meg, you get error #7:
        Loading below 1MB is not supported

NOTE: This is a limitation of GRUB, not of Multiboot.

If the load address is beyond the end of RAM, you get error #28:
        Selected item cannot fit into memory

And if you use a very high address like 0xC0000000, the math
apparently overflows, and you get error #7 again.

*** NOTE: "mbchk" does not check for these errors.

Error 7 and error 28 usually indicate that the kernel was not
properly linked. A very common cause of this is forgetting the
.rodata section(s) in the linker script for ELF kernels.

Normally, the physical address is the same as the VMA, and is
set either in the linker script or on the linker command line
("ld -Ttext=0x100000 ..."). If your version of 'ld' supports it,
the physical and virtual addresses can be specified separately
in the linker script using 'AT':

        ENTRY(entry)
        virt = 0xC0000000; /* 3 gig */
        phys = 0x100000; /* 1 meg */
        SECTIONS
        {   .text virt : AT(phys)
        /* 'g_code' marks the start of the code segment */
            {   g_code = .; _g_code = .;
                *(.text)
                *(.rodata*)         /* ELF read-only data section(s) */
                . = ALIGN(4096); }  /* align to page boundary */
            .data : AT(phys + (g_data - g_code))
        /* 'g_data' marks the start of the data segment */
	    {   g_data = .; _g_data = .;
		data = .;
		*(.data)
		. = ALIGN(4096); }
            .bss : AT(phys + (g_bss - g_code))
        /* 'g_bss' marks the start of the BSS */
	    {   g_bss = .; _g_bss = .;
		bss = .;
		*(.bss)
		*(COMMON)
		. = ALIGN(4096); }
        /* 'g_end' marks the end of the BSS */
	    g_end = .; _g_end = .; }

After linking, use 'objdump -h -p ...' or 'readelf -S -l ...' to
check if the addresses are all correct.

DJGPP COFF kernels and other file formats
-----------------------------------------
ELF binutils are available for DJGPP, but I don't know how
well they work, if they have bugs, etc.

It's better to use the "aout kludge" if you want GRUB to load
a non-ELF kernel. This uses additional fields at the end of the
Multiboot header, like this:

    MULTIBOOT_PAGE_ALIGN   equ 1<<0
    MULTIBOOT_MEMORY_INFO  equ 1<<1
    MULTIBOOT_AOUT_KLUDGE  equ 1<<16

    MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
    MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
    CHECKSUM               equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

    ; The Multiboot header
	align 4
    mboot:
	dd MULTIBOOT_HEADER_MAGIC
	dd MULTIBOOT_HEADER_FLAGS
	dd CHECKSUM
    ; fields used if MULTIBOOT_AOUT_KLUDGE is set in MULTIBOOT_HEADER_FLAGS
	dd mboot ; these are PHYSICAL addresses
	dd code  ; start of kernel .text (code) section
	dd edata ; end of kernel .data section
	dd end   ; end of kernel BSS
	dd start ; kernel entry point (initial EIP)

NOTE: The "aout kludge" works with binary and other file formats, too.

*** NOTE: GRUB v0.90 will ignore the aout kludge if it's present
in an ELF file.

*** NOTE: It is impossible to make a Multiboot-compatible kernel
using the ILINK32 linker, which is part of Borland C 5.5. Consider:
- ILINK32 can't make ELF files (only Win32 PE COFF), so you must
  use the aout kludge
- For the aout kludge to work, file alignment must equal section
  alignment
- If file alignment and section alignment are equal, ILINK32 will
  accept only the value 4096 for both alignments:
        ilink32 -Ao:4096 -Af:4096 ...
- Linking with this alignment puts the DOS .EXE stub in the first
  4096 bytes of the file, and the Win32 file header in the next
  4096 bytes of the file. .text does not start until file offset
  8192, which is beyond the 8192-byte limit of Multiboot.

================================================================
Booting!
================================================================
1.  Make sure your kernel is some convenient place where GRUB
    can find it. It need not be on the floppy disk.

2.  Boot from a GRUB floppy.

3.  (Optional) Tell GRUB what device to use for its root
    directory:

	root (hd0,1)

    This "mounts" the 2nd primary partition on the 1st hard
    drive as the root directory.

4.  Tell GRUB where your kernel is:

	kernel /krnl.elf

    If the kernel is on a different device than the one you
    booted from, you must specify the device explicitly at
    the start of each path name:

	kernel (hd0,1)/krnl.elf

5.  If GRUB has no complaints about the kernel file, boot it:

	boot

================================================================
Passing system information from GRUB to your kernel
================================================================
Upon entry to the 32-bit kernel
1. CS points to a code segment descriptor with base address 0
   and limit 4 gig - 1
2. DS, SS, ES, FS, and GS point to a data segment descriptor
   with base address 0 and limit 4 gig - 1
3. A20 is enabled
4. Paging is disabled
5. Interrupts are disabled. No IDT is defined.
6. The size and location of the GDT and selector values are
   undefined. Your kernel should create it's own GDT as soon
   as possible.
7. EAX=0x2BADB002
8. EBX contains the linear address of (i.e. a pointer to) a
   block of system and bootstrap information:

	/* The Multiboot information.  */
	typedef struct multiboot_info
	{
	  unsigned long flags;
/* conventional and extended memory sizes, in K
valid only if (flags & 0x0001)	(MB_INFO_MEMORY) */
	  unsigned long mem_lower;
	  unsigned long mem_upper;
/* valid only if (flags & 0x0002)	(MB_INFO_BOOTDEV) */
	  unsigned long boot_device;
/* valid only if (flags & 0x0004)	(MB_INFO_CMDLINE) */
	  unsigned long cmdline;
/* modules!
valid only if (flags & 0x0008)		(MB_INFO_MODS) */
	  unsigned long mods_count;
	  unsigned long mods_addr;
	  union
	  {
/* a.out symbol table
valid only if (flags & 0x0010)		(MB_INFO_AOUT_SYMS)*/
	    aout_symbol_table_t aout_sym;
/* ELF section header table
valid only if (flags & 0x0020)		(MB_INFO_ELF_SHDR) */
	    elf_section_header_table_t elf_sec;
	  } u;
/* detailed map of memory ranges
valid only if (flags & 0x0040)		(MB_INFO_MEM_MAP) */
	  unsigned long mmap_length;
	  unsigned long mmap_addr;
/* ...
there are additional fields; see the GRUB source code and docs
... */
	} multiboot_info_t;

The kernel startup code should store EBX into a global variable.

    ; in NASM kernel startup code:
	...
    ; no leading underscores for ELF
    EXTERN _g_mboot_data
    mov [_g_mboot_info],ebx
	...

Once this is done, the C code can access the information
supplied by the bootloader:

    /* in C kernel code */
    #include <_mb_info.h> /* MBOOT_FLAG_MEM, mboot_info_t */
    mboot_info_t *g_mboot_data;
    void init_mm(void) {
	if(!(g_mboot_data->flags & MBOOT_FLAG_MEM))
	    panic("bootloader did not set Multiboot memory fields");
	...

================================================================
Making a boot menu (file "menu.lst")
================================================================
Example 1:
	# Entry 0:
        title   WildMagnolia
        root    (fd0)
        kernel  /boot/kernel.elf
        module  /boot/mod_a
        module  /boot/mod_b

Example 2:
        #
        # Sample boot menu configuration file
        #

        #  default - boot the first entry.
        default 0

        # if have problem - boot the second entry.
        fallback 1

        # after 30 sec boot default.
        timeout 30

        # GNU Hurd
        title  GNU/Hurd
        root   (hd0,0)
        kernel /boot/gnumach.gz root=hd0s1
        module /boot/serverboot.gz

        # Linux - boot ot second HDD
        title  GNU/Linux
        kernel (hd1,0)/vmlinuz root=/dev/hdb1

        # booting Mach - get kernel from floppy
        title  Utah Mach4 multiboot
        root   (hd0,2)
        pause  Insert the diskette now!!
        kernel (fd0)/boot/kernel root=hd0s3
        module (fd0)/boot/bootstrap

        # booting OS/2
        title OS/2
        root  (hd0,1)
        makeactive
        # chainload OS/2 bootloader from the first sector
        chainloader +1

        # For booting Windows NT or Windows95
        title Windows NT / Windows 95 boot menu
        root        (hd0,0)
        makeactive
        chainloader +1
        # za boot na DOS ako Windows NT e instaliran
        # chainload /bootsect.dos

        # Colors change :0).
        title Change the colors
        color light-green/brown blink-red/blue

================================================================
Loading modules with the kernel
================================================================
Use the 'module' option in the 'menu.lst' file. Module files
will be loaded in their entirety, into extended memory, just
beyond the kernel.

================================================================
Other
================================================================
The kernel and modules can be compressed with GZIP. GRUB will
unzip the files automatically. If you want to load a compressed
module into memory without unzipping it, use 'modulenounzip'
instead of 'module' in 'menu.lst'

*** NOTE: The 'mbchk' supplied with GRUB version 0.90 will
report GZIPped kernels as unbootable.

