Перевод Multiboot Specification

Для своих нужд потребовалось копаться в этом документе, результатом чего стал этот перевод. Я не переводчик, да и с языком у меня проблемы, так что если будут замечания и улучшения по тексту - прошу в комментарии.

1. Определения, встречающиеся в тексте.
ДОЛЖЕН
     Будем использовать термин ДОЛЖЕН, когда речь идет о положениях, которым необходимо следовать для совместимости мультизагрузчика и образа ОС.

СЛЕДУЕТ
     Будем использовать термин следует, когда речь идет о рекомендательном характере положений.

МОЖЕТ
     Будем использовать термин МОЖЕТ, когда речь будет идти о положениях, которым следовать не запрещено.

ЗАГРУЗЧИК
     Программа или набор программ, которые загружают образ операционной системы. ЗАГРУЗЧИК может состоять из нескольких стадий, но это является деталью имплементации, не описываемой в данной спецификации. Только финальная стадия ЗАГРУЗЧИКА - стадия на которой управление передается операционной системе - должна соответствовать требованиям, описываемым в данном документе.

ОБРАЗ ОС
     Двоичный файл, который ЗАГРУЗЧИК загружает в память и которому передает управление для старта ОС. ОБРАЗ ОС обычно является исполняемым файлом, содержащим ядро ОС.

МОДУЛЬ ЯДРА
    Другие вспомогательные файлы, которые ЗАГРУЗЧИК загружает в память вместе с ОБРАЗОМ ОС, но которым не передается управление из загрузчика. Вместо этого указатель на память, куда размещен такой МОДУЛЬ передается операционной системе, когда той передается управление.

u8
    8-битный беззнаковый тип данных.
u16
     16-битный беззнаковый тип данных.
u32
     32-битный беззнаковый тип данных.
u64
     64-битный беззнаковый тип данных.

Так как целевой архитектурой является x86, то предполагается little-endian порядок байт.

2. Определения мультизагрузки.
Всего существует три главных правила для описания интерфейса взаимодействия загрузчика и ядра ОС:

  1. Формат образа ОС должен быть понятен загрузчику;
  2. Должно быть определено состояние машины в момент передачи управления от загрузчика ядру ОС;
  3. Должен быть определен формат информации передаваемой от загрузчика в ОС.


2.1. Формат образа ОС.
Образом ядра ОС может быть обычный 32-битный исполняемый файл в стандартном для данной ОС формате. Он не должен располагать свой код в области памяти, отведенной по умолчанию для начальной загрузки, и других зарезервированных областях памяти. И, конечно же, он не должен использовать разделяемые библиотеки или другие "модные" функции.

Образ ОС должен содержать помимо заголовков, определяемых форматом файла, специальный заголовок, предназначенный для взаимодействия с загрузчиком (Multiboot header). Этот заголовок должен полностью располагаться в первых 8192 байтах образа ОС и должен быть выровнен по длине 32-битного слова. Мы рекомендуем располагать его в начале сегмента кода (text segment), сразу после обязательного заголовка исполняемого файла (executable header).

2.1.1. Расположение заголовка мультизагрузки.
Расположение заголовка мультизагрузки должно быть следующим:
Offset     Type     Field Name     Note
 0     u32     magic           required
 4     u32     flags           required
 8     u32     checksum        required
12     u32     header_addr     if flags[16] is set
16     u32     load_addr       if flags[16] is set
20     u32     load_end_addr   if flags[16] is set
24     u32     bss_end_addr    if flags[16] is set
28     u32     entry_addr      if flags[16] is set
32     u32     mode_type       if flags[2] is set
36     u32     width           if flags[2] is set
40     u32     height          if flags[2] is set
44     u32     depth           if flags[2] is set



2.1.2. Обязательные поля заголовка мультизагрузки.

'magic'
Поле 'magic' - это значение, которое однозначно идентифицирует заголовок мультизагрузки. Оно всегда должно быть равно 0x1BADB002.
'flags'
Поле 'flags' определяет набор свойств, которые ОС запрашивает или требует от загрузчика. Биты 0-15 указывают на обязательные для загрузки ОС поля; если один из этих битов установлен, но загрузчик не может по каким-либо причинам предоставить затребованные сведения, он должен уведомить пользователя об ошибке и прекратить загрузку образа ОС. Биты 16-31 определяют набор дополнительных сведений; если какой-либо бит установлен, но загрузчик не может удовлетворить запрос ОС, то он может просто проигнорировать этот бит и продолжить загрузку ядра ОС. Разумеется, для всех неопределенных полей биты должны быть сброшены. В этом случае, поле 'flags' служит как для контроля версии, так и для определения свойств.

Если установлен 0 бит в поле 'flags', тогда все МОДУЛИ ЯДРА, загружаемые вместе с ОС, должны быть выровнены по границе страницы (4 кб). Некоторые ОС ожидают такого поведения для того, чтобы проще было отобразить страницы, содержащие МОДУЛИ ЯДРА, в страничное адресное пространство в процессе запуска ОС.

Если установлен 1 бит в поле 'flags', тогда в структуру информации мультизагрузчика ДОЛЖНА быть включена информация о доступной памяти, по крайней мере, должны быть заполнены поля 'mem_*' структуры мультизагрузчика. Если ЗАГРУЗЧИК способен передать карту памяти (поля 'mmap_*'), тогда она также может быть включена в структуру.

Если установлен 2 бит в поле 'flags', ядру ОС ДОЛЖНА быть доступна таблица информации о видео режиме (video mode table).

Если установлен 16 бит поля 'flags', тогда информация по смещению 12-28 заголовка мультизагрузки будет действительна, и ЗАГРУЗЧИКу следует использовать эту информацию вместо полей в заголовке исполняемого файла образа ОС для размещения образа ОС в памяти. В установке этого бита нет необходимости, если образ ядра ОС собран в ELF-формате, но он ДОЛЖЕН быть установлен, если этот образ ядра в формате a.out или в каком-либо другом. Совместимые загрузчики ДОЛЖНЫ уметь загружать образ ОС собранный в ELF-формате; все другие форматы могут поддерживаться, но данный документ этого не требует.

'checksum'
Поле 'checksum' содержит контрольную сумму первых двух полей.

2.1.3. Поля адреса заголовка мультизагрузки
Все поля, включенные установкой 16 бита поля 'flags', содержат физические адреса. Значение каждого из полей следующее:

header_addr
Содержит адрес начала заголовка мультизагрузки - область физической памяти из которой предполгается считать поле 'magic'. Это поле служит для синхронизации отображения смещения образа ОС и его адреса в физической памяти.

load_addr
Содержит физический адрес начала сегмента кода. Смещение в файле образа ОС начиная с которого необходимо загружать ОС определяется как смещение по которому находится заголовок минус (header_addr - load_addr). load_addr ДОЛЖЕН быть меньше либо равен header_addr.

load_end_addr
Содержит физический адрес конца сегмента данных. (load_end_addr - load_addr) определяет количество данных для загрузки. Подразумевается, что сегмент кода и сегмент данных ДОЛЖНЫ располагаться последовательно в образе ОС;
это справедливо для существующего формата файла a.out. Если это поле равно нулю, то предполагается, что сегмент кода и сегмент данных занимают все пространство, отведенное для файла образа ОС.

bss_end_addr
Содержит физический адрес конца сегмента неинициализированных данных(bss segment). ЗАГРУЗЧИК инициализирует все пространство сегмента нулями, и резервирует память, адрессуемую данным сегментом, чтобы предотвратить размещение в этих адресах МОДУЛЕЙ ЯДРА и других данных, соответствующих операционной системе. Если это поле равно нулю, то ЗАРУЗЧИК предполагает отсутствие зарезервированных для данного сегмента адресов.

entry_addr
Физический адрес, в котором располагается точка входа в ядро.

2.1.4. Поля для хранения информации о видеорежиме
Данные поля становятся актуальными при установленном 2 бите поля 'flags'. Они определяют предпочтительный графический режим. Обратите внимание, что это только рекоммендуемый режим. Если такой режим существует, ЗАГРУЗЧИКу следует установить этот режим. В случае, если пользователь задал режим явно, ЗАГРУЗЧИКу следует переключиться в соответствующий режим.

Значения полей:
mode_type
Содержит '0' в случае линейного графического режима или '1' для EGA. Все остальные числа зарезервированы для будущих расширений. Обратите внимание, что ЗАГРУЗЧИК может установить текстовый режим, даже если поле содержит '0'.

width
Содержит число колонок. Для графического режима - количество пикселей по горизонтали, для текстового режима - количество символов в строке. Нулевое значение показывает, что параметр не задан.

height
Содержит число строк. Для графического режима - количество пикселей по вертикали, для текстового - количество строк. Нулевое значение показывает, что параметр не задан.

depth
Содержит число бит на пиксель в графическом режиме, и ноль в текстовом.

2.2. Состояние машины.
Когда ЗАГРУЗЧИК передает управление 32-битной операционной системе, машина ДОЛЖНА находиться в следующем состоянии:

%eax
Должен содержать число '0x2BADB002'; наличие этого значения сигнализирует о том, что управление было передано от совместимого мультизагрузчика (в отличие от других загрузчиков, которые также могут использоваться).

%ebx
Должен содержать 32-битный физический адрес специальной структуры (Multiboot information structure), подготовленной загрузчиком.

%cs
Должен указывать на сегмент кода с соответствующим правами доступа к памяти. Значение может лежать в пределах от '0' до '0xFFFFFFFF'.

%ds,%es,%fs,%gs,%ss
Должны указывать на сегменты данных с соответствующими правами доступа к памяти. Значение может лежать в пределах от '0' до '0xFFFFFFFF'.

'A20 gate'
Должна быть включена.

%cr0
31 бит (PG) должен быть сброшен. 0 бит (PE) должен быть установлен. Значение остальных битов не определено.

%eflags
17 бит (VM) должен быть сброшен. 9 бит (IF) должен быть сброшен. Значение остальных битов не определено.

Значения всех остальных регистров и флагов процессора не определены. Это, в частности, относится к:
%esp
ОС ДОЛЖНА создать свой собственный стек сразу после получения управления.

'GDTR'
Хотя сегментные регистры установлены, регистр таблицы глобальных дескрипторов может содержать недействительное значение, поэтому ОС НЕ ДОЛЖНА загружать какие-либо сегментные регистры (даже пытаться перегружать теми же значениями) до тех пор, пока не создаст свою собственную таблицу GDT.

'IDTR'
ОС не ДОЛЖНА разрешать аппаратные прерывания, пока не настроит свою IDT.

Однако, загрузчику СЛЕДУЕТ оставить все регистры и флаги, не описанные выше, в том состоянии, в которое они были установлены BIOS. Другими словами, ОС должна быть в состоянии обратиться к BIOS после загрузки до тех пор, пока сама не перезапишет структуры данных BIOS. Кроме того, загрузчик ДОЛЖЕН оставить значения контроллера прерываний (PIC) теми, которые были установлены BIOS, даже если он изменил их в процессе переключения в защищенный режим.

3.3. Формат структуры загрузчика.
После передачи управления ОС, в регистре EBX содержится физический адрес специальной структуры Multiboot information (далее - MI), при помощи которой загрузчик сообщает ОС важную информацию. ОС может использовать или игнорировать любые части данной структуры по своему усмотрению; вся информация носит рекомендательный характер.

Структура MI и ее подструктуры согут располагаться где угодно в памяти (за исключением областей, зарезервированных для ядра и модулей ядра). Ответственность за сохранность этих данных полностью лежит на ОС.

Формат структуры следующий:
             +-------------------+
     0       | flags             |    (required)
             +-------------------+
     4       | mem_lower         |    (present if flags[0] is set)
     8       | mem_upper         |    (present if flags[0] is set)
             +-------------------+
     12      | boot_device       |    (present if flags[1] is set)
             +-------------------+
     16      | cmdline           |    (present if flags[2] is set)
             +-------------------+
     20      | mods_count        |    (present if flags[3] is set)
     24      | mods_addr         |    (present if flags[3] is set)
             +-------------------+
     28 - 40 | syms              |    (present if flags[4] or
             |                   |                flags[5] is set)
             +-------------------+
     44      | mmap_length       |    (present if flags[6] is set)
     48      | mmap_addr         |    (present if flags[6] is set)
             +-------------------+
     52      | drives_length     |    (present if flags[7] is set)
     56      | drives_addr       |    (present if flags[7] is set)
             +-------------------+
     60      | config_table      |    (present if flags[8] is set)
             +-------------------+
     64      | boot_loader_name  |    (present if flags[9] is set)
             +-------------------+
     68      | apm_table         |    (present if flags[10] is set)
             +-------------------+
     72      | vbe_control_info  |    (present if flags[11] is set)
     76      | vbe_mode_info     |
     80      | vbe_mode          |
     82      | vbe_interface_seg |
     84      | vbe_interface_off |
     86      | vbe_interface_len |
             +-------------------+

Первое двойное слово определяет наличие других полей в структуре. Все биты, которые не были до сих пор определены, должны быть сброшены загрузчиком. Все наборы битов, которые ОС не "понимает" следует игнорировать. Таким образом, поле 'flags' также может служить для определения версии, позволяя в будущем расширять структуру MI без опасения что-либо "сломать".

Если 0 бит поля 'flags' установлен, то поля 'mem_*' действительны. 'mem_lower' и 'mem_upper' показывают размеры основной памяти (Conventional memory) и "верхней" памяти (UMB) (в кб). Основная память начинается с адреса 0, "верхняя" память начинается с адреса 0x00100000 (1 Мб). Максимальное возможно значение для основной памяти - 640 кб (0x000A0000). Максимальное значение, возвращаемое для "верхней" памяти будет равно адресу начала первой "дыры" в "верхней" памяти минус 1 Мб.

Если установлен бит 1 поля 'flags', то поле 'boot_device' показывает с какого дискового устройства ЗАГРУЗЧИК загрузил образ ОС. Если образ ОС был загружен не с дискового устройства, тогда это поле не должно присутствовать (бит 1 должен быть сброшен). ОС может использовать это поля в качестве подсказки для определения собственного корневого устройства, но это не обязательно. Поле 'boot_device' устанавливается в четырех однобайтовых полях следующим образом:
     +-------+-------+-------+-------+
     | part3 | part2 | part1 | drive |
     +-------+-------+-------+-------+

Первый байт содержит номер BIOS устройства, который соответствует номеру, передаваемому при прерывании BIOS INT 0x13 (Пример: 0x00 - для дискеты, 0x80 - для первого жесткого диска).

Три оставшихся байта определяют загрузочный раздел диска. 'part1' определяет номер раздела верхнего уровня, 'part2' определяет подраздел в этом разделе, и.т.д. Номера разделов всегда начинаются с нуля. Неиспользуемые номера разделов должны быть установлены в 0xFF. Пример: Если диск разбит на разделы, используя одноуровневую схему разбиения как в MS-DOS, тогда 'part1' будет содержать номер DOS раздела, а поля 'part2' и 'part3' значения 0xFF. Другой пример: если диск разбит сначала на DOS разделы и один из этих DOS разделов разбивают на несколько BSD разделов, используя disklabel, тогда 'part1' будет содержать номер раздела DOS, 'part2 - номер BSD подраздела, а 'part3' - 0xFF.

Расщиренные разделы DOS помечаются начиная с номера 4 и выше, а не как вложенные подразделы, даже не смотря на то, что в действительности схема носит иерархический характер. Например, если ЗАГРУЗЧИК запускается со второго расширенного раздела на диске, разбитом в стиле DOS, тогда 'part1' будет равно 5, а 'part2' и 'part3' - 0xFF.

Если бит 2 поля 'flags' установлен, то поле 'cmdline' содержит физический адрес коммандной строки. Коммандная строка - это нормальная нуль-терминированная C-строка.

Если установлен 3 бит поля 'fields', то поля 'mods' указывают на МОДУЛИ ЯДРА, загруженные вместе с образом ОС. 'mods_count' содержит число загруженных модулей; 'mods_addr' содержит физический адрес первой структуры модуля (module structure). 'mods_count' может быть равен нулю, показывая тем самым, что модули не были загружены, даже если установлен 3 бит поля 'flags'. Каждая структура, описывающая модуль, имеет следующий вид:

             +-------------------+
     0       | mod_start         |
     4       | mod_end           |
             +-------------------+
     8       | string            |
             +-------------------+
     12      | reserved (0)      |
             +-------------------+

Первые два поля содержат адреса начала и конца модуля ядра. Поле 'string' содержит условное название данного модуля. Обычно эта строка может быть эквивалентна командной строке (если например ОС трактует МОДУЛИ ЯДРА как исполняемые программы), или пути в ФС (например, если ОС трактует модули как файлы в ФС), но точное значение и порядок использования этой строки определяется операционной системой. Поле 'reserved' должно быть установлено в 0 ЗАГРУЗЧИКОМ и проигнорировано ОС.

Внимание: Биты 4 и 5 должны быть взаимоисключающими.

Если бит 4 в поле 'flags' установлен, то поля, начинающиеся по смещению 28 структуры MI, имеют следующий вид:
             +-------------------+
     28      | tabsize           |
     32      | strsize           |
     36      | addr              |
     40      | reserved (0)      |
             +-------------------+

Эти данные показывают, где можно найти таблицу символов, если исполняемый файл образа ядра собран в формате a.out. 'addr' - это физический адрес, по которому располагается размер (4-байтовое беззнаковое целое) массива структур nlist для формата a.out, следующиий сразу после самого этого массива, затем размер (4-байтовое беззнаковое целове) набора нуль-терминированных ASCII-строк (плюс sizeof(unsigned long) в таком случае), и, наконец, набор этих самых строк. Значение 'tabsize' равно размеру таблицы символов, и 'strsize' равно размеру таблицы строк, на которую ссылается таблица символов. Обратите внимание, что 'tabsize' может равняться 0, сигнализируя, что таблица символов пуста, даже если бит 4 в поле 'flags' установлен.

Если установлен бит 5 поля 'flags', то поля, начинающиеся по смещению 28 структуры MI, имеют следующий вид:

             +-------------------+
     28      | num               |
     32      | size              |
     36      | addr              |
     40      | shndx             |
             +-------------------+

Эти данные показывают где можно найти заголовок исполняемого файла ядра формата ELF: размер каждой записи, число записей и таблица строк, используемая как индекс имен. Они ссылаются на 'shdr_*' записи ('shdr_num' и т.д.) в определении формата ELF. Все секции загружаются, физические адреса полей секций заголовка ELF затем указывают на расположение секций в физической памяти. Обратите внимание, что 'shdr_num' может быть равно 0, сигнализируя отсутствие символов, даже если бит 5 в поле 'flags' установлен.

Если установлен бит 6 в поле 'flags', то поля 'mmap_*' показывают адрес и размер буфера, содержащего карту памяти предоставляемую BIOS. 'mmap_addr' содержит адрес, а 'mmap_length' – полный размер этого буфера. Буфер состоит из одной или более следующих друг за другом пар размер/структура:
             +-------------------+
     -4      | size              |
             +-------------------+
     0       | base_addr         |
     8       | length            |
     16      | type              |
             +-------------------+

где 'size' - это размер структуры в байтах, который может быть больше 20 байт. 'base_addr' - это адрес начала. 'length' - размер региона памяти в байтах. 'type' - разновидность представленных диапазонов адресов, где значение 1 показывает доступную RAM, и все остальные значения показывают, что диапазон зарезервирован.

Представленная карта гарантированно включает все стандартные области памяти, которые должны быть доступны для обычного использования.

Если установлен бит 7 поля 'flags', то поля 'drives_*' содержат указатель на адрес первой структуры, описывающей приводы (drive structure) и количество этих структур. 'drives_addr' - это соответственно адрес, а 'drives_length' - это полный размер всех структур. Обратите внимание, что 'drive_length' может быть равен нулю. Каждая структура имеет следующий вид:
             +-------------------+
     0       | size              |
             +-------------------+
     4       | drive_number      |
             +-------------------+
     5       | drive_mode        |
             +-------------------+
     6       | drive_cylinders   |
     8       | drive_heads       |
     9       | drive_sectors     |
             +-------------------+
     10 - xx | drive_ports       |

Поле 'size' определяет размер структуры. Этот размер может меняться в зависимости от количества портов. Обратите внимание, что размер может быть не равен (10 + 2*количество портов) из-за выравнивания.

Поле 'drive_number' содержит номер привода в BIOS. Поле 'drive_mode' содержит значение режима доступа, используемого ЗАГРУЗЧИКОМ. В настоящее время определены следующие режимы:

'0'
    CHS (традиционный режим адресации цилиндр/головка/сектор)

'1'
     LBA (режим Logical Block Addressing)

Три поля 'drive_cylinders', 'drive_heads' и 'drive_sectors' определяют геометрию привода. 'drive_cylinders' содержит число цилиндров, 'drive_heads' - число головок, 'drive_sectors' - число секторов в треке.

Поле 'drive_ports' содержит массив портов ввода-вывода, используемых для привода в коде BIOS. Массив состоит из нуля или более двухбайтовых беззнаковых целых и оканчивается нулем. Обратите внимание, что массив может содержать любое число портов ввода-вывода, на самом деле не имеющих никакого отношения к приводу (например, портов контроллера DMA).

Если установлен 8 бит, тогда поле 'config_table' указывает на адрес таблицы конфигурации ROM, который возвращается системным вызовом BIOS GET CONFIGURATION. Если системный вызов BIOS заканчивается неудачей, тогда размер этой таблицы должен быть равен нулю.

Если установлен бит 9, то поле 'boot_loader_name' содержит физический адрес имени загрузчика, загрузившего ядро. Это имя представляет собой обычную нуль-терминированную C-строку.

Если установлен бит 10, то поле 'apm_table' содержит физический адрес таблицы APM имеющей следующий вид:
             +----------------------+
     0       | version              |
     2       | cseg                 |
     4       | offset               |
     8       | cseg_16              |
     10      | dseg                 |
     12      | flags                |
     14      | cseg_len             |
     16      | cseg_16_len          |
     18      | dseg_len             |
             +----------------------+

Поля 'version', 'cseg', 'offset', 'cseg_16', 'dseg', 'flags', 'cseg_len', 'cseg_16_len', 'dseg_len' определяют соответственно номер версии, сегмент кода защищенного режима, смещение точки входа, 16-битный сегмент кода защищенного режима, 16-битный сегмент данных защищенного режима, флаги, длину сегмента кода защищенного режима, длину 16-битного сегмента кода, длину 16-битного сегмента данных. Только поле 'offset' имеет размер 4 байта, все остальные - двух байтовые.

Если установлен бит 11, то доступна графическая таблица. Последнее возможно только в том случае, если было указано, что ядро поддерживает графический режим.

Поля 'vbe_control_info' и 'vbe_mode_info' содержат физические адреса управляющей информации VBE, возвращаемой функцией VBE 0x00, и информации о режиме, возвращаемой функцией VBE 0x01, соответственно.

Поле 'vbe_mode' указывает текущий видео режим в формате определенном в VBE 3.0.

Остальные поля 'vbe_interface_seg', 'vbe_interface_off' и 'vbe_interface_len' содержат таблицу интерфейса защищенного режима, определенную в VBE 2.0+. Если эта информация не доступна, эти поля содержат ноль. Обратите внимание, что VBE 3.0 определяет другой интерфейс защищенного режима, который не совместим со старыми версиями. Если Вы хотите использовать новый интерфейс защищенного режима, Вам придется найти эту таблицу самостоятельно.

Поля для графической таблицы разработаны для VBE, но ЗАГРУЗЧИК может эмулировать VBE на не поддерживающих этот интерфейс режимах.

Оригинал документа находится по адресу: http://www.gnu.org/software/grub/manual/multiboot/multiboot.html

4 комментария:

  1. Спасибо за перевод.
    На самом деле гораздо интереснее сам факт того, что Вам понадобилась эта спецификация. Вот например, когда Линус опубликовал объявление о поиске текста стандартов POSIX, знающие люди сразу просекли, что он пишет свою ОС :)

    ОтветитьУдалить
  2. ...Хотя тематика блога уже в принципе даёт подсказку...

    ОтветитьУдалить
  3. Этот комментарий был удален автором.

    ОтветитьУдалить
  4. Ошибка:
    Структура MI и ее подструктуры сМогут*

    ОтветитьУдалить