Структура упаковки 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. Также может содержать исполнительный код (модуль) или содержать под-капсулу, т.е. полная абстракция и неограниченные возможности для реализаций.
Ну, пожалуй, это только зародыш статейки, точней даже - лишь её зачатие.
Просто сложно собраться и решиться описать главные моменты работы EFI, потому просто начал "с чего-нибудь". Если кому-то нужно, кстати, разобраться с конкретным EFIBIOS - предлагайте (желательно поновей), будем разбирать на конкретном примере.
Касаемо капсулы, то весьма сложно сразу обхватить необъятное. А так, правильно, капсула задумывалась как структура для перезаписи (EFI)BIOS. Общая философия следующая - под Windows (Linux, MACOS etc) запускается специально обученная программа (гомомодифицированный внук прошивальщика), которая формирует специальную структуру в оперативной памяти (также, возможно, в специальном месте/адресах) после чего системе посылается не менее специальный сигнал рестарта. Специален он тем, что при перезагрузке не проискходит очистка памяти (как минимум области расположения капсулы). После RESET стартует BIOS (EFI), в котором до момента стандартной очистки всей памяти срабатывает специально натренированная процедура, которая перехватывает процесс работы по найденной в памяти сигнатуре капсулы. После некоторых проверок её (капсулы) целостности, а также (не)хитрой проверки на вшивость (что запущена не злобным трояном) - капсуле передаётся управление и наступает её трансмутация в микросхему BIOS. Обычно сие таинство протекает в SMM.
Однако в нашем случае капсула будет важна больше не тем, зачем она нужна, а чисто практически - как из неё получить "нормальный" EFIBIOS.
Итак, снова, здравствуйте мои много и по-разному уважаемые! Надеюсь, толпы читающих сии опусы достигают в перигее лишь моего монитора (паршивенький, честно сказать, но таки цельных 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. Обычно жмутся лишь «конечные» модули, однако секции (в плане - содержащие внутри себя другие матрёшки) тоже жмутся. Хоть, как бы особо не должны (но об этом уже было выше).
Т.е. смысл простой, типы секций могут быть (обратимся к первоисточнику):
Как видно, если тип секции равен единице, значит она пожатая. Также ещё видно, что валидные типы это 1, 2, 0x10-0x1B. Однако сразу нужно дополнить, что по всё тому же правилу военного буравчика к этому списку нужно приплюсовать 0x00, 0x1C и 0xFF.
Самые важные типы – 0x10, который обозначает, что в секции находится PE32-модуль и 0x17, который обозначает, что внутри её матрёшка раздела - FV. Тип секции 0x19 обозначает, что в ней лежат «просто данные», часто это какие-то «внешние», не используемые непосредственно EFIBIOS данные (например, нужные для прошивальщика и т.п.), а также картинки/логотипчики. В секции 0x15 лежит (если она есть, конечно) «человеческое» название модуля, т.е. просто его название. Несмотря на подобную заботу о людях в реальности BIOS-писатели её успешно не пользуются и «человеческие» названия являются исключением, а не правилом (если их не «выуживать» другими способами, как в указанных выше на картинках). Секция 0x16 пусть особо не возбуждает ваше воображение, ибо ничего особо полезного «совместимого» там нету и предназначена она для 16-битного SMM-обработчика. Модуль 0x12-того типа (то есть тип секции, который его и определяет) есть «недомодуль PE32-типа», который отличается более упрощённым заголовком и предназначен для исполняемых файлов на PEI-стадии (т.е. в проекции на BIOS – _исполняемый_ модуль для BootBlock-а).
…Так, а ведь про стадии то я и не рассказал. А где, собственно, начальник транспортного цеха?!?...
Небольшая поправка: "within" переводится как "внутри", т. е. получается, что
Поправьте, если не так...
И чем все закончилось? EFI все актуальней, а мануалов днем с огнем.
Отправить комментарий