Вы когда-нибудь задумывались, что происходит, когда вы нажимаете кнопку питания на компьютере? За той краткой паузой, прежде чем экран загорится, скрывается сложный процесс. В этой статье мы погрузимся в увлекательный мир прошивок (firmware) и исследуем, как разные компоненты взаимодействуют во время загрузки системы. Поняв эти связи, вы получите четкое представление о том, как основные элементы приводят вашу систему в действие. Мы сосредоточимся на Intel архитектуре x86, хотя многие принципы применимы и к другим архитектурам.

Если вы пропустили первую часть нашей серии, прочитайте её для лучшего понимания темы. А теперь давайте раскроем тайны, скрытые за прошивкой.

Содержание

  • Определения

  • Общая архитектура прошивок

  • First-Stage Boot Loader (FSBL)

    • BIOS (POST фаза)

    • UEFI Platform Initialization (PI)

    • coreboot

    • Другие решения

  • Second-Stage Boot Loader (SSBL)

    • BIOS

    • UEFI

  • OS Boot Loader

Определения

Firmware (Прошивка): специализированный тип ПО, встроенный в аппаратное оборудование (hardware), который обеспечивает низкоуровневое управление и позволяет оборудованию правильно функционировать и взаимодействовать с другими компонентами системы.

Basic Input/Output System (BIOS): устаревшая прошивка (изначально созданная для IBM PC), ответственная за инициализацию оборудования после включения платформы. В наше время этот термин часто используется в общем смысле для обозначения полного набора прошивок.

Bootloader: общее название для прошивки, отвечающей за загрузку компьютера. Это современный концепт, заменяющий BIOS, часто представляет из себя фреймворк с кодом начальной загрузки для инициализации процессора и чипсета, а также интерфейсы для сторонних разработчиков (например, разработчиков материнской платы) для выполнения специфичной для платформы инициализации.

Payload: программное обеспечение, которое выполняется сразу после Bootloader'а. Это может быть second stage bootloader, операционная система, BIOS/UEFI приложение и другое. Обычно она управляет процессом загрузки в соответствии с архитектурой прошивки.

Использование терминов BIOS и bootloader может быть запутанным, поскольку их значения зависят от контекста. Однако, когда говорят о firmware (прошивке), BIOS или bootloader, обычно имеют в виду полный набор прошивок, работающий между операционной системой и аппаратным оборудований.

EFI vs UEFI: Extensible Firmware Interface (EFI) это изначальная спецификация, разработанная компанией Intel. Unified Extensible Firmware Interface (UEFI) является преемником EFI, созданная UEFI Forum'ом для стандартизации и расширения исходной спецификации. Практически всегда EFI и UEFI используются взаимозаменяемо.

Общая архитектура прошивок

Чтобы понять как взаимодействуют компоненты прошивки, рассмотрим всю архитектуру и все ее взаимосвязанные части. Процесс загрузки, показанный на диаграмме ниже, начинается с reset vector, который является частью First-Stage Bootloader. Оттуда он проходит через различные стадии прошивки:

Firmware или BIOS обычно делится на две основные части с минимальным интерфейсом между ними:

  1. Инициализация оборудования: Отвечает за инициализацию аппаратных компонентов системы.

  2. Интерфейс к ОС и пользователю: Предоставляет необходимые интерфейсы для операционной системы и пользователя.

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

Следующая диаграмма иллюстрирует, как различные компоненты прошивки взаимодействуют друг с другом и могут использоваться вместе для поддержки процесса загрузки (стрелки указывают последовательность выполнения):

Если эти диаграммы кажутся вам сложными, не переживайте. Посмотрите на них снова после прочтения статьи, и они станут понятнее.

First-Stage Boot Loader (FSBL)

Этот компонент прошивки предназначен для инициализации компьютеров и встроенных (embedded) систем с фокусом на минимальную инициализацию оборудования: выполнить только необходимое и затем передать управление Second-Stage Bootloader, чтобы загрузить операционную систему. FSBL не загружает операционные системы с накопителей отличных от флеш-памяти (flash chip). Поскольку он только инициализирует базовое оборудование и не работает с носителями загрузки, такими как жесткие диски, SSD или USB-флешки, для фактической загрузки операционной системы требуется другой компонент программного обеспечения.

Основные задачи FSBL:

  1. Процессор: Переход с 16-битного Real Mode на 32-битный Protected Mode (или в Virtual 8086 mode в случае BIOS).

  2. Использование кэша: Вызов FSP-T для настройки Cache-As-RAM для Си окружения.

  3. Debug Port: Инициализация желаемоего debug port'а с помощью методов инициализации, специфичных для платы.

  4. Инициализация памяти: Вызов FSP-M для инициализации основной памяти системы и сохранения важной информации о системной памяти.

  5. GPIO: Настройка General-Purpose Input/Output (GPIO) контактов для взаимодействия с внешними устройствами.

  6. Silicon: Ранняя инициализация платформы и использование FSP-S для завершения инициализации чипсета, процессора и контроллера ввода-вывода.

  7. PCI Enumeration: Перечисление (enumerating) устройств PCI и распределение ресурсов, таких как адреса памяти и IRQs.

  8. Подготовка к Payload: Настройка SMBIOS и ACPI таблиц, включая подготовку информации (coreboot tables, HOBs), необходимой для передачи управления в payload.

  9. Передача управления: Нахождение, загрузка и передача управления payload'у.

BIOS (POST фаза)

В ранние дни компьютеров программное обеспечение с открытым исходным кодом не было широко популярным, и большинство реализаций BIOS были проприетарными. Существует лишь несколько доступных решений с открытым кодом, предоставляющих исходный код для POST фазы BIOS, такие как Super PC/Turbo XT BIOS и GLaBIOS. Эти проекты были разработаны для работы на системах IBM 5150/5155/5160 и большинстве XT клонов.

Однако более известные реализации BIOS с открытым исходным кодом, такие как OpenBIOS и SeaBIOS, не выполняют инициализацию оборудования, поскольку они не предназначены для работы на чистом оборудовании (bare hardware). Вместо этого они широко используются в качестве Second-Stage Bootloader'ов и работают в виртуальных средах, таких как QEMU и Bochs.

В любом случае, вам вряд ли придется работать непосредственно с этими ранними BIOSами или глубоко изучать их особенности. Но если вам интересно, упомянутые репозитории будут хорошей отправной точкой для изучения.

Что касается текущих тенденций в разработке, то, похоже, нет продолжающейся разработки проприетарных решений BIOS, и такие проекты стали устаревшими на фоне современных альтернатив.

UEFI Platform Initialization (PI)

Процесс загрузки следует поэтапной последовательности (staged flow), начиная с левого края и перемещаясь вправо на диаграмме ниже. Timeline процесса загрузки платформы делится на следующие фазы, как показано жёлтыми рамками:

  • Security (SEC): Первая фаза после reset vector. Основная функция этой фазы - настройка временной оперативной памяти (CPU Cache-As-RAM или SRAM).

  • Pre-EFI Initialization (PEI): Эта фаза запускает драйверы, называемые Pre-EFI Initialization Modules (PEIMs). Эти модули выполняют важные задачи по инициализации оборудования, такие как настройка CPU и чипсета, а также настройка основной памяти (DRAM).

  • Driver Execution Environment (DXE): В этой фазе выполняется остальная часть инициализации системы. Фаза DXE предоставляет UEFI сервисы и поддерживает различные протоколы и драйверы, необходимые для функционирования системы.

  • Boot Device Select (BDS): Эта фаза реализует политику загрузки платформы, определяя последовательность загрузки и выбирая подходящее загрузочное устройство.

  • Transient System Load (TSL): В этой фазе система запускает приложения, использующие UEFI сервисы, чтобы подготовить ОС к запуску. Это включает переход от среды UEFI к операционной системе и завершается ExitBootServices() вызовом.

  • Run Time (RT): В этой фазе операционная система полностью функционирует, управляя системой под своим контролем.

  • After Life (AL): Эта фаза занимается сценариями, когда оборудование или ОС крашится / выключается / перезагружается. Прошивка может попытаться выполнить действия по восстановлению, однако спецификация UEFI PI не определяет конкретные требования или поведение для этой фазы.

Этот процесс и его фазы охватыватся в UEFI Platform Initialization (PI) Specification. Однако существует также UEFI Interface (обозначен жирной синей линией на рисунке), который не входит в предыдущий документ и описан в UEFI Specification. Несмотря на то что названия и частое использование UEFI могут сбивать с толку, эти два документа сфокусированы на разных аспектах:

  • UEFI PI Spec:Ориентирован на интерфейсы между низкоуровневыми компонентами прошивки и деталями того, как эти модули взаимодействуют для инициализации платформы.

  • UEFI Spec: Определяет интерфейсы для взаимодействия между операционной системой (OS) и прошивкой. Это будет рассмотрено более подробно в контексте Second-Stage Bootloader. При этом UEFI Specification опирается на PI Specification.

По сути, обе спецификации касаются интерфейсов, но на разных уровнях. Для детальной информации вы можете получить доступ к обеим спецификациям на официальном сайте UEFI Forum.

UEFI PI изначально был разработан как универсальное решение для firmware, не учитывая различие между first-stage и second-stage bootloader'ами. Однако когда мы говорим о UEFI как о First-Stage Bootloader, это включает фазы SEC, PEI и early DXE. Причина, по которой мы делим DXE на early и late стадии, заключается в их различных ролях в процессе инициализации.

В early DXE фазе драйверы обычно выполняют основную инициализацию CPU/PCH/материнской платы и также создают DXE Architectural Protocols (APs), которые помогают изолировать фазу DXE от аппаратного оборудования платформы. APs инкапсулируют детали, специфичные для платформы, позволяя late DXE фазе работать независимо от специфики аппаратного оборудования.

Coreboot

Подробные статьи о том, как работает Coreboot, будут выпущены в ближайшее время. Подписывайтесь на мои социальные сети - они будут опубликованы очень скоро!

Другие решения

  • Intel Slim Bootloader (SBL): first-stage bootloader, который обеспечивает только инициализацию основных аппаратных компонентов, после чего загружает payload. Однако он работает только на Intel x86 платформах и не поддерживает AMD x86 или другие архитектуры.

  • Das U-Boot: как first-stage, так и second-stage bootloader. Однако поддержка загрузки напрямую с x86 reset vector платформы (известного как bare mode) ограничена по сравнению с другими прошивками. Он более популярен во встроенных (embedded) системах и устройствах на базе ARM. Как second-stage bootloader, U-Boot реализует часть спецификации UEFI, но с фокусом на встроенные системы.

Second-Stage Boot Loader (SSBL)

После завершения начальной инициализации hardware, на сцену выходит second-stage bootloader. Его основная задача - создать программный интерфейс между операционной системой и прошивкой платформы через который ОС могла управлять системными ресурсами и взаимодействовать с аппаратными компонентами. SSBL стремится максимально скрыть различия в аппаратном оборудовании, упрощая разработку ОС и приложений. Эта абстракция позволяет разработчикам сосредоточиться на функционале более высокого уровня, не беспокоясь о различиях в аппаратных компонентах.

Основные задачи SSBL:

  1. Получение информации о платформе: Извлечение информации о платформе от First-Stage Bootloader, включая memory mapping, SMBIOS, ACPI таблицы, SPI флеш-память.

  2. Запуск независимых от платформы драйверов: Включает драйверы для SMM, SPI, PCI, SCSI/ATA/IDE/DISK, USB, ACPI, сетевых интерфейсов и так далее.

  3. Реализация сервисов (aka интерфейса): Обеспечивает набор сервисов, упрощающих коммуникацию между операционной системой и аппаратными компонентами.

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

  5. Логика загрузки: Механизм для поиска и загрузки payload'а (вероятно, операционной системы) с доступных загрузочных носителей.

BIOS

Интерфейс в BIOS известен как сервисы / функции / прерывания BIOS. Эти функции предоставляют набор процедур для доступа к hardware, но конкретные детали их реализации на определенном оборудовании скрыты от пользователя.

В 16-битном Real Mode к этим функциям можно легко получить доступ, вызвав программное прерывание через инструкцию x86 ассемблера INT. В 32-битном Protected mode почти все сервисы BIOS недоступны из-за другого способа обработки сегментов.

Для примера рассмотрим сервисы для взаимодействия с диском (INT 13h), которые предоставляют возможность чтения и записи на жесткие диски и дискеты на основе Cylinder-Head-Sector (CHS) адресации. Предположим, что нам нужно прочитать 2 сектора (1024 байта) и загрузить их по адресу в памяти 0x9020. Для этого можно выполнить следующий код:

mov $0x02, %ah       # Выбрать функцию чтения сектора BIOS
mov $0x00, %ch       # Выбрать цилиндр 0
mov $0x00, %dh       # Выбрать головку 0 [начинается с 0]
mov $0x02, %cl       # Выбрать сектор 2 (следующий после загрузочного
                     # сектора) [начинается с 1]
mov $0x02, %al       # Прочитать 2 сектора
mov $0x00, %bx       # Установить general регистр BX в 0
mov %bx, %es         # Установить segment регистр ES в 0
mov $0x9020, %bx     # Загрузить сектора по адресу ES:BX (0:0x9020)
int $0x13            # Начать чтение с диска
jmp $0x9020          # Перейти к загруженному коду

Если вам интересно, как эта служба реализована в SeaBIOS, взглядите на src/disk.c.

BOOT фаза

  • Начинается поиск загрузочного устройства (это может быть жесткий диск, CD-ROM, дискета и т.д.) путем считывания первых 512 байт (нулевого сектора) с устройств.

  • BIOS загружает первый валидный Master Boot Record (MBR), который она находит, в физическую память компьютера по физическому адресу 0x7C00 (примечание: 0x0000:0x7c00 и 0x7c0:0x0000 указывают на один и тот же физический адрес).

  • BIOS передает управление первым 512 байтам payload'а. Этот загруженный сектор, который слишком мал, чтобы содержать весь код payload'а, предназначен для загрузки остальной части payload с загрузочного устройства. На этом этапе payload может использовать интерфейс, предоставляемый BIOS.

Стоит отметить, что спецификация BIOS не существовала в ранние дни. BIOS является де факто стандартом - он работает так, как работал на реальных IBM PC в 1980-х годах. Остальные производители просто обратным инженерным путем (reverse engineering) создавали IBM-совместимые BIOSы. В результате не было регулирования, предотвращающего BIOS производителей от изобретения новых функций BIOS или со схожей функциональностью.

Unified Extensible Firmware Interface (UEFI)

Как уже упоминалось, UEFI сам по себе является лишь спецификацией и имеет множество реализаций. Наиболее широко используемой является TianoCore EDKII, открытая референсная реализация спецификаций UEFI и PI. Хотя EDKII сам по себе не является полностью функциональной прошивкой, он предоставляет надежный фундамент для большинства коммерческих решений.

Чтобы поддерживать различные First-Stage Bootloaderы и предоставить UEFI интерфейс, был разработан проект под названием UEFI Payload. Он использует первоначальную настройку и информацию о платформе, предоставленную загрузочной прошивкой (first-stage), для подготовки системы к UEFI среде.

UEFI Payload состоит из DXE и BDS фаз, которые спроектированы как платформо-независимые. Он предлагает общий (generic) payload, который может адаптироваться к различным платформам. В большинстве случаев даже не требуется никакой дополнительной настройки или специфичных для платформы изменений - он может использоваться "как есть", получая информацию о платформе от First-Stage Bootloader.

Варианты UEFI Payload:

  1. Legacy UEFI Payload: Требует парсинг-библиотеку для обработки специфичной для реализации информации о платформе. Если bootloader обновляет свое API, payload также должен быть обновлен.

  2. Universal UEFI Payload: Следует Universal Scalable Firmware (USF) Specification, используя Executable and Linkable Format (ELF) или Flat Image Tree (FIT) в качестве формата изображения. Вместо самостоятельного парсинга он ожидает получения Hand Off Blocks (HOBs) при входе в payload.

Несмотря на то, что Legacy UEFI Payload работает нормально, сообщество EDK2 стремится переключить индустрию на Universal UEFI Payload. Выбор между этими payload'ами зависит от компонентов вашей прошивки. Например, невозможно запустить Legacy Payload с поддержкой SMM на Slim Bootloader без моего патча. С другой стороны, использование Universal Payload с coreboot требует shim-слоя, который будет переводить coreboot tables в HOBs. Такая функциональность доступна только в форке EDK2 от StarLabs.

Интерфейс

Каждая система, соответствующая спецификации UEFI, предоставляет System Table, которая передается коду, работающему в среде UEFI (драйверам, приложениям, загрузчикам ОС). Эта структура данных позволяет UEFI приложениям получить доступ к system configuration tables таким как ACPI, SMBIOS и набору UEFI сервисов.

Структура таблицы описана в MdePkg/Include/Uefi/UefiSpec.h:

typedef struct {
  EFI_TABLE_HEADER                   Hdr;
  CHAR16                             *FirmwareVendor;
  UINT32                             FirmwareRevision;
  EFI_HANDLE                         ConsoleInHandle;
  EFI_SIMPLE_TEXT_INPUT_PROTOCOL     *ConIn;
  EFI_HANDLE                         ConsoleOutHandle;
  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL    *ConOut;
  EFI_HANDLE                         StandardErrorHandle;
  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL    *StdErr;

  //
  // A pointer to the EFI Runtime Services Table.
  //
  EFI_RUNTIME_SERVICES               *RuntimeServices;

  //
  // A pointer to the EFI Boot Services Table.
  //
  EFI_BOOT_SERVICES                  *BootServices;

  UINTN                              NumberOfTableEntries;
  EFI_CONFIGURATION_TABLE            *ConfigurationTable;
} EFI_SYSTEM_TABLE;

Сервисы включают следующие типы: Boot Services, Runtime Services, Services provided by protocols (сервисы, предоставляемые протоколами).

UEFI абстрагирует доступ к устройствам с помощью UEFI Protocols. Эти протоколы представляют собой структуру данных, содержащую указатели на функции и идентифицируются с помощью Globally Unique IDentifier (GUID), что позволяет другим модулям находить и использовать их. Они могут быть обнаружены через Boot Services.

UEFI драйвер создает эти протоколы, и реальные функции (не указатели!) содержатся в самом драйвере. Этот механизм позволяет различным компонентам внутри среды UEFI взаимодействовать друг с другом и обеспечивает возможность операционной системе общаться с устройствами до загрузки собственных драйверов.

Хотя некоторые протоколы предопределены и описаны в UEFI спецификации, производители прошивок могут также создавать свои собственные протоколы для расширения функциональности платформы.

Boot Services

Предоставляет функции, которые могут использоваться только во время загрузки. Эти сервисы остаются доступными до тех пор, пока не будет вызвана функция EFI_BOOT_SERVICES.ExitBootServices() (MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c).

Указатели на все Boot Services хранятся в Boot Services Table (MdePkg/Include/Uefi/UefiSpec.h):

typedef struct {
  EFI_TABLE_HEADER        Hdr;
  ...
  EFI_GET_MEMORY_MAP      GetMemoryMap;
  EFI_ALLOCATE_POOL       AllocatePool;
  EFI_FREE_POOL           FreePool;
  ...
  EFI_HANDLE_PROTOCOL     HandleProtocol;
  ...
  EFI_EXIT_BOOT_SERVICES  ExitBootServices;
  ...
} EFI_BOOT_SERVICES;

Runtime Services

Минимальный набор сервисов, доступных после запуска операционной системы. В отличие от Boot Services, эти сервисы остаются действительными после того, как любой загрузочный модуль (например, OS bootloader) взял под контроль платформу через вызов EFI_BOOT_SERVICES.ExitBootServices().

Указатели на все Runtime Services хранятся в Runtime Services Table (MdePkg/Include/Uefi/UefiSpec.h):

typedef struct {
  EFI_TABLE_HEADER                  Hdr;
  ...
  EFI_GET_TIME                      GetTime;
  EFI_SET_TIME                      SetTime;
  ...
  EFI_GET_VARIABLE                  GetVariable;
  EFI_GET_NEXT_VARIABLE_NAME        GetNextVariableName;
  EFI_SET_VARIABLE                  SetVariable;
  ...
  EFI_GET_NEXT_HIGH_MONO_COUNT      GetNextHighMonotonicCount;
  EFI_RESET_SYSTEM                  ResetSystem;
  ...
} EFI_RUNTIME_SERVICES;

На изображении ниже показано время жизни (timeline) для boot и runtime сервисов, чтобы вы могли увидеть, когда каждая из них активна.

Boot Device Select (BDS) phase

Спецификация UEFI определяет механизм политики загрузки, называемый UEFI boot manager. Он будет пытаться загрузить UEFI приложения в определенном порядке до тех пор, пока не передаст управление найденному приложению. Этот порядок и другие настройки можно настроить, изменяя глобальные NVRAM (nonvolatile random-access memory) Variables (энергонезависимые переменные). Рассмотрим наиболее важные из них:

  • Boot#### (#### заменяется уникальным шестнадцатеричным значением) — опция загрузки/загрузочный вариант.

  • BootCurrent — опция загрузки, которая использовалась для запуска текущей системы.

  • BootNext — опция загрузки для следующего загрузочного процесса. Эта опция заменяет BootOrder только для одной загрузки и удаляется Boot Manager'ом после первого использования. Это позволяет изменить поведение следующей загрузки без изменения BootOrder.

  • BootOrder — упорядоченный список загрузочных вариантов. Boot Manager пытается загрузить первый активный вариант из этого списка. Если не удается, он пробует следующий вариант, и так далее.

  • BootOptionSupport — типы загрузочных вариантов, поддерживаемые Boot Manager.

  • Timeout — время ожидания загрузки, установленное менеджером загрузки, в секундах, перед автоматическим выбором стартового значения из BootNext или BootOrder.

Эти переменные можно легко получить в Linux воспользовавшись efibootmgr(8):

[root@localhost ~]# efibootmgr
BootCurrent: 0000
Timeout: 5 seconds
BootOrder: 0000,0001,2001,2002,2003
Boot0000* ARCHLINUX HD(5,GPT,d03ca3cf-1511-d94e-8400-c7a125866442,0x40164000,0x100000)/File(\EFI\ARCHLINUX\grubx64.efi)
Boot0001* Windows Boot Manager  HD(1,GPT,6f185443-09fc-4f15-afdf-01c523565e52,0x800,0x32000)/File(\EFI\Microsoft\Boot\bootmgfw.efi)57a94e544f5753000100000088900100780000004200430044039f0a42004a004500430054003d007b00390064006500610038003600320063002d1139006300640064002d0034006500370030102d0061006300630031002d006600330032006200330034003400640034003700390035007d00000033000300000710000000040000007fff0400
Boot0002* ARCHLINUX HD(5,GPT,d03ca3cf-1511-d94e-8400-c7a125866442,0x40164000,0x100000)
Boot2001* EFI USB Device    RC
Boot2002* EFI DVD/CDROM RC
Boot2003* EFI Network   RC

Рассмотрим процесс загрузки, основываясь на полученных знаниях. UEFI начинает проходиться (итерировать) по списку BootOrder. Для каждого элемента в списке он ищет соответствующую Boot#### переменную — Boot0000 для 0000, Boot2003 для 2003 и так далее. Если переменная не существует, Boot Manager переходит к следующему элементу. Если переменная существует, она считывает содержимое этой переменной. Каждая boot option переменная содержит EFI_LOAD_OPTION дескриптор, который представляет собой буфер с упакованными байтами переменной длины (это просто структура данных).

Структура данных описана в MdePkg/Include/Uefi/UefiSpec.h:

typedef struct _EFI_LOAD_OPTION {
  /// Атрибуты для этой записи загрузочного варианта.
  UINT32                         Attributes;

  /// Длина в байтах списка FilePathList.
  UINT16                         FilePathListLength;

  /// Человеко-читаемое описание для загрузочного варианта.
  /// Пример: 'ARCHLINUX' / 'Windows Boot Manager' / `EFI USB Device`
  // CHAR16                      Description[];

  /// Упакованный массив путей к устройствам UEFI.
  /// Пример: 'HD(5,GPT,d03ca3cf-1511-d94e-8400-c7a125866442,0x40164000,0x100000)/File(\EFI\ARCHLINUX\grubx64.efi)'
  // EFI_DEVICE_PATH_PROTOCOL    FilePathList[];

  /// Оставшиеся байты в дескрипторе загрузочного варианта — это бинарные данные, которые передается загруженному образу.
  /// Пример: '57a9...0400' в переменной Boot0001
  // UINT8                       OptionalData[];
} EFI_LOAD_OPTION;

На этом этапе прошивка будет проверять Device Path (EFI_DEVICE_PATH_PROTOCOL). В большинстве случаев наш компьютер загружается с физического устройства хранения данных (жесткий диск/SSD/NVMe и т.д.). Таким образом, Device path к устройству будет содержать следующий узел: HD(Номер раздела, Тип, Подпись, Начальный сектор, Размер в секторах).

  • Тип — указывает на используемый формат размещения таблиц разделов с помощью ключевых слов MBR (1) или GPT (2).

  • Подпись — 4ех байтовая MBR подпись, если Тип равен MBR, либо 16ти байтовый UUID, если Тип равен GPT.

Примечание: Если вам интересно, как переводятся другие пути, обратитесь к UEFI Specification v2.10, 10.6.1.6 Text Device Node Reference.

UEFI будет искать на диске раздел, соответствующий узлу. Если он существует, он должен быть помечен определенным Globally Unique IDentifier (GUID), который указывает на EFI System Partition (ESP). Этот раздел отформатирован с использованием файловой системы, основанной на FAT file system, и называется EFI File System; фактически, это обычный FAT12/16/32.

  • Обычная (Native) загрузка: если Device Path содержит явный путь к файлу File(\Path\To\The\File.efi), то UEFI будет искать именно этот файл. Например, опция Boot0000 содержит File(\EFI\ARCHLINUX\grubx64.efi).

  • Резервная (Fallback) загрузка: если Device Path указывает просто на диск, то в таких ситуациях прошивка использует резервный путь загрузки, который зависит от архитектуры — \EFI\BOOT\BOOT{arch}.EFI (BOOTx64.EFI для amd64 или BOOTia32.EFI для i386/IA32). Этот механизм позволяет работать загрузочным съемным носителям (например, USB-флеш) в UEFI, они просто используют резервный путь загрузки. Например, опция Boot0002 будет использовать этот механизм.

Примечание: Все упомянутые выше опции Boot#### относятся к параметрам загрузки, полученным в примере использования утилиты efibootmgr.

В обоих случаях UEFI Boot Manager загрузит UEFI Application (это может быть OS bootloader, UEFI Shell, утилита, Setup Menu и другое) в память. В этот момент управление передается точке входа в UEFI application. В отличие от BIOS, UEFI приложение может вернуть управление прошивке (за исключением ситуации, когда приложение берет на себя управление системой). Если это произойдет или если что-то пойдет не так, Boot Manager перейдет к следующей записи Boot#### и повторит тот же процесс.

Спецификация упоминает, что Boot Manager может заниматься обслуживание базы данных переменных. Это включает удаление переменных загрузочных опций, которые не ссылаются или не могут быть разобраны. Кроме того, он может переписывать любой упорядоченный список, чтобы удалить любые загрузочные опции без соответствующих переменных загрузочных опций.

Вышеприведенный текст описывает UEFI загрузку. Также прошивка UEFI может работать в режиме Compatibility Support Module (CSM), который эмулирует BIOS.

OS Boot Loader

Программное обеспечение, запускаемое прошивкой (обычно Second-Stage Bootloader) и использующее его интерфейс для загрузки ядра ОС. Загрузчик ОС может быть столь же сложным, как и сама ОС, предлагая такие функции, как:

  • Чтение с различных файловых систем (HFS+, ext4, XFS и др.)

  • Взаимодействие через сеть (например, TFTP, HTTP)

  • Загрузка ядер, совместимых с Multiboot

  • Chainloading

  • Загрузка начальных ramdisk (initrd)

  • И многое другое!

Общий дизайн этих программ выходит за рамки данной статьи. Для подробного сравнения популярных загрузчиков ОС вы можете обратиться к ArchLinux wiki и статье в Википедии.

Windows система использует собственный проприетарный загрузчик ОС, известный как Windows Boot Manager (BOOTMGR).


Firmware (прошивка) больше не является маленьким, сложным фрагментом кода. Она превратилась в огромный объем сложного кода, и современные тенденции только способствуют этому. Мы можем запускать Doom, Twitter и многие другие интересные приложения прямо в прошивке.

Понимание общей архитектуры помогает лучше представить, как все компоненты работают вместе. Изучая структуру существующих прошивок, вы сможете увидеть и оценить впечатляющий процесс, который происходит каждый раз, когда компьютер запускается. Такой подход позволяет не только понять роль каждой части, но и увидеть насколько сложными становятся современные системы прошивки.

Источники

Комментарии (4)


  1. oleg_rico
    22.08.2024 16:52

    Как раз буквально в прошлые выходные забирал свежесобранный компьютер для деревни. И как же пришлось помучиться с биосом потому что он никак не хотел загружать операционку. Не скажу, что эта статья помогла бы, но очень интересно прочитать как это всё устроено более детально.


    1. john_do
      22.08.2024 16:52

      Если тебе собирали, то нужно было попросить протестировать систему.

      Ну а если сам собрал и не работает, здесь я умываю руки.


      1. oleg_rico
        22.08.2024 16:52

        Система работала до биоса А дальше не начиналась загрузка. Хотя я тщательно подготовил диск для установки командой sysprep.

        Ребята которые собирали в DNS что-то там в биосе но так ничего сделать и не смогли. Пришлось уже дома самому выбрать правильный режим загрузки операционной системы и всё получилось.

        А потом пришлось помучиться с поиском как организовать загрузку с другого диска потому что выяснилось что в MSI выбор диска в биосе получается только при переключении на английский.

        Там много было нюансов начиная от того что никто не порекомендовал брать не встроенную видюху а отдельную потому что у меня монитор на DVI а переходники работают хреново не дают возможность разрешения более 1.920 и заканчивать таким нюансом что не знают как настроить bios. Ребята хорошие, душевные, но сейчас просто нет тех сборщиков которые были когда-то знающие всё от и до без гугла.


  1. NickDoom
    22.08.2024 16:52
    +2

    Держите все возможные плюсы, эти статьи необходимы для того, чтобы больше было наконец возможностей избавиться от этого решета безопасности в лице современных «уёфищ». Чем больше людей в это вникает, тем больше рук будет у всяких корбутов.

    Да, я понимаю, что какой-нибудь SMBIOS может дать жару любому энтузиасту, потому что поди разбери, что там надо подать этой «атмеге» посреди материнки, чтобы она переключила питание в S1 или там в S3. Это не планка памяти, где всё плюс-минус стандартно, это один Ктулху ведает что за штука :) что херстеллер захотел, то и сделал :)

    Но почему-то мне кажется, что если немного пореверсить, что там по SMBus гуляет, то можно и эту проблему благополучно разрешить :)