Оригинал: https://klarasystems.com/articles/the-freebsd-boot-process/
В этой статье изучается процесс загрузки FreeBSD. Он надёжен и хорошо продуман, но слегка различается в зависимости от архитектуры системы, файловой системы (UFS или ZFS), схемы разметки диска (GPT или MBR) и интерфейса прошивки, управляющего загрузкой (UEFI или BIOS/CSM).
● BIOS
BIOS также известен как режим CSM или legacy. Это старый механизм загрузки 16-битных систем, который до сих пор поддерживается на многих компьютерах. Он подходит для загрузки FreeBSD, хотя будет чуть медленней UEFI и не поддерживает загрузку с устройств NVMe. Режим BIOS поддерживает разметки MBR и более современную GPT, а также слайсы Freebsd.
● UEFI
Все современные системы архитектуры amd64 поддерживают режим загрузки UEFI; также многие из них поддерживают устаревший режим BIOS ─ некоторые даже предлагают установить один из них основным, а второй в качестве запасного, если первый метод не сработает.
Также режим UEFI доступен на многих системах с архитектурой arm64 ─ его поддержка требуется для сертификата ServerReady ARM.
Некоторые системы arm64 и riscv/riscv64 поддерживают загрузку с помощью U-Boot вместо загрузки с помощью UEFI ─ FreeBSD поддерживает оба метода. Системы архитектуры RISC-V также можно загружать с помощью загрузчика OpenSBI, заменившего BBL(Berkley Boot Loader) из проекта FreeBSD. В данный момент команда FreeBSD работает над реализацией поддержки UEFI в системах RISC-V.
● Процесс загрузки Freebsd
Этот процесс бывает довольно сложным и включает в себя множество частей, которые сложно отличить друг от друга. Чтобы облегчить понимание, мы разбили статью на два раздела. В первом рассматривается часть загрузки, предшествующая запуску программы loader(8), которая отображает знаменитое "меню с Бисти"; второй посвящен тому, что происходит после того, как loader загрузит выбранное ядро системы.
▪ Стадии загрузки до активации загрузчика Freebsd ─ программы loader(8)
К этапу загрузки ядра Freebsd и передачи управления загрузкой процессу PID 1 ─ то есть системе init(8) ─ можно прийти разными путями в зависимости от архитектуры системы, метода загрузки, схемы разметки и файловой системы.
Прежде чем вникать в детали каждого режима загрузки, давайте схематично разобьем их на стадии:
BIOS/ MBR/UFS
+-> MBR from 'Boot Device' BIOS disk | MBR
+-> boot0 | STAGE 0
+-> boot1 | STAGE 1
+-> boot2 | STAGE 2
+-> loader | STAGE 3
+-> kernel | KERNEL
+-> init | INIT
BIOS/ MBR/ZFS
+-> MBR from 'Boot Device' BIOS disk | MBR
+-> boot0 | STAGE 0
+-> boot1 | STAGE 1
+-> zfsboot | STAGE 2
+-> zfsloader | STAGE 3
+-> kernel | KERNEL
+-> init | INIT
BIOS/ GPT/UFS
+-> GPT from 'Boot Device' BIOS disk | GPT
+-> pmbr | STAGE 0
+-> gptboot | STAGE 1 + STAGE 2
+-> loader | STAGE 3
+-> kernel | KERNEL
+-> init | INIT
BIOS/ GPT/ZFS
+-> GPT from 'Boot Device' BIOS disk | GPT
+-> pmbr | STAGE 0
+-> gptzfsboot | STAGE 1 + STAGE 2
+-> zfsloader (analogous to loader) | STAGE 3
+-> kernel | KERNEL
+-> init | INIT
UEFI/GPT/MBR/UFS/ZFS
+-> GPT/MBR from 'Boot Device' BIOS disk | GPT/MBR
+-> UEFI | STAGE 0
+-> boot1.efi (/efi/boot/boot${ARCH}.efi) | STAGE 1 + STAGE 2
+-> loader.efi | STAGE 3
+-> kernel | KERNEL
+-> init | INIT
UEFI/GPT/MBR/UFS/ZFS (13.0 and later)
+-> GPT/MBR from 'Boot Device' BIOS disk | GPT/MBR
+-> UEFI | STAGE 0
+-> loader.efi (/efi/FreeBSD/loader.efi) | STAGE 1-3
+-> kernel | KERNEL
+-> init | INIT
Переменная ARCH, в зависимости от вашей системы, может принимать одно из значений: x86 - x64 - arm - aa64.
Теперь, когда у вас есть общее представление о процессе загрузки, давайте рассмотрим каждый режим подробно.
▪ BIOS/Legacy, MBR и UFS
Использование таблицы разделов MBR и файловой системы UFS ─ самый старый метод загрузки FreeBSD. Допустим, ваше загрузочное устройство ─ диск с интерфейсом sata: /dev/ada0. На нём есть один основной раздел MBR (в терминах FreeBSD он называется слайс), который система видит как /dev/ada0s1. На этот слайс надо будет установить флаг активного (загрузочного) раздела. Внутри слайса вы увидите разделы (партиции) BSD, созданные утилитой bsdlabel(8). Корневой раздел, с которого будет производиться чтение в процессе загрузки, обозначен как /dev/ada0s1a.
Stage 0:
Из-за ограничений на размер файла загрузчика, накладываемых BIOS+MBR, загрузчик делится на стадии. BIOS загружает в память для исполнения первый сектор диска. Это называется нулевой стадией ─ stage 0. Нулевая стадия использует файл /boot/boot0, в котором записано 446 байт кода на ассемблере, а оставшиеся 66 байт (512-446) первого сектора содержат таблицу разделов (именно поэтому в ней может быть только 4 пункта) и сигнатуру. После загрузки в память программа, которая 446 байт, анализирует таблицу разделов и отображает меню загрузки на основании кода типа раздела, считанного из таблицы. Например:
F1 FreeBSD
F2 Win
Default: F1
Вместо файла /boot/boot0 можно использовать /boot/boot0sio ─ в этом случае меню также будет выведено на последовательную консоль. Но если на вашей машине нет последовательной консоли, она может зависнуть в процессе загрузки, поэтому данный файл не используется по умолчанию.
Stage 1:
Следующий этап загрузки ─ считывание и выполнение файла /boot/boot1 с первого сектора выбранного первичного раздела (слайса) ─ это называется Stage 1. Как правило, на этом этапе выполняются два файла: /boot/boot1 (512 байт) и /boot/boot2 (7680 байт). Файл /boot/boot2 занимает первые шестнадцать секторов по 512 байт, которые UFS не использует.
Главная задача boot1 ─ загрузка следующей стадии загрузчика, которая имеет более сложный код, чем первая стадия. Этот код загружает сервер BTX, после чего тот загружает свой клиент boot2, а затем ещё один клиент под названием loader. После этого мы переходим на вторую стадию.
Stage 2:
boot 2 задаёт и инициализирует важную структуру данных bootinfo и передаёт её загрузчику, а затем ─ ядру FreeBSD, но об этом позже. boot 2 ─ это маленькая программа, которая имеет базовую информацию о файловой системе UFS, достаточную, чтобы найти файлы /boot/loader и /boot/kernel ─ учитывая объём кода (7680 байт), это весьма впечатляет.
Stage 3:
Запускается loader, который ищет ядро и загружает его. Как уже было сказано, loader также является клиентом BTX. Считываются и загружаются конфигурационные файлы /boot/loader.rc и /boot/loader.conf. Основная задача программы loader - загрузка ядра FreeBSD; loader позволяет выбрать одно из нескольких ядер. После загрузки ядра в память начинается его инициализация.
▪ BIOS/Legacy, MBR и ZFS
Схема загрузки ZFS с раздела MBR слегка отличается. На нулевой стадии (Stage 0) boot0 загружает другой файл boot1. Затем boot1 находит файл zfsboot ─ аналог boot2 ─ размером 64 КiB по смещению 1 Mib на разделе ZFS в предопределенном пустом месте файловой системы.
Это означает, что на этапах Stage 1 и Stage 2 соответственно код zfsboot распознает файловую систему ZFS и загружает zfsloader. zfsloader выводит интерактивное меню, аналогичное тому, которое выдает loader для системы UFS. Точно так же, как и loader, zfsloader после этого загружает ядро и остальную часть системы FreeBSD.
Этап, на котором zfsloader уже загружен и работает ─ это Stage 3. На этой стадии можно выбрать, какой ZFS Boot Environment загружать.
▪ BIOS/Legacy, GPT и UFS
Теперь давайте рассмотрим чуть более современный процесс загрузки с использованием таблицы разделов GPT, но в режиме загрузки BIOS (Legacy, CSM). Хотя диск размечен с использованием таблицы GPT, он все равно содержит защитную загрузочную запись ─ Protective MBR или, сокращенно, pmbr. Она нужна для того, чтобы операционные системы, не поддерживающие схему GPT, не определяли диск как неразмеченный.
pmbr загружает FreeBSD тем же способом, что и MBR ─ считывается один сектор размером 512 байт и выполняется код из него. Это можно назвать нулевой стадией ─ Stage 0.
Специальный загрузочный раздел GPT (его тип freebsd-boot) содержит код других стадий загрузчика: gptldr ─ аналог boot1 ─ и gptboot вместо boot2. Это будут соответственно Stage 1 и Stage 2.
pmbr находит этот загрузочный раздел и выполняет код, содержащийся в нём. Для файловой системы UFS используется файл gptboot. Как и раньше, первый этап (Stage 1) представляет собой программу на ассемблере размером 512 байт, которая только загружает и выполняет Stage 2. gptboot содержит код, который больше по размеру и позволяет обращаться к UFS, а также (опционально) понимает шифрование GELI, Этот код умеет находить загрузчик на разделе с файловой системой UFS и запускать его.
На стадии Stage 3 loader загружен и работает, можно выбрать, какое ядро загружать.
▪ BIOS/Legacy, GPT и ZFS
Загрузка с GPT и файловой системой ZFS во многом похожа на схему с GPT и UFS2. Сначала pmbr загружает систему FreeBSD, выполняя код из первого сектора размером 512 байт ─ как и в случае MBR. Этот этап можно назвать Stage 0.
Специальный загрузочный раздел GPT (тип freebsd-boot) содержит код других стадий загрузчика: gptldr ─ аналог boot1 ─ и zfsboot вместо boot2. Это Stage 1 и Stage 2.
pmbr считывает и выполняет код из этого раздела. Для ФС ZFS используется файл gptzfsboot, который содержит информацию о ZFS и ─ опционально ─ о GELI и может расшифровать, загрузить в память и запустить zfsloader из набора данных ZFS, созданного по умолчанию.
Когда zfsloader загружен и работает, начинается Stage 3 и можно выбрать, какую среду загрузки и какое ядро загружать.
▪ UEFI, GPT/MBR и UFS/ZFS
А теперь поговорим о "новейшем" способе загрузки FreeBSD ─ с помощью Unified Extensible Firmware Interface, (UEFI). UEFI быстрее, чем метод BIOS, поскольку переходит в 64-разрядный режим, как только это становится возможным.
Немного предыстории: первой реализацией интерфейса был Intel EFI для архитектуры Itanium (IA64). Затем эту разработку "унифицировали" и перенесли на другие архитектуры ─ отсюда и "Unified" в начале названия. Разумеется, это очень сокращённая версия истории прошивки: кроличья нора значительно глубже, ─ но в данном случае этого достаточно.
FreeBSD прекрасно поддерживает режим загрузки UEFI. Разумеется, как и в случае других способов загрузки, процесс делится на стадии. Эти стадии практически не зависят от таблицы разделов на диске ─ GPT или MBR.
На этапе Stage 0 после включения питания запускается интерфейс прошивки UEFI и ищет загрузчик операционной системы на разделе EFI File System Partition (ESP), отформатированном в FAT32. UUID системного раздела C12A7328-F81F-11D2-BA4B-00A0C93EC93B.
В зависимости от архитектуры файл загрузчика может называться следующим образом:
ARCH WIDE PATH
i386 32bit /efi/boot/bootx86.efi
amd64 64bit /efi/boot/bootx64.efi
arm 32bit /efi/boot/bootarm.efi
arm64 64bit /efi/boot/bootaa64.efi
По умолчанию во FreeBSD файл boot1.efi установлен под именем bootx64.efi. Его исполнение означает, что мы находимся на этапе Stage 1 или Stage 2.
Для определения корневой файловой системы, с которой будет загружаться FreeBSD, файл boot1.efi использует такую последовательность:
- Поиск загрузочных пулов для ZFS
- Поиск загрузочных разделов для UFS
Раздел распознаётся как загрузочный, если boot1.efi может загрузить с него loader.efi. Если на одном устройстве есть как раздел UFS, так и ZFS, то предпочтение отдаётся ZFS. После того как UEFI исполнит код файла bootx64.efi (который на самом деле boot1.efi), начинается этап Stage 3 и загружается loader.efi, который позволяет выбрать среду загрузки и ядро.
После того, как среда загрузки и ядро будут выбраны, loader.efi загрузит ядро FreeBSD.
● Ядро и многопользовательский режим
Прежде чем загрузить выбранное ядро, loader считывает и загружает параметры из файла /boot/device.hints. После выбора ядра и/или среды загрузки loader запускает ядро, которое начинает проверку наличия и инициализацию устройств.
Затем ядро передаёт управление процессу /sbin/init (PID 1), который монтирует файловые системы. init(8) также занимается другими вещами ─ такими как виртуальные консоли, найденные в файле ttys(5), ─ и запускает процессы getty(8), которые, в свою очередь, инициализируют команды login(1).
Закончив с этим, init(8) продолжает процесс загрузки в многопользовательский режим, после чего начинает конфигурацию системных служб FreeBSD ─ rc(8). Параметры системных служб по умолчанию считываются из файла /etc/defaults/rc.conf, а специфичные для системы данные ─ из файла /etc/rc.conf.
Затем монтируются файловые системы из /etc/fstab и выдаются адреса сетевым интерфейсам. И наконец запускаются сервисы и демоны с помощью стартовых скриптов из файлов /etc/rc.d и /usr/local/etc/rc.d. rcorder(8) гарантирует правильный порядок их запуска.
● nextboot(8)
Во FreeBSD есть утилита nextboot(8), которая позволяет загрузить определенное ядро, указав флаги к нему, только один раз ─ при следующей загрузке.
Это достигается тем, что loader(8) считывает информацию о ядре из файла /boot/nextboot.conf при его наличии. При перезагрузке машины nextboot.conf автоматически удаляется и система возвращается к предыдущей "постоянной" конфигурации.
Для файловых систем ZFS nextboot хранит метаданные в специально отведенном поле метки пула ZFS, поскольку загрузчик более ранней стадии не может записывать в сам пул. Метаданные nextboot стираются сразу после считывания и поэтому влияют только на один процесс загрузки после их записи.
Чтобы один раз загрузить ядро FreeBSD под названием CUSTOM, введите следующую команду:
# nextboot -k CUSTOM
Если надо загрузить тестовое ядро TEST в однопользовательском режиме, введите команду:
# nextboot -o "-s" -k TEST
Следующая команда удаляет все существующие конфигурации nextboot(8):
# nextboot -D
● Флаги загрузки bootme/bootonce/bootfailed
Во FreeBSD существуют дополнительные флаги загрузки: bootme, bootonce, и bootfailed. Флаг bootme указывает, с какого раздела должна производиться загрузка. Флаг bootonce используется вместе с bootme, чтобы загрузиться с указанного раздела один раз и затем вернуться к обычной конфигурации. Это достигается тем, что loader(8) удаляет флаги. Последний флаг ─ bootfailed ─ указывает, что с данного раздела система не смогла загрузиться. Чтобы управлять этими флагами, воспользуйтесь утилитой gpart(8). Например:
# gpart set -a bootme -i 2 ada0
Эти флаги особенно полезны, если у вас более одного корневого раздела UFS с разными версиями FreeBSD: можно установить флаг bootme, чтобы загружаться с другого раздела.
● Примеры управления процессом загрузки с помощью команд
С помощью следующих команд можно изменить или обновить код загрузчика в MBR:
# fdisk -B
# fdisk -B -b /boot/boot0 ada0
# boot0cfg -B
# boot0cfg -s 1 -b /boot/boot0 ada0
Процесс установки zfsboot на слайс MBR:
# gpart create -s mbr ada0
# gpart add -t freebsd ada0
# gpart bootcode -b /boot/boot0 ada0
# gpart set -a active -i 1 ada0
# dd if=/dev/zero of=/dev/ada0s1 count=2
# dd if=/boot/zfsboot of=/dev/ada0s1 count=1
# dd if=/boot/zfsboot of=/dev/ada0s1 iseek=1 oseek=1024
Процесс установки загрузчика для GPT:
UFS
# gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 ada0
ZFS
# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada0
Создание раздела UEFI ESP вручную:
# gpart add -a 4K -t efi -s 200M ada0
# newfs_msdos /dev/ada0s1
# mount_msdosfs /dev/ada0s1 /mnt
# mkdir -p /mnt/efi/boot
# cp /boot/boot1.efi /mnt/efi/boot/bootx64.efi
● Дополнительные источники:
Вот интересные ресурсы FreeBSD, посвященные процессу загрузки, которые также могут вам пригодиться:
▪ FreeBSD Architecture Handbook - Chapter 1 - Bootstrapping and Kernel Initialization
▪ FreeBSD Handbook - Chapter 13 - FreeBSD Booting Process
▪ boot(8)
▪ boot.config(5)
▪ boot0cfg(8)
▪ boot1.efi(8)
▪ efibootmgr(8)
▪ efivar(8)
▪ fdisk(8)
▪ gptboot(8)
▪ gptzfsboot(8)
▪ zfsboot(8)
▪ zfsbootcfg(8)
▪ loader(8)
▪ loader.efi(8)
▪ nextboot(8)
▪ fastboot(8)
No comments:
Post a Comment