Структура EFI

Структура упаковки EFI отличается от "классической", что обязательно нужно учитывать, чтобы разобраться в общей схеме его (таки EFI - это "ифай", а не "ефи", потому причислим оного к мужскому роду:) ) работы. В классической схеме "старого" BIOS для каждого модуля имеется "заранее" прописанное место назначения/распаковки, которое или берётся из его заголовка напрямую, либо определяется его ID. В EFI же модули не имеют подобной привязки и за их распаковку/загрузку/расположение в памяти отвечает соответствующий менеджер, действуя, так сказать, "по собственному усмотрению".

Важным отличием, которое обычно сразу бросается в глаза – «неровный» объём файла EFI-образа. Хотя, понятно, в микросхему он зашивается «ровным». Если так – значит у вас не образ для прошивки, а «капсула», в которой имеются уже нужные модули. Т.е. EFI, в отличие от BIOS может быть представлен в двух вариантах – в качестве «обычного» образа для прошивки (с «кратным» объёмом) и в качестве «капсулы», имеющей «неровный» объём, обычно чуть более мегабайта (но не обязательно – может быть 4Мб и больше).

/* Начну по-порядку, далее, после, поредактирую. */

Определить капсулу просто – в самом её начале будет (самые первые 16 байт):

BD 86 66 3B 76 0D 30 40 B7 0E B5 51 9E 2F C5 A0

Это её (капсулы) GUID, уникальный 16-байтный идентификатор, который имеют все структуры (т.е. не только модули, но и любые упорядоченные нужные данные) в EFI.

Саму структуру капсулы можно найти в исходниках:

typedef struct {
  EFI_GUID  CapsuleGuid;
  UINT32    HeaderSize;
  UINT32    Flags;
  UINT32    CapsuleImageSize;
  UINT32    SequenceNumber;
  EFI_GUID  InstanceId;
  UINT32    OffsetToSplitInformation;
  UINT32    OffsetToCapsuleBody;
  UINT32    OffsetToOemDefinedHeader;
  UINT32    OffsetToAuthorInformation;
  UINT32    OffsetToRevisionInformation;
  UINT32    OffsetToShortDescription;
  UINT32    OffsetToLongDescription;
  UINT32    OffsetToApplicableDevices;
} EFI_CAPSULE_HEADER;

К сожалению, здесь и дальше «навсегда», тем, кто захочет «плотно» разобраться с темой EFI нужно учесть, что расхожее мнение, что «там всё есть» - в плане Open Sources (конкретно для EFI – tianocore.org, uefi.org) ничего хорошего не обозначает, кроме того, что в переводе на русский – там можно найти «полуфабрикат» и его нужно будет, во-первых, правильно готовить, во-вторых, на вкус и цвет товарища нет. Последнее утверждение выливается в то, что некоторые как бы «стандартные» (т.е. вроде бы «стандартизированные») вещи некоторые «отдельно взятые» BIOS-писатели – трактуют «немножко по-своему».
Кроме того, это нужно помножить на эволюцию EFI от 1.0/1.1 до EFI/UEFI2.1b на данный момент. При том, некоторые вещи принципиально поменялись при переходе на вторую версию, однако были оставлены возможности для поддержки (т.е. есть в исходниках) сразу обоих, что выливается в двойное трактование некоторых структур (т.к. для старых версий они были одного вида, а в современных – другого). Так и EFI-образы, с которыми вы столкнётесь могут относится к разным версиям.
Применительно к нашему самому первому случаю та же структура EFI-капсулы в девичестве была другой. Однако, чтобы вас лишний не путать, я здесь и дальше буду указывать лишь «правильные» версии структур, обычно это последние версии (не всегда, т.к. несмотря на наличие и поддержку новых спецификаций никто не запрещает пользоваться BIOS-писателям старым проверенным кодом).

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

Капсула – как матрёшка. В ней есть другие матрёшки, которые могут содержать внутри себя ещё матрёшки и так далее. В этом есть важное отличие – в стареньком BIOS подобной «рекурсии» не наблюдается.

FFS (Firmware File System) – есть «чисто» матрёшка, которая позволяет помечать нужную часть собственной «биркой» (т.к. имеет свой GUID):

//
// FFS file header definition
//
typedef UINT8 EFI_FFS_FILE_ATTRIBUTES;
typedef UINT8 EFI_FFS_FILE_STATE;

typedef struct {
  EFI_GUID                Name;
  EFI_FFS_INTEGRITY_CHECK IntegrityCheck;
  EFI_FV_FILETYPE         Type;
  EFI_FFS_FILE_ATTRIBUTES Attributes;
  UINT8                   Size[3];
  EFI_FFS_FILE_STATE      State;
} EFI_FFS_FILE_HEADER;

FFS или дальше просто «файл» в нашей системе – есть «обёртка» для FV – Firmware Volume. А вот именно FV уже являются важнейшими для нас матрёшками, внутри них содержится «логически законченный» набор блоков/модулей. К примеру, PreEFI (аналог BootBlock для BIOS). Или FV ядра, где содержатся модули основной стадии (аналог POST для BIOS), который, кстати, может содержать сотни вложенных файлов-модулей.


Структура «раздела» (FV):

//
// Firmware Volume Header definition
//
typedef struct {
  UINT8                   ZeroVector[16];
  EFI_GUID                FileSystemGuid;
  UINT64                  FvLength;
  UINT32                  Signature;
  EFI_FVB_ATTRIBUTES      Attributes;
  UINT16                  HeaderLength;
  UINT16                  Checksum;
#if (PI_SPECIFICATION_VERSION < 0x00010000)
  UINT8                   Reserved[3];
#else
  UINT16                  ExtHeaderOffset;
  UINT8                   Reserved[1];
#endif
  UINT8                   Revision;
  EFI_FV_BLOCK_MAP_ENTRY  FvBlockMap[1];
} EFI_FIRMWARE_VOLUME_HEADER;

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

На этом вложенность не заканчивается, я бы даже сказал – только начинается , т.к. FV – тоже, в принципе, можно обозвать лишь «обёрткой». Внутри разделов располагаются секции, которые бывают разные, потому имеют разные заголовки, но неизменяемая их начальная часть имеет следующий вид:

//
// Common section header
//
typedef struct {
  UINT8 Size[3];
  UINT8;
} EFI_COMMON_SECTION_HEADER;

Как можно догадаться байт типа (Type) определяет, что это за секция и какие внутри неё будут данные. Внутри секций уже снова могут располагаться файлы, разделы, снова секции (вложенные в предыдущие «матрёшки» и т.д. Подобная вложенность может достигать десятого и более уровней…

Однако главной единицей информации является «модуль». Он содержится внутри секций (если нет вложений). Тип секции описывает тип вложенного в неё модуля, которые бывают разными (больше десятка видов), но наиболее распространёнными (точней – важными для нас) являются PE32-модули. Это, как видно из названия - PE32-файлы и по сути являются «нормальными» dll-ками лишь со слегка изменёнными заголовками. Данный факт сразу проливает много на всю суть EFI, которая имеет внутри себя по большому счёту набор «почти виндовых» dll-ок. А раз это dll-ки, то и механизмы их обработки/подгрузки/защиты – схожие. Но это уже совсем другая история…

Отличная статейка, и хорошее продолжение EFI темы. Можно сделать что-то вроде справочного описания для основных EFI-модулей.
Немножко дополню про капсулу. 
GUID, с которого начинается капсула: 


BD 86 66 3B 76 0D 30 40 B7 0E B5 51 9E 2F C5 A0
в исходном коде определен так:
#define EFI_CAPSULE_GUID { 0x3b6686bd, 0xd76, 0x4030, 0xb7, 0xe, 0xb5, 0x51, 0x9e, 0x2f, 0xc5, 0xa0}
Капсула является модулем-посредником между ОС и EFI и служит для передачи данных или внесения изменений в прошивку на этапе загрузки EFI BIOS. Также может содержать исполнительный код (модуль) или содержать под-капсулу, т.е. полная абстракция и неограниченные возможности для реализаций.

Аватар пользователя apple_rom

Ну, пожалуй, это только зародыш статейки, точней даже - лишь её зачатие.:)

Просто сложно собраться и решиться описать главные моменты работы EFI, потому просто начал "с чего-нибудь". Если кому-то нужно, кстати, разобраться с конкретным EFIBIOS - предлагайте (желательно поновей), будем разбирать на конкретном примере.

Касаемо капсулы, то весьма сложно сразу обхватить необъятное. А так, правильно, капсула задумывалась как структура для перезаписи (EFI)BIOS. Общая философия следующая - под Windows (Linux, MACOS etc) запускается специально обученная программа (гомомодифицированный внук прошивальщика), которая формирует специальную структуру в оперативной памяти (также, возможно, в специальном месте/адресах) после чего системе посылается не менее специальный сигнал рестарта. Специален он тем, что при перезагрузке не проискходит очистка памяти (как минимум области расположения капсулы). После RESET стартует BIOS (EFI), в котором до момента стандартной очистки всей памяти срабатывает специально натренированная процедура, которая перехватывает процесс работы по найденной в памяти сигнатуре капсулы. После некоторых проверок её (капсулы) целостности, а также (не)хитрой проверки на вшивость (что запущена не злобным трояном) - капсуле передаётся управление и наступает её трансмутация в микросхему BIOS. Обычно сие таинство протекает в SMM.
Однако в нашем случае капсула будет важна больше не тем, зачем она нужна, а чисто практически - как из неё получить "нормальный" EFIBIOS.

Аватар пользователя apple_rom

Итак, снова, здравствуйте мои много и по-разному уважаемые! Надеюсь, толпы читающих сии опусы достигают в перигее лишь моего монитора (паршивенький, честно сказать, но таки цельных 20 импортных сантиметров), потому позволю себе расслабабиться (неплохая очепятка, можно и оставить) чутка вечерним словоблудием на между и околотехнические темы.
Итак, о чём это мы. Ах, да, EFI, будь он(о) неладно. Так вот, скажу я вам, что эту штуку явно накрапали не программеры, а самые что ни на есть прокилограммеры. Всё складывается, всё раскладывается так, просто держись. Закончили, насколько понятно, на матрёшках и капсулах. С капсулами, кто и зачем её породил – вроде ясно. А вот с матрёшками нужно будет поднапрячься иначе никак. Главная задумка всех этих матрёшек – стандартизирования сборки EFI под новомодные компиляторы. Нонче в них рулят xml, потому всё должно как можно более универсальней описано и иметь вариантик на любой, даже самый больной случай. Хотя, конечно, я тут подумал, возможно, это больше важно будет для тех, кто задумает написать свою программулину для работы с EFI.

Если это ты, будущий EFI-программер, то заранее тебе сочувствую. Ты наивно думаешь, что в EFI есть лишь два варианта упаковки модулей – “Standard compression” (типа 0) и “Tiano compression” (типа 1)? Так написано в даташитах? Да, верно, написано. Но, как любил говаривать наш прапорщик - «На заборе тоже написано, а там танки стоят». А про третью поправку восьмого исправления четвёртой редакции от 32 мая того года – ничегось не слышал? Так вот, там чётко тёмным по светлому написано – в военное время не только прямой угол достигает 100 градусов, но и бит типа компрессии может достигать двойки. И самое противное: где, кто и когда объявляет военное положение – неизвестно даже упомянутому прапорщику.
Мораль у этой короткой военно-патриотической басни такова – даже нули и единицы каждый из BIOS-писателей могут трактовать по-своему, что уж говорить про остальное…

Но, всё же, чтобы совсем уж не уйти от технических моментов, остановимся на компрессии. Жмутся в EFI секции, для которых и предусмотрен конкретный тип таковых. В роли компрессии выступает недалеко ушедший от «классического» LZINT-метода компрессии (т.е. такого же, как используется в Award/AMI /Phoenix) алгоритм с двумя, отличающимися лишь «тонкими» настройками подвариантами (разные дефайны некоторых констант), которые именуются как Standard Compression и Tiano Compression. Обычно жмутся лишь «конечные» модули, однако секции (в плане - содержащие внутри себя другие матрёшки) тоже жмутся. Хоть, как бы особо не должны (но об этом уже было выше).

Т.е. смысл простой, типы секций могут быть (обратимся к первоисточнику):

//
// ////////////////////////////////////////////////////////////////////////////
//
// Section types
//
typedef UINT8 EFI_SECTION_TYPE;
//
// ************************************************************
// The section type EFI_SECTION_ALL is a psuedo type.  It is
// used as a wildcard when retrieving sections.  The section
// type EFI_SECTION_ALL matches all section types.
// ************************************************************
//
#define EFI_SECTION_ALL 0x00
//
// ************************************************************
// Encapsulation section Type values
// ************************************************************
//
#define EFI_SECTION_COMPRESSION   0x01
#define EFI_SECTION_GUID_DEFINED  0x02
//
// ************************************************************
// Leaf section Type values
// ************************************************************
//
#define EFI_SECTION_FIRST_LEAF_SECTION_TYPE 0x10

#define EFI_SECTION_PE32                    0x10
#define EFI_SECTION_PIC                     0x11
#define EFI_SECTION_TE                      0x12
#define EFI_SECTION_DXE_DEPEX               0x13
#define EFI_SECTION_VERSION                 0x14
#define EFI_SECTION_USER_INTERFACE          0x15
#define EFI_SECTION_COMPATIBILITY16         0x16
#define EFI_SECTION_FIRMWARE_VOLUME_IMAGE   0x17
#define EFI_SECTION_FREEFORM_SUBTYPE_GUID   0x18
#define EFI_SECTION_RAW                     0x19
#define EFI_SECTION_PEI_DEPEX               0x1B

#define EFI_SECTION_LAST_LEAF_SECTION_TYPE  0x1B
#define EFI_SECTION_LAST_SECTION_TYPE       0x1B

Как видно, если тип секции равен единице, значит она пожатая. Также ещё видно, что валидные типы это 1, 2, 0x10-0x1B. Однако сразу нужно дополнить, что по всё тому же правилу военного буравчика к этому списку нужно приплюсовать 0x00, 0x1C и 0xFF.
Самые важные типы – 0x10, который обозначает, что в секции находится PE32-модуль и 0x17, который обозначает, что внутри её матрёшка раздела - FV. Тип секции 0x19 обозначает, что в ней лежат «просто данные», часто это какие-то «внешние», не используемые непосредственно EFIBIOS данные (например, нужные для прошивальщика и т.п.), а также картинки/логотипчики. В секции 0x15 лежит (если она есть, конечно) «человеческое» название модуля, т.е. просто его название. Несмотря на подобную заботу о людях в реальности BIOS-писатели её успешно не пользуются и «человеческие» названия являются исключением, а не правилом (если их не «выуживать» другими способами, как в указанных выше на картинках). Секция 0x16 пусть особо не возбуждает ваше воображение, ибо ничего особо полезного «совместимого» там нету и предназначена она для 16-битного SMM-обработчика. Модуль 0x12-того типа (то есть тип секции, который его и определяет) есть «недомодуль PE32-типа», который отличается более упрощённым заголовком и предназначен для исполняемых файлов на PEI-стадии (т.е. в проекции на BIOS – _исполняемый_ модуль для BootBlock-а).
…Так, а ведь про стадии то я и не рассказал. А где, собственно, начальник транспортного цеха?!?...

Цитата:
FFS или дальше просто «файл» в нашей системе – есть «обёртка» для FV – Firmware Volume.

Цитата:
2.1.3 Firmware File System
A firmware file system (FFS) describes the organization of files and (optionally) free space within
the firmware volume. ...

Platform Initialization Specification, VOLUME 3, Version 1.2, 5/13/2009

Небольшая поправка: "within" переводится как "внутри", т. е. получается, что

Цитата:
FV – Firmware Volume ... есть «обёртка» для ... FFS или дальше просто «файл» в нашей системе...

Поправьте, если не так...

И чем все закончилось? EFI все актуальней, а мануалов днем с огнем.

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
  • Разрешённые HTML-теги: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img>
  • You can use BBCode tags in the text. URLs will automatically be converted to links.

Подробнее о форматировании текста

Антибот - введите цифру.
Ленты новостей