Скрытый мир прошивки: изучение процесса загрузки вашего компьютера

Скрытый мир прошивки: изучение процесса загрузки вашего компьютера

21 апреля 2023 г.

Многих интересует, как запускается компьютер. Здесь начинается волшебство, которое продолжается до тех пор, пока устройство включено. В этой статье мы рассмотрим boot, включая его различные этапы, основные задействованные компоненты и проблемы, возникающие в процессе.

Хотя наше основное внимание будет сосредоточено на архитектуре x86 (наиболее широко используемой), другие архитектуры будут иметь много общего в процессе загрузки. Я надеюсь, что эта статья будет ценным ресурсом для тех, кто хочет углубить свои знания в этой области. Вот так!

Содержание:

  • ЗАГРУЗОЧНАЯ ПРОГРАММА
  • Энергонезависимая память
    • Программируется один раз
    • Программируется на месте
  • Выполнение на месте (XIP)
    • Кэш-память (CAR)
  • Макет & Отображение памяти
    • Режим без дескриптора
    • Intel Flash Descriptor/режим дескриптора
    • Таблица интерфейса встроенного ПО Intel (FIT)
    • Структура встроенного ПО AMD
  • Инициализация микросхемы
  • Пакет поддержки встроенного ПО Intel (FSP)
  • Общая инкапсулированная программная архитектура AMD (AGESA)
  • Автономные подсистемы
  • Интел МЭ
  • AMD PSP
  • Последовательности питания оборудования

ЗАГРУЗОЧНОЕ ПЗУ

интегральная схема (микросхема), расположенная на материнской плате и хранящая код микропрограммы, отвечающая за загрузку компьютера, называется BOOT ROM. Это название не стандартизировано, поэтому другие разработчики часто называют его FLASH ROM, BIOS FLASH, BOOT FLASH, SPI FLASH и т. д. (такие названия им даются из-за названий технологии, интерфейса и назначения). Не беспокойтесь, эти термины взаимозаменяемы. Код микропрограммы в BOOT ROM выполняется первым при включении компьютера. Он выполняет базовые тесты, инициализирует аппаратное обеспечение, а затем загружает загрузчик ОС с загрузочного устройства, например жесткого диска или USB-накопителя, в память. Этот чип сделан из энергонезависимой памяти (NVM).

Энергонезависимая память

Энергонезависимая память – это тип памяти компьютера, который сохраняет свое содержимое даже при отключении питания. Это делает этот тип памяти идеальным для хранения важных данных, которые необходимо сохранить, даже когда компьютер выключен. Кроме того, речь пойдет только о памяти, содержащей код прошивки. Мы не будем говорить о таких хранилищах, как жесткие диски (HDD), твердотельные накопители (SSD), гибкие диски и т. д.

По сути, мы можем разделить этот тип памяти на следующие группы.

Программируется один раз

  • Замаскированное ПЗУ: содержимое определяется во время изготовления и не может быть изменено после этого.
  • Программируемое ПЗУ (ППЗУ): В отличие от ПЗУ с масками, этот тип памяти может быть запрограммирован после изготовления. Но все же только один раз.

Программируется на месте

  • Стираемое программируемое ПЗУ (EPROM): может быть запрограммировано несколько раз, но его содержимое может быть стерто и перепрограммировано с помощью ультрафиолета.

* Электрически стираемое программируемое (EEPROM): можно многократно перепрограммировать с помощью электрических сигналов.

* Флэш-память NOR: архитектурно организована в блоки, где данные стираются на блочном уровне и могут быть прочитаны или записаны на байтовом уровне. Доступ к памяти NOR осуществляется напрямую через стандартный интерфейс, такой как параллельный байт, I2C или SPI.


In the industry, there is a convention to reserve the term **EEPROM** to byte-wise erasable memories compared to block-wise erasable **flash** memories.

Для программируемой памяти действует одно правило: стереть перед записью. В такой памяти запись новых данных более сложна, потому что данные хранятся в виде заряда на плавающем затворе (причина большей части просто кроется в физике ячеек памяти). Количество заряда на воротах определяет, хранит ли ячейка «0» или «1».

Когда вы стираете микросхему флэш-памяти, вы устанавливаете все биты данных, хранящихся на ней, в известное (по умолчанию) состояние, обычно логическую «1». Это позволяет вам, так сказать, начать с чистого листа и запрограммировать новые данные на чип, не сохраняя на нем остатки старых данных. Когда новые данные записываются в микросхему, состояние отдельных битов изменяется с «1» на «0», чтобы представить новые данные.

Если вы просто запишете новые данные в чип без предварительного их стирания, новые данные будут объединены со старыми данными, что приведет к непредсказуемым результатам. Например, рассмотрим микросхему флэш-памяти с 8 битами памяти, в которой хранится значение «0110 0010». Если вы запишете в чип новые данные "1100 1001" без предварительного их стирания, результирующее состояние чипа будет "0100 0000", что может быть не таким, как вы предполагали.

Основная путаница связана со словом ROM, которое означает Read Only Memory. Термин «постоянная память» исторически использовался для обозначения памяти, которая является постоянной и не может быть изменена пользователем. Однако по мере развития технологий определение ПЗУ изменилось, и теперь оно часто используется для обозначения памяти, которая предварительно запрограммирована на заводе и не может быть легко изменена конечным пользователем. Но если у пользователя есть желаемые навыки и специализированное оборудование (например, программатор), человек может перепрограммировать чип. Название ROM осталось, несмотря на то, что определение изменилось, как историческая ссылка на первоначальное назначение памяти.

При применении защиты от записи некоторые типы перепрограммируемых ПЗУ могут временно стать постоянной памятью.

Это НЕ ВСЕ существующие типы энергонезависимой памяти, но большинство популярных, о которых вы можете услышать случайно. В настоящее время на большинстве системных плат эти микросхемы изготавливаются с использованием технологии NOR Flash.

Выполнение на месте (XIP)

Выполнение на месте (XIP) — это метод, который позволяет процессору выполнять код непосредственно из флэш-памяти, не копируя его в энергозависимую память (например, ОЗУ). Это достигается путем сопоставления флэш-памяти с адресным пространством процессора, так что выполнение кода может выполняться непосредственно из флэш-памяти. Таким образом, система может начать выполнение кода как можно скорее, не дожидаясь первой инициализации ОЗУ.

Подождите... ЦП может связываться с BOOT ROM по протоколу SPI/Parallel/etc? Конечно нет, это просто получение инструкций из системной памяти, запросы к этой области памяти перенаправляются в Intel Direct Media Interface (DMI)< /a> или AMD Infinity Fabric (IF) / Unified Media Interface (UMI) (предшественник). Это связь между процессором и чипсетом на материнской плате. В этот момент происходит декодирование адреса с помощью декодеров, расположенных в чипсете, и данные с чипа возвращаются в процессор.

Когда чип сделан из флэш-памяти NOR, которая поддерживает чтение с произвольным доступом, но не запись с произвольным доступом, возникла одна проблема. Поскольку доступной для записи памяти нет, все вычисления должны выполняться в регистрах процессора. На этом этапе код может быть написан только на языке ассемблера, и он стремится настроить среду для языка высокого уровня (как правило, для язык C). Причина этого в том, что инициализация памяти стала настолько сложной, что было бы сложно писать только на ассемблере. Так как для таких языков требуется как минимум куча и стек, нам нужна перезаписываемая память. Некоторые процессоры имеют SRAM, встроенную в сам чип, но более современный подход заключается в использовании встроенной кэш-памяти в качестве RAM (CAR).

Кэш-память (CAR)

Кэш процессора – это высокоскоростная память, в которой хранится копия часто используемых данных и инструкций из основной памяти. Кэш расположен ближе к процессору и состоит из нескольких уровней (L1, L2, L3, ...), причем каждый уровень больше и медленнее предыдущего.

Если данные находятся в кэше, ЦП может извлечь запрошенные данные из кэша (это называется попаданием в кэш). Когда кэш ЦП не может найти необходимые данные, это приводит к промаху кэша. Это может произойти либо из-за того, что данные никогда не сохранялись в кэше, либо из-за того, что данные ранее сохранялись, но были удалены из кэша. В любом случае, процессор должен пройти весь путь до основной памяти, чтобы получить доступ к данным и скопировать их в кеш.

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

Однако, если мы хотим использовать кэш ЦП в качестве ОЗУ, нам нужно настроить кеш для работы в режиме без вытеснения, который также называется без заполнения. Режим. Этот метод предотвращает удаление из-за промаха кэша. Вместо этого кеш обрабатывается как обычная SRAM, и все обращения (чтение/запись) попадают в кеш, а не в основную память. Этот режим можно активировать с помощью инструкций процессора конкретного поставщика.

Макет & Отображение памяти

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

Недескрипторный режим

Изначально чипсет выполняет прямое сопоставление всего содержимого BOOT ROM с памятью (от 4 ГБ до 4 ГБ — 16 МБ). Как правило, если размер загрузочного ПЗУ меньше 16 МБ, его содержимое повторно отображается. ЦП и прошивка могут читать/записывать во флэш-память без каких-либо ограничений.

Недескрипторный режим больше не поддерживается новыми чипсетами.

Intel Flash Descriptor/Режим дескриптора

В конце концов, в ICH8 Intel представила специальный макет для BOOT ROM. Flash разделен на следующие области:

* Flash Descriptor (FD) - эта структура данных должна располагаться в начале устройства со смещением 0x10. Он состоит из одиннадцати разделов, как показано на рисунке ниже:

Descriptor MAP содержит указатели на другие регионы и размер каждого из них.

В разделе Компонент содержится информация о флэш-памяти в системе (количество компонентов, плотность каждого, недопустимые инструкции и т. д.).

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

* BIOS - в память отображается только этот регион.

* Intel Converged Security and Management Engine (CSME/ME) — прошивка для поддержки различных технологий Intel и ME. * Gigabit Ethernet (GbE) — прямой доступ возможен только через контроллер Gigabit Ethernet. * Данные платформы * Встроенный контроллер (EC)

Единственными обязательными регионами являются дескриптор Flash и Intel ME.

Таблица интерфейса встроенного ПО Intel (FIT)

FIT — это структура данных внутри региона BIOS, содержащая различные записи, описывающие конфигурацию платформы. Каждая запись в таблице имеет размер 16 байт. Первый называется заголовком FIT, второй — записью FIT. Он расположен с помощью указателя FIT по физическому адресу 0xFFFFFFFC0 (4 ГБ – 0x40).

Эти компоненты должны быть обработаны до выполнения первой инструкции ЦП из вектора сброса. Записи включают обновления микрокода ЦП, Startup ACM, политики загрузки платформы/TPM/BIOS/TXT и другие материалы. Но, по крайней мере, FIT должен включать записи FIT Header и Microcode Update. Таким образом, обычно FIT используется для обновления микрокода перед выполнением вектора сброса.

Вот как выглядит карта памяти:

Структура встроенного ПО AMD

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

На самом деле, будет достаточно знать, что AMD-аналог Flash Descriptor — это Embedded Firmware Structure, и он содержит указатели на Таблицу каталогов PSP, Таблицу каталогов BIOS, и другие прошивки.

Инициализация кремния

Если вы хотели посмотреть, как именно инициализируется современная память и процессор, то я должен вас огорчить. Intel и AMD не спешат публиковать Silicon Initialization Code для сообщества. Поскольку такой информации нет в открытом доступе, они предлагают бинарный дистрибутив необходимого кода инициализации кремния. Это библиотека для разработчиков микропрограмм, содержащая двоичный код для инициализации контроллера памяти, набора микросхем, ЦП и других частей системы.

Пакет поддержки встроенного ПО Intel (FSP)

Этот двоичный файл можно разделить на 4 компонента:

* FSP-T: Настройка среды раннего выполнения («Временная оперативная память»), в которой может выполняться код C. На практике этот двоичный файл устанавливает CAR, но также выполняет некоторую раннюю аппаратную инициализацию, такую ​​как настройка пространства конфигурации с отображением памяти PCIe. * FSP-M: Инициализация постоянной памяти (например, DRAM). * FSP-S: завершение инициализации кремния, включая инициализацию процессора и контроллера ввода-вывода. * FSP-O: дополнительный компонент, обеспечивающий инициализацию OEM-устройств.

Вот репозиторий двоичных файлов Intel FSP, опубликованный Intel, который вы можете найти на их GitHub. Спецификацию FSP v2.1 можно получить на веб-сайте Intel.

Общая инкапсулированная программная архитектура AMD (AGESA)

AGESA для продуктов до семейства 17h называется v5 или Arch2008. В то время исходный код AGESA был открытым, и код был доступен в репозиторий coreboot (устарел после версии 4.18). Спецификацию Arch2008 можно найти на веб-сайте AMD.

С введением продуктов Family 17h (микроархитектура Zen) AMD не публиковала исходный код AGESA, а только готовые двоичные файлы. решения. Такой преемник называется AGESA v9 и поддерживает семейство 17h и более поздние версии.

openSIL

Подробной информации нет, только новости.

Автономные подсистемы

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

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

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

Intel Management Engine (ME)

Intel ME — это отдельный микропроцессор i486/80486, интегрированный в набор микросхем Intel (PCH) с 2008 года. собственное ОЗУ, встроенное ПЗУ, шинные мосты со всеми шинами внутри чипсета (в результате он может получить доступ к сети и даже к основной ОЗУ на ЦП) и так далее. Запускает пользовательскую ОС на основе MINIX.

Процессор безопасности платформы AMD (PSP)

AMD PSP — это ядро ​​ARM, основанное на расширении Trustzone, которое вставляется в кристалл ЦП в качестве сопроцессора. Этот чип интегрирован в большинство платформ AMD с 2013 года. Работает под управлением недокументированной проприетарной ОС.

Последовательности питания оборудования

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

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

https://www.youtube.com/watch?v=G4FOBL1c3pA&embedable=true

* материнская плата посылает сигнал на блок питания (БП).

* Блок питания получает сигнал, обеспечивает необходимое количество электроэнергии и отправляет сигнал обратно на материнскую плату. * Как только материнская плата получает сигнал питания, она включает компоненты платформы, такие как ядро, часы, набор микросхем, память, различные контроллеры и т. д. * Различные подсистемы, в том числе автономные подсистемы (описанные выше), могут запускаться раньше основного процессора.

* Системы на базе AMD (для семейства 17h и более поздних версий)

* PSP выполняет на-чипе BOOT ROM. * PSP находит Таблицу встроенного ПО в выключенном-чипе BOOT ROM и выполняет встроенное ПО PSP. * PSP анализирует таблицу каталогов PSP, чтобы найти этапы ABL и выполнить их. * Этапы ABL инициализируют основную память, находят образ BIOS в BOOT ROM и загружают его в DRAM (распаковывают, если образ сжат).

* Системы на базе Intel * Набор микросхем (ICH/PCH) находит Intel Flash Descriptor в BOOT ROM. * Чипсет копирует прошивку CSME во внутреннюю память, где Intel ME может получить к ней доступ, и последний начинает ее выполнение. * Набор микросхем сопоставляет регион BIOS с памятью. * Обновления микрокода, расположенные в Таблице интерфейса встроенного ПО, загружаются в ЦП. Их необходимо применять при каждой загрузке системы. * (необязательно) Если обнаружены модули кода с проверкой подлинности (ACM), то эта запись выполняется.

* Все это время подается сигнал сброса ЦП, чтобы предотвратить запуск ЦП до того, как другие части системы будут готовы. Когда платформа готова, строка сброса процессора снимается. В многопроцессорной или многоядерной системе один ЦП динамически выбирается в качестве загрузочного процессора (BSP), на котором выполняется весь код инициализации встроенного ПО. Остальные процессоры, называемые на данный момент процессорами приложений (AP), остаются остановленными до тех пор, пока позже они не будут явно активированы прошивкой/ядром.

* После первого включения ЦП он работает в реальном режиме. Большинство регистров имеют четко определенные значения, включая указатель инструкций (IP), сегмент кода (CS) и кеш дескрипторов, который является копирование каждого дескриптора сегмента внутри процессора, чтобы обеспечить быстрый доступ к памяти сегмента.

Дескриптор сегмента — это запись в Глобальная таблица дескрипторов (GDT), содержащая базовый адрес, лимит сегмента и информацию о доступе (эта часть игнорируется, поскольку в реальном режиме нет контроля доступа, как в защищенном режиме). Вместо обращения к GDT (находящемуся в памяти) при каждом доступе к памяти информация сохраняется в кэше дескрипторов.

Однако GDT не задействован в реальном режиме, поэтому процессор создает записи внутри себя. Регистр селектора CS, используемый для доступа к дескриптору сегмента, загружается с помощью 0xF000. Базовый адрес CS инициализируется как 0xFFFF_0000. IP инициализируется как 0xFFF0.

Поэтому процессор начинает извлекать инструкции из памяти, расположенной по физическому адресу 0xFFFF_FFF0 (0xFFFF_0000 + 0x0000_FFF0). Первая инструкция, выполняемая по этому адресу, называется вектором сброса.

ПРИМЕЧАНИЕ. Этот трюк дает вам доступ к верхнему адресному пространству, однако вы не можете получить доступ к коду ниже адреса 0xFFFF_0000. Базовый адрес CS остается в этом начальном значении до тех пор, пока микропрограмма не загрузит регистр селектора CS. Это можно сделать, совершив дальний прыжок.

На данный момент лучшим решением будет переключиться в защищенный режим с 4 ГБ адресации. Если прошивка этого не делает, то для того, чтобы работал реальный режим, чипсет должен иметь возможность совмещать диапазон памяти ниже 1 МБ с эквивалентным диапазоном чуть меньше 4 ГБ. Некоторые наборы микросхем не имеют этого псевдонима и могут потребовать переключения в другой режим работы перед выполнением первого длинного перехода.

* Адрес находится в разделе энергонезависимой памяти, поэтому ЦП использует метод Execute In Place (XIP). Хотя, если это система на базе AMD, вы, вероятно, читаете из основной памяти.

* ЦП выполняет код прошивки.

Рекомендую посмотреть видео ниже о последовательности включения питания, в котором объясняется процесс на примере материнской платы ASUS P9X79. Несмотря на то, что он на русском языке, вы сможете все понять, если включите автоматически генерируемые английские субтитры.

https://www.youtube.com/watch?v=lAH7eSjR1d4&embedable=true


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

В следующей статье мы более подробно рассмотрим BIOS, UEFI и coreboot.

Ресурсы


Оригинал