================================================================ 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" 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" Date: Mon, 4 Jun 2001 17:21:17 +0100 References: <4a400d54.0106040458.5140872b@posting.google.com> Message-ID: Subject: Re: Grub multiboot example Newsgroups: alt.os.development From: "Mike Wimpy" 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" Date: Sun, 16 Sep 2001 10:57:34 GMT References: Message-ID: ================================================================ 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.