Автор: apple_rom , 8 июля 2008
Содержимое данного поля является приватным и не предназначено для показа.

BBCode

  • HTML-теги не обрабатываются и показываются как обычный текст
  • You may use the following BBCode tags:
    • [align]
    • [b]
    • [code]
    • [color]
    • [font]
    • [hr]
    • [i]
    • [img]
    • [list]
    • [quote]
    • [s]
    • [size]
    • [spoiler]
    • [sub]
    • [sup]
    • [table]
    • [u]
    • [url]
  • Адреса веб-страниц и email-адреса преобразовываются в ссылки автоматически.

<font size="1">Отличная статейка, и хорошее продолжение 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. Также может содержать исполнительный код (модуль) или содержать под-капсулу, т.е. полная абстракция и неограниченные возможности для реализаций.

</font>

apple_rom

17 лет 7 месяцев назад

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

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

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

apple_rom

17 лет 7 месяцев назад

Итак, снова, здравствуйте мои много и по-разному уважаемые! Надеюсь, толпы читающих сии опусы достигают в перигее лишь моего монитора (паршивенький, честно сказать, но таки цельных 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. ...


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

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


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

Phoentel (не проверено)

15 лет 4 месяца назад

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

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

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

Определить капсулу просто – в самом её начале будет (самые первые 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-ки, то и механизмы их обработки/подгрузки/защиты – схожие. Но это уже совсем другая история…