Pull to refresh

Comments 40

В оригинале (книги) ее нет.
Теперь запускается BIOS; после инициализации и проверки оборудования BIOS необходимо найти загрузочное устройство. Порядок загрузки сохраняется в конфигурации BIOS.

А можно чуть подробнее раскрыть фразу «Теперь запускается BIOS»? Кто это делает? Кто выполняет настройку оборудования с указанными в биосе параметрами? Это делает:
— процессор биоса (есть ли такой)?
— центральный процессор выполняет код записанный в биосе?
— пресловутая Intel Management Engine?
Чтобы подробно объяснить как всё происходит придётся написать пару книг, сравнимых с тем, которую мы тут обсуждаем.

Лучше вернуться на 30 лет назад и посмотреть на то, как стартовал какой-нибудь Compaq Deskpro: включили процессор, выставили ему CS, как описано в статье и начали исполнять код. Всё.

В современном же мире у вас в компьютере десяток (а то и не один) процессоров, у каждого своя прошивка, часть загружается из основной памяти, часть нет, часть «заводится» BIOS'ом, часть, наоборот, стартует до него… в общем масса бурной деятельности, которая, для ващей убедительности ещё и нифига не документирована (то есть буквально: от Intel приходят бинарные блобы на несколько мег, про которые сказано, что их нужно в ваш BIOS внинковать и позвать в определённый момент… что точно они делают — документация умалчивает).

Так что если ваша цель — не потратить несколько лет на разборки с этими механизмами, а загрузить какую-нибудь OS… то лучше сделать вид, что мы вернулись в 1980е и у нас простой и бесхитростный 80386й «под капотом».
Так что если ваша цель — не потратить несколько лет на разборки с этими механизмами, а загрузить какую-нибудь OS… то лучше сделать вид, что мы вернулись в 1980е и у нас простой и бесхитростный 80386й «под капотом».

Лучше не надо, потому что те механизмы, которые делают современный ПК похожим на 80386 — это такое хтоническое чудовище, что с ними лучше не сталкиваться никогда. UEFI имеет в разы более простой интерфейс с прошивкой, и лучше изучить один раз код Linux EFI stub, или любого минимального EFI-загрузчика, и забыть про этот легаси-ад навсегда.
Не видел никаких «более простых интерфейсов с прошивкой», увы. Сам по себе EFI — это куча безумно сложного кода, решающего простую задачу. То, что с ним приходится общаться — как по мне, так скорее проблема, чем достоинство. И да — «хтоническое чудовище» имеет место быть, но вот только от способа загрузки это особо не зависит.

Всё отличие, в сущности — используя интерфейсы прошлого века вы общаетесь как бы с чем-то простым и бесхитростным (хотя на самом деле нет), а общаясь с EFI — вы с самого начала отказываетесь от идеи контролировать что-либо.
Мы, мне кажется, друг друга не понимаем.
"Положите загрузчик на диск по вот этому пути, сделайте в нем функцию EFIAPI efi_main(EFI_HANDLE Handle, EFI_SYSTEM_TABLE *SystemTable), которую вам прошивка и вызовет" — это вот, на мой взгляд, в разы проще спецификации multiboot, прерываний этих номерных, карты памяти e820 и прочего EBDA.
EFI — это интерфейс, а сложным он стал от того, что оборудование стало сложным, плюс вагон особенностей реализации наконец-то решили стандартизировать.
От способа загрузки чудовищность зависит напрямую, с EFI-загрузкой нам доступны все сервисы и возможности прошивки с человеко-понятном виде, с нормальными именами, со спецификацией открытой. А с легаси — кривая эмуляция на IoTrap-ах, трамплины в 16-битный режим и обратно, и прочие костыли для совместимости с седой древностью, которые теперь приходится заводить через жертвоприношения.
Если ваша ОС нормально работает с EFI-загрузкой — ей и пользуйтесь, а CSM отключайте — от него на современном железе одни проблемы.
Понимаете, есть разная «простота». «Положите загрузчик на диск» — это то, что сегодня принято называть. «простотой», когда «легковесный фреймворк» состоит из десятков тысяч файлов, в которых полностью не разбирается никто.

Я же говорю про другую простоту — когда полное описание всей системы занимает осмысленный размер и понятно из чего она состоит. Да, описание карты памяти e820 может быть и выглядит не так красиво, как «понятные» интерфейсы EFI — но разбор его можно написать даже в машинном коде, а на ассемблере — так вообще запросто. А вот чтобы взаимодействовать с EFI — вам нужна масса весьма сложного кода, который, от того, что вы его скачали с сайта и запустили «не глядя» проще не становится.

Что касается CSM — то тут согласен, но не потому что EFI прост, а BIOS сложен (всё ровно наоборот), а потому что CSM — это не BIOS! Это куча… добра, которая пытается эмулировать простую вещь (BIOS) поверх другой, ещё более сложной кучи… добра. В результате получаем чудесный гибрид, который ещё сложнее и глючнее, чем EFI, но при этом ещё и ограниченнее!
А вот чтобы взаимодействовать с EFI — вам нужна масса весьма сложного кода, который, от того, что вы его скачали с сайта и запустили «не глядя» проще не становится.

Не нужна там никакая масса кода, все взаимодействие идет через EFI_SYSTEM_TABLE, которая по факту обыкновенная таблица указателей на функции. Ничего хитрого в работе с EFI нет, и взаимодействовать с ним можно и в машинном коде, и на ассемблере, и на чем угодно. Придется узнать немного о формате PE, и соглашении о вызовах Microsoft x64, и о том, что зачем нужен ExitBootServices(), но это все значительно проще, чем BIOS Interrupt Call с его магическими номерами, магическими адресами, нереальным режимом, управлением адресной линией А20, ресетом через порт клавиатуры и ручным пробросом VGA через мосты.
Вы, мне кажется, смотрите на GNU EFI SDK или EDK2 и думаете, что они необходимы и без них с EFI нельзя договориться. Если так — вы ошибаетесь.

Мне очень нравится идея концептуальной простоты, но реализации таковой не выдерживают, к сожалению, столкновения с реальностью, начальством и отделом маркетинга, и потому либо не существуют, либо не используются. Приходится ехать на том, что есть, и на вот этом ехать с EFI значительно веселее, чем без него, на мой взгляд.
Не нужна там никакая масса кода, все взаимодействие идет через EFI_SYSTEM_TABLE
А этот EFI_SYSTEM_TABLE где висит? В воздухе? Уже для того, чтобы собрать бинарник, который EFI признает «своим» нужна масса тулов, просто в hexeditor'е вы этого не сделаете.

Ничего хитрого в работе с EFI нет, и взаимодействовать с ним можно и в машинном коде, и на ассемблере, и на чем угодно.
Нет. «На чём угодно» не получится. «Hello, world» для EFI — сложнее, чем весь BIOS первых 80386х! Во всяком случае у меня слодилось такое впечатление.

Придется узнать немного о формате PE, и соглашении о вызовах Microsoft x64, и о том, что зачем нужен ExitBootServices(), но это все значительно проще, чем BIOS Interrupt Call с его магическими номерами, магическими адресами, нереальным режимом, управлением адресной линией А20, ресетом через порт клавиатуры и ручным пробросом VGA через мосты.
Я бы не сказал, что оно проще. Скорее подходящее слово «привычнее». Тот же «ресет через порт клавиатуры» — это, я извиняюсь, пара простых команд на ассемблере. Проблемы возникли из-за того, что этот механизм не был частью официальных спецификаций и разные производители не до конца его скопировали.

Вы, мне кажется, смотрите на GNU EFI SDK или EDK2 и думаете, что они необходимы и без них с EFI нельзя договориться. Если так — вы ошибаетесь.
Было бы интересно посмотреть. Вот прямо так — как с нуля, условно говоря, в Hex Editor'е создать OS, загружающуюся в EFI-системе.

Я таких штук не видел, но если можете написать статью — буду очень благодарен.

Приходится ехать на том, что есть, и на вот этом ехать с EFI значительно веселее, чем без него, на мой взгляд.
Веселее — дооо, очень весело. Невозможность защитить систему от малвари — это ж так весело.

Эпоху первых 386х я, скажу честно, не застал. Но даже на одной из моих первых матерей, MS-6368, я мог замкнуть JBIOS1 перемычкой и знать что все эти CIHи гарантированно ничего не смогут сделать. В крайнем случае — замкнуть перемычкой JBAT1, перезагрузиться — и можно начть жизнь «с чистого листа».

Да и вообще — сколько вы знаете BIOS-систем, где очистка CMOS'а превращает систему в «кирпич»? Защита флеша есть не всегда (хотя на первых 386х никакого флеша и не было, там чтобы что-то испортить программатор нужен), но CMOS — всегда можно очищать.

А сколько вы знаете EFI-систем, которые можно превратить в «кирпич» что-то не то прописав в EFI-настройки? Ответ: гораздо больше, чем хотелось бы.
А этот EFI_SYSTEM_TABLE где висит? В воздухе? Уже для того, чтобы собрать бинарник, который EFI признает «своим» нужна масса тулов, просто в hexeditor'е вы этого не сделаете.
Висит в памяти, вывесил DxeCore, вам отдадут указательна на нее вторым параметром точки входа (т.е. в регистре EDX).

Нет. «На чём угодно» не получится. «Hello, world» для EFI — сложнее, чем весь BIOS первых 80386х! Во всяком случае у меня сложилось такое впечатление.
Это именно впечатление. «Hello World» для EFI выглядит вот так:
SystemTable->ConOut->OutputString(gST->ConOut, L"Hello World!");
Т.е. от языка нужны следующие возможности: генерация файла PE/COFF и вызов функции с MS x64 ABI.

Про проблемны реализации EFI спорить не готов, потому что выйдет очень длинно, но проблемы именно с CMOS/NVRAM — они исключительно от желания снизить стоимость решения (т.е. не ставить дорогой CMOS SRAM заметного объема) при возросшых требованиях к нему (железо усложнилось кратно).
Т.е. от языка нужны следующие возможности: генерация файла PE/COFF и вызов функции с MS x64 ABI.
Что уже сильно сложнее интерйейса BIOS. Там нужно было два байта в нужное место написать (пресловутые 55 AA) и всё — можно вызывать прерывания.

Про проблемны реализации EFI спорить не готов, потому что выйдет очень длинно, но проблемы именно с CMOS/NVRAM — они исключительно от желания снизить стоимость решения (т.е. не ставить дорогой CMOS SRAM заметного объема) при возросшых требованиях к нему (железо усложнилось кратно).
Я прекрасно понимаю почему простое и надёжное решение было заменено этой кучей… гуано: мартышек, которые могут как-то суметь в IDE вызвать функции с MS x64 ABI на рынке больше, чем людей, которые могут работать в BIOS. А уже проблемы с реализациями — они отсюда и следуют: если мы вместо нормальных разработчиков наняли мартышек, то остаётся радоваться что результирующее чудо брикает систему не всегда, а через раз.
Вызывать прерывания, это, конечно, намного проще, чем стандартные функции через стандартный же ABI…

А PE/COFF-файл можно не только самому собрать в хекс-редакторе, но и любая фигня, способная делать исполняемые файлы для Windows это умеет уже.

Стало именно проще, причем практически везде. Если вы не хотите в это верить — дело ваше, а нам тут пока ехать надо, с мартышками или нет.

BIOS умер под собственным грузом легаси и костылей, не успев за развитием железа, и то, что пришло ему на смену — лучше во всех отношениях. Если вы думаете что это заговор мартышек — я не смогу вас переубедить.
У интерфейса биоса есть только два преимущества:
1) Тонны кода, которые накопились за 30 лет
2) Полная свобода действий — хочешь юзай прерывания биоса, хочешь пиши по портам, юзай всё, что хочешь.
Есть одна только проблема — мартышек много, и они иногда под руководством немартышки пишут вполне вменяемый код, а вот под БИОС надо ещё и вменяемые знания ассемблера иметь. Теже яйца, что и «почему у нас очень часто применяют микроконтроллеры, а не плис, плис же быстрее!». Вменяемого БИОСника надо ещё найти суметь, а потом привлечь к себе жЫЫЫрными плюшками, а с EFI порог вхождения ниже, нужно только C/C++ знать и в алгоритмах более-менее разбираться. Если везде брать мега-крутых специалистов, то конечный продукт будет стоить как новенький боинг, и его просто не купят, как говорится «Пока Василий прикручивает идеальное крыло к идеальному фюзеляжу, Петя уже пролетает над ним на автобусе с прицепленой фанерой и танком, собирая бабло с пассажиров на следующую леталду»
У биосов в детстве болячек было дофига и более, для примера почитайте, что делает BIOS Patcher за авторством apple_rom и нафига он вообще его сделал. Да и во времена NVidia nForce 1/2 окирпичить материнку можно было случайным нажатием ресета.
Я перечитал два раза, и думаю, что понял наконец, о чем речь. Вам, видимо, не нравится тот факт, что EFI пытается абстрагировать сложность оборудования от пользователя, и делает это громадным количеством кода, который вам бы не хотелось запускать, а он все равно запускается и повлиять на это крайне не просто.
Альтернатив тут несколько, но выстрелить может, на мой взгляд, только одна — LinuxBoot, который сейчас развивают Google и Facebook. Там ребята выкинули из EFI весь верхний уровень (тот самый Extensible Firmware Interface) и заменили его ядром Linux, которое основную систему затем загружает kexec'ом без всяких интерфейсов, и умирает после этого практически без следа.
Идея отличная и работает замечательно, но для загрузки произвольной ОС (которой нужен ACPI, к примеру) не подходит, а если начать подводить — снова получится EFI, и далеко не факт, что во второй раз получится лучше.
Вам, видимо, не нравится тот факт, что EFI пытается абстрагировать сложность оборудования от пользователя, и делает это громадным количеством кода, который вам бы не хотелось запускать, а он все равно запускается и повлиять на это крайне не просто.
Нет, мне не нравится тот факт, что это громадное количество кода не абстрагирует сложность оборудования от пользователя.

У меня нет претензий к BIOS. У меня нет претензий к ABIOS.

В первом случае у нас есть простая «заглушка», которая работает только в реальном режиме и к OS, работающим в защищённом она отношения не имеет. Они, в общем, более-менее самостоятельно вынуждены обращаться к железу — но зато мы получаем небольшой, компактный и понятный загрузчик (хотя, конечно, появление SMM, APM и прочих чудес начинают портить картину).

Во втором случае — firmware является частью OS, драйвера сидят там и OS, соотвественно, этих драйверов не имеет. Firmware, понятно, становится сложнее, но зато OS — проще. Ok.

Но EFI?

В случае в EFI у нас есть огромная, очень сложная и наворочанная система… которая всё равно не позволяет избавиться от драйверов железа в OS… спрашивается — нифига козе баян?

Альтернатив тут несколько, но выстрелить может, на мой взгляд, только одна — LinuxBoot, который сейчас развивают Google и Facebook. Там ребята выкинули из EFI весь верхний уровень (тот самый Extensible Firmware Interface) и заменили его ядром Linux, которое основную систему затем загружает kexec'ом без всяких интерфейсов, и умирает после этого практически без следа.
Это тоже немного странное поделие (нафига вам два ядра Linux в одной системе?), но, хотя тут налицо явное дублирование бинарного кода, но, по крайней мере, исходный код не дублирован… так что это шаг в правильном направлении…

Идея отличная и работает замечательно, но для загрузки произвольной ОС (которой нужен ACPI, к примеру) не подходит, а если начать подводить — снова получится EFI, и далеко не факт, что во второй раз получится лучше.
Для загрузки произвольной OS надо бы отделить драйвера от операционки… что в Linux невозможно сделать по политическим причинам.
Баян нужен был самим авторам прошивки по большей частью, потому что эта простая заглушка стала с развитием железки под ней настолько сложной, что во времена чипсета P55 там творилась уже настолько жесткая вакханалия, что у меня нет слов описать ее. JerleShannara смог бы, я думаю, если у него настроение будет.
То, что потом этот уже имеющийся баян дали загрузчикам ОС совершенно бесплатно, т.е. даром — это хорошо, а не плохо, потому что работа не пропала зря.
На современном x86 железе я всеми ушами за EFI, вы всёравно никогда не получите полной документации, чтобы понять, что там кто делает и зачем, даже подписав тонну NDA вам не дадут посмотреть на ME/PSP, вы врядли получите документацию на Matryoshka процессоры, которые живут в чипсете. Про баян — в интернете сейчас есть куча утёкших исходников AWARD и AMI, сравните блин тот-же обработчик int 13h во времена 386/486, потом во времена Pentium3/Pentium4, а далее насладитесь реализацией AMI на более поздних чипсетах (965/P31/P45), там проще застрелиться, чем понять сотни костылей, чтобы оно могло что-то читать/писать. Как вам понравится, что Core 2 Duo с AWARD BIOS имеет около 10 костылей для работы с конкретными CD-ROMами? А теперь давайте вспомним по USB, и вот уже костылями придавило нафиг. Живой пример — IBM T61, подключаем к нему ZALMAN VE-300, и опа, если он прикидывается CD-ROM — всё грузится, если хардом — всё снова грузится, а если одновременно HDD+CD — CDROM исчезает куда-то.
Ладно, рассматривать обработчик не будем, рассмотрим простую как тапок функцию энумерации и настройки устройств на PCI шине, ой, я опять костылём по затылку получил от S3Trio. Куда ещёб залезть… о, настройка памяти, хотя нет, я пожалуй лучше порно с малолетними мёртвыми боброудавами посмотрю, оно гораздо более приемлимо даже для показа в детском саду, чем разборка кода настройки DDR2/3/4. А как эротично выглядело постоянное скаканием между 16bit/32flat/unreal/32bit protected режимами, а потом у нас появилось памяти больше, чем 4Гб, и теперь я даже не знаю, какое порно будет хуже этого.
EFI позволило выкинуть в мусорку пару товарных вагонов костылей, убрать вечное скакание по режимам, произвести унификацию загрузочного интерфейса, позволило убрать нафиг 16битный режим, который нужен был только DOSу. Да, не спорю, оно уже тоже начинает обрастать костылями, но благо тут выкатываются новые версии стандарта, где костыли медленно стряхивают. И если для поддержки нового USB контроллера ранее Intel/AMD вынуждены были писать отдельный код почти под каждого IBV, то теперь, получив унификацию у нас есть стандарт, по которому можно хоть турбовибратор засунуть в прошивку, да, как правило EFI драйвера нам дадут только базовую функциональность, но она будет выше, чем в Legacy варианте, и опятьже, они будут стандартными. Единственное место, где я не люблю EFI — это ARM, там ИМХО лучше UBOOT.
U-boot и прочие embedded device tree вещи на АРМах привели уже к тому, что у каждой собаки свой собственный загрузчик, совместимый примерно ни с чем, при этом какой-нибудь вообще пятистадийный, и у него там внутри половина EFI, кусок линукса, тут написано вендором, тут написано сообществом, тут рыбу заворачивали.
Чем быстрее они там передут хоть на какой-то интерфейс стандартный (даже пусть это будут UEFI и ACPI, фиг с ним), тем лучше будет для их популярности и применимости, на мой взгляд.
Согласен, вся разработка софта под армы начинается с «Скачайте убут вот той версии, а потом наложите вот эти патчи», но у армов гораздо более пёстрый набор железа и silicon specific кода. Если тут смогут нормально всё сделать, не дорисовав подобия PSP(от которого я в диком бешенстве), то будет очень круто.
Чуть подробнее.
Прямо от ресет-вектора выполняется так называемая прошивка или firmware (firm она потому, что это ровно между hard и soft). Выполняется она на том же центральном процессоре, что и все остальное (начинается исполнение с одного конкретного ядра, обычно нулевого, которое называют BootStrap Processor, а все остальные ядра ничего не делают, пока не получат специальное SIPI-прерывание). Код прошивки исполняется прямо из ПЗУ, подключенного к одной из простых шин — LPC/SPI/eSPI, а затем копируется в ОЗУ после того, как это самое ОЗУ удасться найти и проинициализировать (процесс этот называется тренировкой, memory training).
По сути своей, прошивка состоит из двух относительно независимых слоев, нижний из которых занимается инициализацией конкретного оборудования (процессора, памяти, шин), абсолютно необходимого для верхнего слоя и ОС, и верхнего, который реализует интерфейс к этой самой ОС, и которым ОС пользуется для своих собственных целей. Само слово BIOS (т.е. базовая система ввода-вывода) — оно именно про интерфейс (который полностью называется BIOS Interrupt Call, т.к. использует программные прерывания практически для всего), а UEFI уже и называется именно интерфейсом.
Intel Management Engine — это отдельный процессор со своей собственной прошивкой (хранящейся обычно на том же ПЗУ, что и основная), стартующий раньше центрального и исполняющий различные сервисные задачи, которые на центральном процессоре исполнять по разным причинам не удобно.
Тов. khim прав в том, что тут надо книги писать и не одну, и одной Beyond BIOS тут не обойдешься, потому что она исключительно про одну (хоть и популярную) реализацию верхнего уровня прошивок для современных архитектур.
Кроме UEFI/TianoCore можно посмотреть на coreboot/Intel FSP/AMD AGESA/U-boot (это реализации именно нижнего уровня), а для верхнего — LinuxBoot и SeaBIOS.

Вот уж точно, что все относительно. Я делаю на Си структуру данных trie и считал это низким уровнем. :-)

0x000B8000 — 0x000BFFFF — видеопамять цветного режима

Если мне не изменяет память, то эти адреса относятся к текстовому режиму.
Текстовый, 4 страницы 80x25. Память графических режимов (вернее, отображение видеопамяти в оперативку) по умолчанию с 0x000A0000, 64 Kb для реального режима (чтоб в высоких разрешениях работать, надо через int 10h переключать банки памяти). Игрался с этим еще в школе, Turbo Pascal + Assembler :)
Не факт, что на платформах начиная с «х686» (условно), вот так легко и просто выполнится первая инструкция по адресу 0xfffffff0. Даже в рамках firmware-букваря стоит заметить, что аппаратные платформы давно уже не являются классическими х86 в полном смысле этого слова.
А какие есть ещё альтернативы? Всегда после включения должен выполняться какой-то известный адрес. Как иначе? И если уж исторически он расположен по адресу 0xfffffff0 — то куда и зачем его вдруг переносить?
Если коротко — Intel ACM, который процессор еще до ресет-вектора найдет через FIT, проверит его подпись при помощи МЕ, и затем исполнит, а он уже передаст управление на legacy reset vector, или сразу на SEC core.
Нужно это для организации root-of-trust для verified boot и measured boot, загрузки микрокода из FIT, и других задач, с которыми нужно закончить до начала исполнения пользовательского кода, но которые при этом нельзя выполнить на МЕ.
Вот тут @matrosov хорошо написал про ACM и BootGuard, почитайте если интересно.
О, спасибо за статью, качественно описано.
Вам в пример AMD PSP. Запуск начинается с того, что x86 ядро сидит в reset, запускается ARM, который проверяет подпись прошивки платы, и если есть косяк, то вы вообще никогда не увидите загрузки, если у вас готовая плата, а не комплект разработчика, с процессора вообще не будет снят сигнал сброса.
Все это благополучно было отлажено еще на AMD Athlon с использованием ROM SIP. Для того, чтобы запускалась платформа, нужно было обеспечить ряд условий для готовности чипсета.
Ну а теперь оно уже навсегда и везде в AMD, увы.
Уточните пожалуйста, все высшеописаное происходит в рельном, 16-битном режиме?
Перевод в 32-битный режим будет описан в следующей статье?

Жесть, я думал, что GRUB уже в рельном режиме гурзит ядро.
Жесть, я думал, что GRUB уже в рельном режиме гурзит ядро.
Реальный режим может адресовать только 1MB, а ядро уже давно гораздо больше. Его просто некуда загружать в реальном режиме.
пардон, я имел в виду зашищенный, и 32/64 битный.

А в статье написано, что ядро само себя переводит в зашищенный режим.
QEMU получает команду использовать двоичный файл boot, который мы только что создали как образ диска. Так как двоичный файл, сгенерированный выше, удовлетворяет требованиям загрузочного сектора (начало в 0x7c00 и завершение магической последовательностью), то QEMU будет рассматривать двоичный файл как главную загрузочную запись (MBR) образа диска.

Я таки могу ошибаться, но «начало в 0x7c00» — это не требование к MBR, это адрес загрузки MBR в память. Требование к MBR — размещение в первом секторе загрузочного диска с маркером 0x55AA.
Sign up to leave a comment.

Articles