Hacking GRUB
************

This chapter documents the user-invisible aspect of GRUB.

As a general rule of software development, it is impossible to keep the descriptions of the internals up-to-date, and it is quite hard to document everything. So refer to the source code, whenever you are not satisfied with this documentation.  Please assume that this gives just hints to you.

The memory map of various components
====================================

GRUB consists of two distinct components, called stages, which are loaded at different times in the boot process. Because they run mutual-exclusively, sometimes a memory area overlaps with another memory area. And, even in one stage, a single memory area can be used for various purposes, because their usages are mutually exclusive.

Here is the memory map of the various components:

0 to 4K-1
     BIOS and real mode interrupts

0x07BE to 0x07FF
     Partition table passed to another boot loader

down from 8K-1
     Real mode stack

0x2000 to ?
     The optional Stage 1.5 is loaded here

0x2000 to 0x7FFF
     Command-line buffer for Multiboot kernels and modules

0x7C00 to 0x7DFF
     Stage 1 is loaded here by BIOS or another boot loader

0x7F00 to 0x7F42
     LBA drive parameters

0x8000 to ?
     Stage2 is loaded here

The end of Stage 2 to 416K-1
     Heap, in particular used for the menu

down from 416K-1
     Protected mode stack

416K to 448K-1
     Filesystem buffer

448K to 479.5K-1
     Raw device buffer

479.5K to 480K-1
     512-byte scratch area

480K to 512K-1
     Buffers for various functions, such as password, command-line, cut and paste, and completion.

The last 1K of lower memory
     Disk swapping code and data

See the file stage2/shared.h, for more information.

Embedded variables in GRUB
==========================

Stage 1 and Stage 2 have embedded variables whose locations are well-defined, so that the installation can patch the binary file directly without recompilation of the stages.

In Stage 1, these are defined:

0x3E
     The version number (not GRUB's, but the installation mechanism's).

0x40
     The boot drive. If it is 0xFF, use a drive passed by BIOS.

0x41
     The flag for if forcing LBA.

0x42
     The starting address of Stage 2.

0x44
     The first sector of Stage 2.

0x48
     The starting segment of Stage 2.

0x1FE
     The signature (0xAA55).

See the file stage1/stage1.S, for more information.

In the first sector of Stage 1.5 and Stage 2, the block lists are recorded between firstlist and lastlist. The address of lastlist is determined when assembling the file stage2/start.S.

The trick here is that it is actually read backward, and the first 8-byte block list is not read here, but after the pointer is decremented 8 bytes, then after reading it, it decrements again, reads, and so on, until it is finished. The terminating condition is when the number of sectors to be read in the next block list is zero.

The format of a block list can be seen from the example in the code just before the firstlist label. Note that it is always from the beginning of the disk, but not relative to the partition boundaries.

In the second sector of Stage 1.5 and Stage 2, these are defined:

0x6
     The version number (likewise, the installation mechanism's).

0x8
     The installed partition.

0xC
     The saved entry number.

0x10
     The identifier.

0x11
     The flag for if forcing LBA.

0x12
     The version string (GRUB's).

0x12 + the length of the version string
     The name of a configuration file.

See the file stage2/asm.S, for more information.

The generic interface for filesystems
=====================================

For any particular partition, it is presumed that only one of the normal filesystems such as FAT, FFS, or ext2fs can be used, so there is a switch table managed by the functions in disk_io.c. The notation is that you can only mount one at a time.

The block list filesystem has a special place in the system. In addition to the normal filesystem (or even without one mounted), you can access disk blocks directly (in the indicated partition) via the block list notation. Using the block list filesystem doesn't effect any other filesystem mounts.

The variables which can be read by the filesystem backend are:

current_drive
     The current BIOS drive number (numbered from 0, if a floppy, and numbered from 0x80, if a hard disk).

current_partition
     The current partition number.

current_slice
     The current partition type.

saved_drive
     The drive part of the root device.

saved_partition
     The partition part of the root device.

part_start
     The current partition starting address, in sectors.

part_length
     The current partition length, in sectors.

print_possibilities
     True when the dir function should print the possible completions of a file, and false when it should try to actually open a file of that name.

FSYS_BUF
     Filesystem buffer which is 32K in size, to use in any way which the filesystem backend desires.

The variables which need to be written by a filesystem backend are:

filepos
     The current position in the file, in sectors.

     Caution: the value of FILEPOS can be changed out from under the filesystem code in the current implementation. Don't depend on it being the same for later calls into the backend code!

filemax
     The length of the file.

disk_read_func
     The value of DISK_READ_HOOK only during reading of data for the file, not any other fs data, inodes, FAT tables, whatever, then set to NULL at all other times (it will be NULL by default). If this isn't done correctly, then the testload and install commands won't work correctly.

The functions expected to be used by the filesystem backend are:

devread
     Only read sectors from within a partition. Sector 0 is the first sector in the partition.

grub_read
     If the backend uses the block list code, then grub_read can be used, after setting BLOCK_FILE to 1.

print_a_completion
     If PRINT_POSSIBILITIES is true, call print_a_completion for each possible file name. Otherwise, the file name completion won't work.

The functions expected to be defined by the filesystem backend are described at least moderately in the file filesys.h. Their usage is fairly evident from their use in the functions in disk_io.c, look for the use of the FSYS_TABLE array.

Caution: The semantics are such that then mounting the filesystem, presume the filesystem buffer FSYS_BUF is corrupted, and (re-)load all important contents. When opening and reading a file, presume that the data from the mount is available, and doesn't get corrupted by the open/read (i.e. multiple opens and/or reads will be done with only one mount if in the same filesystem).

The generic interface for built-ins
===================================

GRUB built-in commands are defined in a uniformal interface, whether they are menu-specific or can be used anywhere. The definition of a builtin command consists of two parts: the code itself and the table of the information.

The code must be a function which takes two arguments, a command-line string and flags, and returns an int value. The flags argument specifies how the function is called, using a bit mask. The return value must be zero if successful, otherwise non-zero. So it is normally enough to return ERRNUM.

The table of the information is represented by the structure struct builtin, which contains the name of the command, a pointer to the function, flags, a short description of the command and a long description of the command. Since the descriptions are used only for help messages interactively, you don't have to define them, if the command may not be called interactively (such as title).

The table is finally registered in the table BUILTIN_TABLE, so that run_script and enter_cmdline can find the command. See the files cmdline.c and builtins.c, for more details.

The bootstrap mechanism used in GRUB
====================================

The disk space can be used in a boot loader is very restricted because a MBR (see MBR) is only 512 bytes but it also contains a partition table (see Partition table) and a BPB. So the question is how to make a boot loader code enough small to be fit in a MBR.

However, GRUB is a very large program, so we break GRUB into 2 (or 3) distinct components, Stage 1 and Stage 2 (and optionally Stage 1.5). See Memory map, for more information.

We embed Stage 1 in a MBR or in the boot sector of a partition, and place Stage 2 in a filesystem. The optional Stage 1.5 can be installed in a filesystem, in the boot loader area in a FFS or a ReiserFS, and in the sectors right after a MBR, because Stage 1.5 is enough small and the sectors right after a MBR is normally an unused region. The size of this region is the number of sectors per head minus 1.

Thus, all Stage1 must do is just load Stage2 or Stage1.5. But even if Stage 1 needs not to support the user interface or the filesystem interface, it is impossible to make Stage 1 less than 400 bytes, because GRUB should support both the CHS mode and the LBA mode (see Low-level disk I/O).

The solution used by GRUB is that Stage 1 loads only the first sector of Stage 2 (or Stage 1.5) and Stage 2 itself loads the rest. The flow of Stage 1 is:

 1. Initialize the system briefly.

 2. Detect the geometry and the accessing mode of the loading drive.

 3. Load the first sector of Stage 2.

 4. Jump to the starting address of the Stage 2.

The flow of Stage 2 (and Stage 1.5) is:

 1. Load the rest of itself to the real starting address, that is, the starting address plus 512 bytes. The block lists are stored in the last part of the first sector.

 2. Long jump to the real starting address.

Note that Stage 2 (or Stage 1.5) does not probe the geometry or the accessing mode of the loading drive, since Stage 1 has already probed them.

How to probe I/O ports used by INT 13H
======================================

FIXME: I will write this chapter after implementing the new technique.

How to detect all installed RAM
===============================

FIXME: I doubt if Erich didn't write this chapter only himself wholly, so I will rewrite this chapter.

INT 13H disk I/O interrupts
===========================

FIXME: I'm not sure where some part of the original chapter is derived, so I will rewrite this chapter.

The structure of Master Boot Record
===================================

FIXME: Likewise.

The format of partition tables
==============================

FIXME: Probably the original chapter is derived from "How It Works", so I will rewrite this chapter.

Where and how you should send patches
=====================================

When you write patches for GRUB, please send them to the mailing list bug-grub@gnu.org. Here is the list of items of which you should take care:

  * Please make your patch as small as possible. Generally, it is not a good thing to make one big patch which changes many things. Instead, segregate features and produce many patches.

  * Use as late code as possible, for the original code. The CVS repository always has the current version (see Obtaining and Building GRUB).

  * Write ChangeLog entries. See Change Logs (GNU Coding Standards), if you don't know how to write ChangeLog.

  * Make patches in unified diff format. diff -urN is appropriate in most cases.

  * Don't make patches reversely. Reverse patches are difficult to read and use.

  * Be careful enough of the license term and the copyright. Because GRUB is under GNU General Public License, you may not steal code from software whose license is incompatible against GPL. And, if you copy code written by others, you must not ignore their copyrights. Feel free to ask GRUB maintainers, whenever you are not sure what you should do.

  * If your patch is too large to send in e-mail, put it at somewhere we can see. Usually, you shouldn't send e-mail over 20K.