================================================================
THE LINUX/I386 BOOT PROTOCOL
================================================================
H. Peter Anvin
На данный момент существуют 4 версии boot-протоколов .
Старые ядра: поддержка только zImage/Image .
Ранние версии ядер не поддерживают командную строку.
Protocol 2.00: Добавлен формат bzImage и поддерживается initrd
для взаимодействия между загрузчиком и ядром .
Файл setup.S становится перемещаемым .
Protocol 2.01: (Kernel 1.3.76) Добавлено heap overrun warning.
Protocol 2.02: (Kernel 2.4.0) Новый протокол командной строки
**** Память
Таблица памяти для загрузчиков типа Image или zImage выглядит так :
| |
0A0000 +------------------------+
| Reserved for BIOS | Не используется
09A000 +------------------------+
| Stack/heap/cmdline | kernel real-mode code.
098000 +------------------------+
| Kernel setup | kernel real-mode code.
090200 +------------------------+
| Kernel boot sector | kernel legacy boot sector.
090000 +------------------------+
| Protected-mode kernel | kernel image.
010000 +------------------------+
| Boot loader | boot-сектор 0000:7C00
001000 +------------------------+
| Reserved for MBR/BIOS |
000800 +------------------------+
| Typically used by MBR |
000600 +------------------------+
| BIOS use only |
000000 +------------------------+
При использовании bzImage, ядро грузится выше адреса
0x100000 ("high memory"), остальные блоки (boot sector,setup, stack/heap) -
ниже адреса 0x100000.
Биосы выделяют память типа Extended BIOS Data Area как раз
ниже этой планки . Для проверки количества такой памяти
загрузчик должен вызывать "INT 12h" .
Для форматов zImage или bzImage память выше отметки 0x90000
недоступна .
**** REAL-MODE KERNEL HEADER
В загрузчике тип "sector" ссылается на 512 байт.
Первое , что нужно сделать при загрузке ядра - загрузить
real-mode часть кода (boot-sector и setup code)
и сделать проверку по смещению 0x01f1.
real-mode code может прыгнуть на 32K,
хотя сам загрузчик для загрузки может использовать только
первые 2 сектора (1K) .
Хидер выглядит так:
Offset Proto Name Meaning
Size
01F1/1 ALL setup_sects Размер setup в секторах
01F2/2 ALL root_flags Если этот флаг установлен,
root онтирован readonly
01F4/2 ALL syssize используется в bootsect.S
01F6/2 ALL swap_dev не используется
01F8/2 ALL ram_size используется в bootsect.S
01FA/2 ALL vid_mode Video mode control
01FC/2 ALL root_dev Default root device number
01FE/2 ALL boot_flag 0xAA55 magic number
0200/2 2.00+ jump Jump instruction
0202/4 2.00+ header Magic signature "HdrS"
0206/2 2.00+ version Boot protocol version supported
0208/4 2.00+ realmode_swtch Boot loader hook (see below)
020C/4 2.00+ start_sys версия ядра
0210/1 2.00+ type_of_loader тип загрузчика
0211/1 2.00+ loadflags флаги boot-протокола
0212/2 2.00+ setup_move_size переход в верхнюю память
0214/4 2.00+ code32_start Boot loader hook (see below)
0218/4 2.00+ ramdisk_image адрес загрузки initrd
021C/4 2.00+ ramdisk_size размер initrd
0220/4 2.00+ bootsect_kludge используется в bootsect.S
0224/4 2.01+ heap_end_ptr свободная память после setup
0226/2 N/A pad1 не используется
0228/4 2.02+ cmd_line_ptr 32-bit указатель на командную строку
Если магическое число "HdrS" (0x53726448) не найдено по смещению 0x202,
версия boot protocol старая - "old" , т.е.
Image type = zImage
initrd не поддерживается
Real-mode kernel загружается по адресу 0x90000.
Загрузчик как правило загружает ядро сразу по адресу назначения .
Следующие поля нужно проинициализировать :
type_of_loader:
прописывается в arch/i386/boot/setup.S, иначе это 0xFF .
initrd обычно размещается в верхней памяти насколько это возможно
и как правило выше 0x3C000000 .
cmd_line_ptr:
Для протоколов 2.02 и выше, это 32-bit указатель на
командную строку . Она размещается после setup и между 0xA0000.
**** KERNEL COMMAND LINE
С его помощью загрузчик взаимодействует с ядром .
Она представляет из себя строку длиной 255 плюс null.
Для протокола 2.02 ее адрес в хидере - поле cmd_line_ptr .
Для остальных протоколов :
По смещению 0x0020 (word), "cmd_line_magic", число 0xA33F.
Эта строка должна находиться внутри региона , определенного
с помощью setup_move_size.
**** Простая конфигурация загрузчика
Рассмотрим сегмент:
0x0000-0x7FFF Real mode kernel
0x8000-0x8FFF Stack и heap
0x9000-0x90FF Kernel command line
Необходимо в хидере прописать следующие поля :
unsigned long base_ptr; /* базовый адрес real-mode segment */
if ( setup_sects == 0 ) {
setup_sects = 4;
}
if ( protocol >= 0x0200 ) {
type_of_loader = ;
if ( loading_initrd ) {
ramdisk_image = ;
ramdisk_size = ;
}
if ( protocol >= 0x0201 ) {
heap_end_ptr = 0x9000 - 0x200;
loadflags |= 0x80; /* CAN_USE_HEAP */
}
if ( protocol >= 0x0202 ) {
cmd_line_ptr = base_ptr + 0x9000;
} else {
cmd_line_magic = 0xA33F;
cmd_line_offset = 0x9000;
setup_move_size = 0x9100;
}
} else {
/* Very old kernel */
cmd_line_magic = 0xA33F;
cmd_line_offset = 0x9000;
/* старые ядра ДОЛЖНЫ загружаться в 0x90000 */
if ( base_ptr != 0x90000 ) {
/* Copy the real-mode kernel */
memcpy(0x90000, base_ptr, (setup_sects+1)*512);
/* Copy the command line */
memcpy(0x99000, base_ptr+0x9000, 256);
base_ptr = 0x90000; /* Relocated */
}
/* очистка памяти 32K */
memset(0x90000 + (setup_sects-1)*512, 0,
(64-setup_sects-1)*512);
}
**** Загрузка ядра
non-real-mode kernel стартует по адресу (setup_sects+1)*512
в image-файле (если setup_sects == 0 , то это 4.)
Ядро грузится по адресу 0x10000 для Image/zImage kernels
и по адресу 0x100000 для bzImage .
Ядро = bzImage , если протокол >= 2.00 и бит 0x01 (LOAD_HIGH)
установлен:
is_bzImage = (protocol >= 0x0200) && (loadflags & 0x01);
load_address = is_bzImage ? 0x100000 : 0x10000;
Ядра Image/zImage могут иметь размер до 512K , и они используют
адреса 0x10000-0x90000 .
**** Опции командной строки
vga=
это либо число либо строка "normal" , "ext" , "ask"
mem=
- число , следующее за буквой K, M или G ( << 10, << 20 << 30).
Определяет размер памяти . Параметр можно разместить в initrd.
initrd=
BOOT_IMAGE=
имя загружаемого файла boot image .
auto
загрузка ядра без участия пользователя
**** Запуск ядра
Ядро прыгает в смещение 0x20 от начала сегмента real mode kernel.
Т.е. если например вы загрузили ядро по адресу 0x90000,
оно стартует с 9020:0000.
Далее происходит инициализация кодовых сегментов
ds = es = ss = fs = gs
которые указывают на 0x9000 , если код загружен в 0x90000
seg = base_ptr >> 4;
cli(); /* Прерывания запрещены ! */
/* Инициализация стека */
_SS = seg;
_SP = 0x9000; /* Загрузка SP после SS! */
_DS = _ES = _FS = _GS = seg;
jmp_far(seg+0x20, 0); /* старт ядра */
**** Нюансы загрузки
Следующие операции предусматривают стековую обработку регистров
%ebp, %esi , %edi.
realmode_swtch:
переключение из защищенного в реальный режим
code32_start:
переход в защищенный режим сразу после того как
разархивируется ядро.
Регистр KERNEL_DS =0x18 .
Термин empty_zero_page используется для передачи параметров
из 16-bit realmode code в 32-битный код .
Ссылки на него можно найти в :
arch/i386/boot/setup.S
arch/i386/boot/video.S
arch/i386/kernel/head.S
arch/i386/kernel/setup.c
Offset Type Description
------ ---- -----------
0 32 bytes struct screen_info, SCREEN_INFO
ATTENTION, overlaps the following !!!
2 unsigned short EXT_MEM_K, extended memory size in Kb (from int 0x15)
0x20 unsigned short CL_MAGIC, commandline magic number (=0xA33F)
0x22 unsigned short CL_OFFSET, commandline offset
Address of commandline is calculated:
0x90000 + contents of CL_OFFSET
(only taken, when CL_MAGIC = 0xA33F)
0x40 20 bytes struct apm_bios_info, APM_BIOS_INFO
0x80 16 bytes hd0-disk-parameter from intvector 0x41
0x90 16 bytes hd1-disk-parameter from intvector 0x46
0xa0 16 bytes System description table truncated to 16 bytes.
( struct sys_desc_table_struct )
0xb0 - 0x1df Free. Add more parameters here if you really need them.
0x1e0 unsigned long ALT_MEM_K, alternative mem check, in Kb
0x1f1 char size of setup.S, number of sectors
0x1f2 unsigned short MOUNT_ROOT_RDONLY (if !=0)
0x1f4 unsigned short size of compressed kernel-part in the
(b)zImage-file (in 16 byte units, rounded up)
0x1f6 unsigned short swap_dev (unused AFAIK)
0x1f8 unsigned short RAMDISK_FLAGS
0x1fa unsigned short VGA-Mode (old one)
0x1fc unsigned short ORIG_ROOT_DEV (high=Major, low=minor)
0x1ff char AUX_DEVICE_INFO
0x200 short jump to start of setup code aka "reserved" field.
0x202 4 bytes Signature for SETUP-header, ="HdrS"
0x206 unsigned short Version number of header format
Current version is 0x0201...
0x208 8 bytes (used by setup.S for communication with boot loader
look there)
0x210 char LOADER_TYPE, = 0, old one
else it is set by the loader:
0xTV: T=0 for LILO
1 for Loadlin
2 for bootsect-loader
3 for SYSLINUX
4 for ETHERBOOT
V = version
0x211 char loadflags:
bit0 = 1: kernel is loaded high (bzImage)
bit7 = 1: Heap and pointer (see below) set by boot
loader.
0x212 unsigned short (setup.S)
0x214 unsigned long KERNEL_START, where the loader started the kernel
0x218 unsigned long INITRD_START, address of loaded ramdisk image
0x21c unsigned long INITRD_SIZE, size in bytes of ramdisk image
0x220 4 bytes (setup.S)
0x224 unsigned short setup.S heap end pointer
0x226 - 0x7ff setup.S code.
0x800 string, 2K max COMMAND_LINE, the kernel commandline as
copied using CL_OFFSET.
Note: this will be copied once more by setup.c
into a local buffer which is only 256 bytes long.
( #define COMMAND_LINE_SIZE 256 )
|