Go to the first, previous, next, last section, table of contents.
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.
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:
See the file `stage2/shared.h', for more information.
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
0x40
0x41
0x42
0x44
0x48
0x1FE
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
0x8
0xC
0x10
0x11
0x12
0x12
+ the length of the version string
See the file `stage2/asm.S', for more information.
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
current_partition
current_slice
saved_drive
saved_partition
part_start
part_length
print_possibilities
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
The variables which need to be written by a filesystem backend are:
filepos
filemax
disk_read_func
NULL
at all other times (it will be NULL
by
default). If this isn't done correctly, then the @command{testload} and
@command{install} commands won't work correctly.
The functions expected to be used by the filesystem backend are:
devread
grub_read
grub_read
can be
used, after setting block_file to 1.
print_a_completion
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 `mount'ing 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).
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 @command{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 disk space can be used in a boot loader is very restricted because a MBR (see section The structure of Master Boot Record) is only 512 bytes but it also contains a partition table (see section The format of partition tables) 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 section The memory map of various components, 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 section INT 13H disk I/O interrupts).
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:
The flow of Stage 2 (and Stage 1.5) is:
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.
FIXME: I will write this chapter after implementing the new technique.
FIXME: I doubt if Erich didn't write this chapter only himself wholly, so I will rewrite this chapter.
FIXME: I'm not sure where some part of the original chapter is derived, so I will rewrite this chapter.
FIXME: Likewise.
FIXME: Probably the original chapter is derived from "How It Works", so I will rewrite this chapter.
When you write patches for GRUB, please send them to the mailing list [email protected]. Here is the list of items of which you should take care:
Go to the first, previous, next, last section, table of contents.