mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-21 23:02:40 +00:00
428 lines
13 KiB
Plaintext
428 lines
13 KiB
Plaintext
@node Internals
|
|
@chapter 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.
|
|
|
|
@menu
|
|
* Memory map:: The memory map of various components
|
|
* Embedded data:: Embedded variables in GRUB
|
|
* Filesystem interface:: The generic interface for filesystems
|
|
* Command interface:: The generic interface for built-ins
|
|
* Bootstrap tricks:: The bootstrap mechanism used in GRUB
|
|
* I/O ports detection:: How to probe I/O ports used by INT 13H
|
|
* Memory detection:: How to detect all installed RAM
|
|
* Low-level disk I/O:: INT 13H disk I/O interrupts
|
|
* MBR:: The structure of Master Boot Record
|
|
* Partition table:: The format of partition tables
|
|
* Submitting patches:: Where and how you should send patches
|
|
@end menu
|
|
|
|
|
|
@node Memory map
|
|
@section The memory map of various components
|
|
|
|
GRUB consists of two distinct components, called @dfn{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:
|
|
|
|
@table @asis
|
|
@item 0 to 4K-1
|
|
BIOS and real mode interrupts
|
|
|
|
@item 0x07BE to 0x07FF
|
|
Partition table passed to another boot loader
|
|
|
|
@item down from 8K-1
|
|
Real mode stack
|
|
|
|
@item 0x2000 to ?
|
|
The optional Stage 1.5 is loaded here
|
|
|
|
@item 0x2000 to 0x7FFF
|
|
Command-line buffer for Multiboot kernels and modules
|
|
|
|
@item 0x7C00 to 0x7DFF
|
|
Stage 1 is loaded here by BIOS or another boot loader
|
|
|
|
@item 0x7F00 to 0x7F42
|
|
LBA drive parameters
|
|
|
|
@item 0x8000 to ?
|
|
Stage2 is loaded here
|
|
|
|
@item The end of Stage 2 to 416K-1
|
|
Heap, in particular used for the menu
|
|
|
|
@item down from 416K-1
|
|
Protected mode stack
|
|
|
|
@item 416K to 448K-1
|
|
Filesystem buffer
|
|
|
|
@item 448K to 479.5K-1
|
|
Raw device buffer
|
|
|
|
@item 479.5K to 480K-1
|
|
512-byte scratch area
|
|
|
|
@item 480K to 512K-1
|
|
Buffers for various functions, such as password, command-line, cut and
|
|
paste, and completion.
|
|
|
|
@item The last 1K of lower memory
|
|
Disk swapping code and data
|
|
@end table
|
|
|
|
See the file @file{stage2/shared.h}, for more information.
|
|
|
|
|
|
@node Embedded data
|
|
@section 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:
|
|
|
|
@table @code
|
|
@item 0x3E
|
|
The version number (not GRUB's, but the installation mechanism's).
|
|
|
|
@item 0x40
|
|
The boot drive. If it is 0xFF, use a drive passed by BIOS.
|
|
|
|
@item 0x41
|
|
The flag for if forcing LBA.
|
|
|
|
@item 0x42
|
|
The starting address of Stage 2.
|
|
|
|
@item 0x44
|
|
The first sector of Stage 2.
|
|
|
|
@item 0x48
|
|
The starting segment of Stage 2.
|
|
|
|
@item 0x1FE
|
|
The signature (@code{0xAA55}).
|
|
@end table
|
|
|
|
See the file @file{stage1/stage1.S}, for more information.
|
|
|
|
In the first sector of Stage 1.5 and Stage 2, the block lists are
|
|
recorded between @code{firstlist} and @code{lastlist}. The address of
|
|
@code{lastlist} is determined when assembling the file
|
|
@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 @code{firstlist} label. Note that it is always from the
|
|
beginning of the disk, but @emph{not} relative to the partition
|
|
boundaries.
|
|
|
|
In the second sector of Stage 1.5 and Stage 2, these are defined:
|
|
|
|
@table @asis
|
|
@item @code{0x6}
|
|
The version number (likewise, the installation mechanism's).
|
|
|
|
@item @code{0x8}
|
|
The installed partition.
|
|
|
|
@item @code{0xC}
|
|
The saved entry number.
|
|
|
|
@item @code{0x10}
|
|
The identifier.
|
|
|
|
@item @code{0x11}
|
|
The flag for if forcing LBA.
|
|
|
|
@item @code{0x12}
|
|
The version string (GRUB's).
|
|
|
|
@item @code{0x12} + @dfn{the length of the version string}
|
|
The name of a configuration file.
|
|
@end table
|
|
|
|
See the file @file{stage2/asm.S}, for more information.
|
|
|
|
|
|
@node Filesystem interface
|
|
@section The generic interface for filesystems
|
|
|
|
For any particular partition, it is presumed that only one of the
|
|
@dfn{normal} filesystems such as FAT, FFS, or ext2fs can be used, so
|
|
there is a switch table managed by the functions in
|
|
@file{disk_io.c}. The notation is that you can only @dfn{mount} one at a
|
|
time.
|
|
|
|
The block list filesystem has a special place in the system. In addition
|
|
to the @dfn{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:
|
|
|
|
@vtable @code
|
|
@item current_drive
|
|
The current BIOS drive number (numbered from 0, if a floppy, and
|
|
numbered from 0x80, if a hard disk).
|
|
|
|
@item current_partition
|
|
The current partition number.
|
|
|
|
@item current_slice
|
|
The current partition type.
|
|
|
|
@item saved_drive
|
|
The @dfn{drive} part of the root device.
|
|
|
|
@item saved_partition
|
|
The @dfn{partition} part of the root device.
|
|
|
|
@item part_start
|
|
The current partition starting address, in sectors.
|
|
|
|
@item part_length
|
|
The current partition length, in sectors.
|
|
|
|
@item print_possibilities
|
|
True when the @code{dir} function should print the possible completions
|
|
of a file, and false when it should try to actually open a file of that
|
|
name.
|
|
|
|
@item FSYS_BUF
|
|
Filesystem buffer which is 32K in size, to use in any way which the
|
|
filesystem backend desires.
|
|
@end vtable
|
|
|
|
The variables which need to be written by a filesystem backend are:
|
|
|
|
@vtable @code
|
|
@item filepos
|
|
The current position in the file, in sectors.
|
|
|
|
@strong{Caution:} the value of @var{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!
|
|
|
|
@item filemax
|
|
The length of the file.
|
|
|
|
@item disk_read_func
|
|
The value of @var{disk_read_hook} @emph{only} during reading of data
|
|
for the file, not any other fs data, inodes, FAT tables, whatever, then
|
|
set to @code{NULL} at all other times (it will be @code{NULL} by
|
|
default). If this isn't done correctly, then the @command{testload} and
|
|
@command{install} commands won't work correctly.
|
|
@end vtable
|
|
|
|
The functions expected to be used by the filesystem backend are:
|
|
|
|
@ftable @code
|
|
@item devread
|
|
Only read sectors from within a partition. Sector 0 is the first sector
|
|
in the partition.
|
|
|
|
@item grub_read
|
|
If the backend uses the block list code, then @code{grub_read} can be
|
|
used, after setting @var{block_file} to 1.
|
|
|
|
@item print_a_completion
|
|
If @var{print_possibilities} is true, call @code{print_a_completion} for
|
|
each possible file name. Otherwise, the file name completion won't work.
|
|
@end ftable
|
|
|
|
The functions expected to be defined by the filesystem backend are
|
|
described at least moderately in the file @file{filesys.h}. Their usage
|
|
is fairly evident from their use in the functions in @file{disk_io.c},
|
|
look for the use of the @var{fsys_table} array.
|
|
|
|
@strong{Caution:} The semantics are such that then @samp{mount}ing the
|
|
filesystem, presume the filesystem buffer @code{FSYS_BUF} is corrupted,
|
|
and (re-)load all important contents. When opening and reading a file,
|
|
presume that the data from the @samp{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).
|
|
|
|
|
|
@node Command interface
|
|
@section 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 @samp{int} value. The @dfn{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 @var{errnum}.
|
|
|
|
The table of the information is represented by the structure
|
|
@code{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 @var{builtin_table}, so
|
|
that @code{run_script} and @code{enter_cmdline} can find the
|
|
command. See the files @file{cmdline.c} and @file{builtins.c}, for more
|
|
details.
|
|
|
|
|
|
@node Bootstrap tricks
|
|
@section The bootstrap mechanism used in GRUB
|
|
|
|
The disk space can be used in a boot loader is very restricted because
|
|
a MBR (@pxref{MBR}) is only 512 bytes but it also contains a partition
|
|
table (@pxref{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, @dfn{Stage 1} and @dfn{Stage 2} (and optionally
|
|
@dfn{Stage 1.5}). @xref{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 @dfn{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 (@pxref{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:
|
|
|
|
@enumerate
|
|
@item
|
|
Initialize the system briefly.
|
|
|
|
@item
|
|
Detect the geometry and the accessing mode of the @dfn{loading drive}.
|
|
|
|
@item
|
|
Load the first sector of Stage 2.
|
|
|
|
@item
|
|
Jump to the starting address of the Stage 2.
|
|
@end enumerate
|
|
|
|
The flow of Stage 2 (and Stage 1.5) is:
|
|
|
|
@enumerate
|
|
@item
|
|
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.
|
|
|
|
@item
|
|
Long jump to the real starting address.
|
|
@end enumerate
|
|
|
|
Note that Stage 2 (or Stage 1.5) does not probe the geometry
|
|
or the accessing mode of the @dfn{loading drive}, since Stage 1 has
|
|
already probed them.
|
|
|
|
|
|
@node I/O ports detection
|
|
@section How to probe I/O ports used by INT 13H
|
|
|
|
FIXME: I will write this chapter after implementing the new technique.
|
|
|
|
|
|
|
|
@node Memory detection
|
|
@section 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.
|
|
|
|
|
|
@node Low-level disk I/O
|
|
@section 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.
|
|
|
|
|
|
@node MBR
|
|
@section The structure of Master Boot Record
|
|
|
|
FIXME: Likewise.
|
|
|
|
|
|
@node Partition table
|
|
@section The format of partition tables
|
|
|
|
FIXME: Probably the original chapter is derived from "How It Works", so
|
|
I will rewrite this chapter.
|
|
|
|
|
|
@node Submitting patches
|
|
@section Where and how you should send patches
|
|
|
|
When you write patches for GRUB, please send them to the mailing list
|
|
@email{bug-grub@@gnu.org}. Here is the list of items of which you
|
|
should take care:
|
|
|
|
@itemize @bullet
|
|
@item
|
|
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.
|
|
|
|
@item
|
|
Use as late code as possible, for the original code. The CVS repository
|
|
always has the current version (@pxref{Obtaining and Building GRUB}).
|
|
|
|
@item
|
|
Write ChangeLog entries. @xref{Change Logs, , Change Logs, standards,
|
|
GNU Coding Standards}, if you don't know how to write ChangeLog.
|
|
|
|
@item
|
|
Make patches in unified diff format. @samp{diff -urN} is appropriate in
|
|
most cases.
|
|
|
|
@item
|
|
Don't make patches reversely. Reverse patches are difficult to read and
|
|
use.
|
|
|
|
@item
|
|
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.
|
|
|
|
@item
|
|
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.
|
|
@end itemize
|