Перейти к содержимому

Kernel boot что это

  • автор:

Kernel parameters (Русский)

Состояние перевода: На этой странице представлен перевод статьи Kernel parameters. Дата последней синхронизации: 2 июля 2022. Вы можете помочь синхронизировать перевод, если в английской версии произошли изменения.

Существует три способа передачи параметров ядру и контроля над ним:

  1. При сборке ядра — через файл config . Полная информация Ядро#Компиляция.
  2. При запуске ядра — используя параметры командной строки (обычно через загрузчик).
  3. На этапе выполнения — через файлы в /proc/sys/ (смотрите sysctl) и /sys/ ).

Настраиваемые в этих способах параметры различаются по доступности, имени и методу, в котором они указаны. Эта страница объясняет только второй метод (параметры командной строки ядра) и показывает список наиболее часто используемых параметров ядра в Arch Linux.

Большинство параметров связаны с подсистемами и работают, только если ядро собрано с этими подсистемами. Они также зависят от наличия оборудования, с которым они связаны.

Параметры, задаваемые в командной строке, имеют формат параметр или параметр=значение .

Примечание: Все параметры чувствительны к регистру.

Совет: Параметры для загружаемых модулей также можно задать через файлы .conf в каталоге /etc/modprobe.d/ . Смотрите Модуль ядра#С помощью файлов в /etc/modprobe.d/.

Настройка

  • Посмотреть параметры, с которыми сейчас загружена система, и проверить, применились ли ваши изменения, можно с помощью команды cat /proc/cmdline .
  • Установочный носитель Arch Linux использует Syslinux на BIOS-системах и GRUB на UEFI.

Параметры ядра можно установить либо временно, отредактировав загрузочную запись в меню загрузчика, либо навсегда, изменив файл конфигурации загрузчика.

Примеры ниже демонстрируют, как добавить параметры quiet и splash в Syslinux, systemd-boot, GRUB, GRUB Legacy, LILO и rEFInd.

Syslinux

  • После появления меню нажмите Tab и добавьте их в конце строки:
linux /boot/vmlinuz-linux root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw initrd=/boot/initramfs-linux.img quiet splash
  • Для сохранения изменений между перезагрузками измените файл /boot/syslinux/syslinux.cfg и добавьте их в строку APPEND :
APPEND root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw quiet splash

Полную информацию по настройке можно прочитать в статье Syslinux.

systemd-boot

  • После появления меню нажмите e и добавьте параметры в конце строки:
initrd=\initramfs-linux.img root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw quiet splash

Нажмите Enter для загрузки с этими параметрами.

  • Если значение таймаута меню не установлено, во время загрузки зажмите пробел, чтобы меню systemd-boot появилось.
  • Если вы не можете изменить параметры из меню загрузки, может понадобиться изменить файл /boot/loader/loader.conf и добавить editor 1 чтобы разрешить редактирование.
  • Для сохранения изменений между перезагрузками измените файл /boot/loader/entries/arch.conf (предполагается, что у вас настроен системный раздел EFI) и добавьте их в строку options :
options root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw quiet splash

Полную информацию по настройке можно прочитать в статье systemd-boot.

GRUB

  • Нажмите e в момент показа загрузочного меню и добавьте в строку, содержащую linux :
linux /boot/vmlinuz-linux root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw quiet splash
  • Для сохранения изменений между перезагрузками, если вы не используете grub-mkconfig, можно изменить эту же строку напрямую в файле /boot/grub/grub.cfg , а для grub-mkconfig измените файл /etc/default/grub и добавьте параметры внутри кавычек в строке GRUB_CMDLINE_LINUX_DEFAULT :
GRUB_CMDLINE_LINUX_DEFAULT

И затем сгенерируйте новый grub.cfg :

# grub-mkconfig -o /boot/grub/grub.cfg

Полную информацию по настройке можно прочитать в статье GRUB (Русский).

GRUB Legacy

  • Нажмите e в момент показа загрузочного меню и добавьте в строку, содержащую kernel :
kernel /boot/vmlinuz-linux root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw quiet splash
  • Для сохранения изменений между перезагрузками измените файл /boot/grub/menu.lst и добавьте их в строку kernel аналогично примеру выше.

Полную информацию по настройке можно прочитать в статье GRUB Legacy (Русский).

LILO

  • Добавьте в /etc/lilo.conf :
image=/boot/vmlinuz-linux . quiet splash

Полную информацию по настройке можно прочитать в статье LILO.

rEFInd

  • На нужном пукте меню нажмите + , F2 или Insert и нажмите ещё раз в нужном подменю. Добавьте параметры ядра в конце строки:
root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw initrd=\boot\initramfs-linux.img quiet splash
  • Для сохранения изменений между перезагрузками измените файл /boot/refind_linux.conf и добавьте их внутри кавычек во всех нужных строках, например
"Boot using default options" "root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw quiet splash"
  • Если вы отключили автоматическое определение ОС в rEFInd и вместо этого определяете разделы ОС в esp/EFI/refind/refind.conf для загрузки своих ОС, вы можете отредактировать его следующим образом:
menuentry "Arch Linux" < . options "root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw quiet splash" . >

Полную информацию по настройке можно прочитать в статье rEFInd (Русский)

EFISTUB

dracut

dracut может встраивать параметры ядра в initramfs, что позволяет исключить их из конфигурации загрузчика. Смотрите dracut#Kernel command line options.

Взлом cmdline

Даже без доступа к загрузчику можно изменить параметры ядра, чтобы включить отладку (если у вас есть root-доступ). Это можно сделать, перезаписав /proc/cmdline , в котором хранятся параметры ядра. Однако /proc/cmdline недоступен для записи даже для root, но это можно обойти с помощью bind mount.

Сначала создайте файл, содержащий желаемые параметры ядра

/root/cmdline
root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 ro console=tty1 logo.nologo debug

Затем сделайте bind mount, перекрыв старый файл с параметрами новым:

# mount -n --bind -o ro /root/cmdline /proc/cmdline

Параметр -n пропускает добавление записи в /etc/mtab , поэтому он будет работать, даже если корневая ФС примонтирована только для чтения. Вы можете выполнить cat /proc/cmdline , чтобы убедиться в успешности изменений.

Список параметров

Список не исчерпывающий. Полный список можно посмотреть в документации к ядру.

Параметр Описание
init Запустить указанный исполняемый файл вместо /sbin/init в качестве процесса init. Пакет systemd-sysvcompat создаёт символическую ссылку /sbin/init , указывающую на /usr/lib/systemd/systemd , для использования systemd. Укажите /bin/sh для загрузки в командную оболочку.
initrd Путь к образу начальной файловой системы (initramfs). Для загрузчиков UEFI и EFISTUB необходимо использовать обратный слэш ( \ ) в качестве разделителей в пути.
cryptdevice Расположение раздела, зашифрованного с dm-crypt, плюс имя device mapper.
debug Включить отладку ядра (уровень журнала событий).
lsm Установить порядок инициализации модулей безопасности Linux, используется для включения AppArmor, SELinux или TOMOYO.
maxcpus Максимальное количество процессоров, которые ядро SMP будет задействовать во время загрузки.
mem Принудительное использование определённого количества используемой памяти.
netdev Параметры сетевых устройств.
nomodeset Отключить Kernel mode setting.
panic Время до автоматической перезагрузки при панике ядра.
resume Указать устройство подкачки, которое будет использоваться при выходе из гибернации.
ro При загрузке монтировать корневую ФС только в режиме чтения (используется по умолчанию 1 ).
root Корневая файловая система. Смотрите список поддерживаемых форматов имён устройств в файле init/do_mounts.c. Имейте в виду, что initramfs с udev поддерживает больше форматов имён.
rootflags Параметры монтирования корневой ФС. Полезно для настройки параметров, которые нельзя применить при перемонтировании (например, с помощью systemd-remount-fs.service(8) ). Например, опция discard для XFS.
rw При загрузке монтировать корневую ФС в режиме чтения/записи.
systemd.unit Загрузка в указанную цель.
video Переопределить стандартные настройки фреймбуффера.

1 mkinitcpio использует ro по умолчанию, если загрузчик не устанавливает ни rw , ни ro . Загрузчики могут устанавливать используемое значение, например, GRUB по умолчанию использует rw (смотрите FS#36275).

Смотрите также

  • Документация ядра: «The kernel’s command-line parameters»
  • Power saving#Kernel parameters
  • List of kernel parameters with further explanation and grouped by similar options

Retrieved from «https://wiki.archlinux.org/index.php?title=Kernel_parameters_(Русский)&oldid=749647»

Глава 4. Ядро

< Прим. пер.: если вас интересует процесс сборки самого ядра и связанных с нею подробностей, рекомендуем вам ознакомиться с нашим переводом книги Программирование ядра Linux Кайваня Н Биллимория, изданной Packt Publishing в марте 2021. >

Загрузка своего ядра в оперативную память

Это занимательная глава. До сих пор мы наблюдали, что вплоть до данного этапа GRUB 2 полностью контролировал нашу процедуру запуска. Теперь он обязан передать управление своему ядру. В этой главе мы увидим как и куда наш начальный загрузчик загружает своё ядро. Иными словами, как извлекается само ядро? Далее мы рассмотрим относящиеся к запуску задач успешно выполняемых соответствующим ядром Linux и, в конце, как это ядро запускает systemd .

Тот исходный код ядра, который применяется в этой главе это версия kernel-5.4.4 . Когда я писал эту книгу это был самый последний код из доступных; отсылаем к https://www.kernel.org/. Исключительным ресурсом по этой теме является Inside Linux, написанная 0xAX. Я почерпнул многое из неё, и я уверен что вы также сделаете это.

Для передачи управления своему ядру нашему начальному загрузчику приходится добиться двую главных моментов.

  • Загрузить это ядро в оперативную память
  • Установить некие из имеющихся полей этого ядра согласно протоколу запуска.

Полный протокол доступен по https://www.kernel.org/doc/Documentation/x86/boot.txt. Первоначальный протокол запуска был определён никем иным как Линусом Торвальдсом.

 ~ ~ | Protected-mode kernel | 100000 +-------------------------------+ | I/O memory hole | 0A0000 +-------------------------------+ | Reserved for BIOS | Остаётся как можно дольше неиспользованным ~ ~ | Command line | (Может также быть ниже отметки X+10000) X+10000 +-------------------------------+ | Stack/heap | For use by the kernel real-mode code. X+08000 +-------------------------------+ | Kernel setup | Код ядра реального режима. | Kernel boot sector | Наследуемый загрузочный сектор ядра. X +-------------------------------+ | Boot loader |  

Согласно установленному протоколу запуска именно начальный загрузчик обязан передать или установить некоторые поля в заголовке своего ядра. Этими полями являются название корневого устройства, параметры монтирования, такие как ro или rw , название initramfs, его размер и т.п. Эти же поля имеют название параметров командной строки ядра и мы уже знаем, что эти параметры командной строки ядра передаются GRUB/ соответствующим начальным загрузчиком в его ядро.

GRUB не будет загружать своё ядро ( /boot/vmlinuz ) в любом случайно выбранном месте; оно всегда будет загружаться в неком конкретном месте. Значение конкретного места будет меняться для каждого дистрибутива Linux и используемой вами версии для установленной архитектуры ЦПУ в вашей системе. vmlinuz является неким архивным файлом и этот архив составлен из трёх частей.

 Vmlinuz (bZimage) = Header + kernel setup code + vmlinux (actual compressed kernel) (part-1) (part-2) (part-3) 

После загрузки ядра в память

Теперь нам необходимо представить, что GRUB 2 загрузил своё ядро в оперативную память в особом месте. Вот шаги самого начального уровня, выполняемые архивным файлом vmlinuz сразу после его загрузки в память.

  1. Как только наш начальный загрузчик загружает необходимое ядро в оперативную память в конкретное место, запускается исполняемый файл, сделанный из arch/x86/boot/header.S .
  2. Путаница случается когда vmlinuz является неким архивом, а наш начальный загрузчик ещё пока не распаковал его. Наш начальный загрузчик только загрузил это ядро в конкретное место. Тогда как же этот код внутри файла своего архива vmlinuz способен выполняться?
  3. Сначала мы рассмотрим короткий ответ, а длинный ответ мы рассмотрим в своём разделе Что распаковывает vmlinuz? в этой главе. Итак, короткий ответ состоит в исполняемом файле, сделанном из файла /x86/boot/header.S не является таким архивом; вместо этого он выступает частью заголовка который выполняет задачу kernel_setup . Этот заголовок пребывает вне архива.
 Vmlinuz (bZimage) = Header + kernel setup code + vmlinux (actual compressed kernel) --->Outside of archiveInside archiveheader.s file is here 
  1. Налаживает регистры своего сегмента
  2. Устанавливает стек и BSS

В каждой главе некая блок- схема снабжает нас чётким пониманием относительно изученного нами и, с точки зрения запуска,чего мы достигли. Рисунок 4-1 показывает самое начало той блок-схемы, которую мы сконструируем в этой главе по мере её развития. Она отображает те действия, которые выполняются кодом kernel_setup из header.s ..

Рисунок 4-1

Предпринимаемые kernel_setup шаги

 Vmlinuz (bZimage) = Header + kernel setup code + vmlinux (actual compressed kernel) --->Outside of archiveInside archivemain.c file is here 134 void main(void) 135 < 136 /* First, copy the boot header into the "zeropage" */ 137 copy_boot_params(); 138 139 /* Initialize the early-boot console */ 140 console_init(); 141 if (cmdline_find_option_bool("debug")) 142 puts("early console in setup code\n"); 143 144 /* End of heap check */ 145 init_heap(); 146 147 /* Make sure we have all the proper CPU support */ 148 if (validate_cpu()) < 149 puts("Unable to boot - please use a kernel appropriate " 150 "for your CPU.\n"); 151 die(); 152 >153 154 /* Tell the BIOS what CPU mode we intend to run in. */ 155 set_bios_mode(); 156 157 /* Detect memory layout */ 158 detect_memory(); 159 160 /* Set keyboard repeat rate (why?) and query the lock flags */ 161 keyboard_init(); 162 163 /* Query Intel SpeedStep (IST) information */ 164 query_ist(); 165 166 /* Query APM information */ 167 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) 168 query_apm_bios(); 169 #endif 170 171 /* Query EDD information */ 172 #if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE) 173 query_edd(); 174 #endif 175 176 /* Set the video mode */ 177 set_video(); 178 179 /* Do the last things and invoke protected mode */ 180 go_to_protected_mode(); 181 > 

Как вы можете видеть, исходный код main.c отвечает за следующее:

  1. Он копирует установленные параметры запуска (параметры командной строки своего ядра) из своего начального загрузчика. Функция copy_boot_params будет применена для копирования следующих параметров запуска, передаваемых его начальным загрузчиком:
 vfs.zfs.vdev.cache.size=debug, earlyprintk, ro, root, ramdisk_image, ramdisk_size и т.п. 
 [ 0.000000] BIOS-provided physical RAM map: [ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x0000000000057fff] usable [ 0.000000] BIOS-e820: [mem 0x0000000000058000-0x0000000000058fff] reserved [ 0.000000] BIOS-e820: [mem 0x0000000000059000-0x000000000009cfff] usable [ 0.000000] BIOS-e820: [mem 0x000000000009d000-0x00000000000fffff] reserved [ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000007e5f7fff] usable [ 0.000000] BIOS-e820: [mem 0x000000007e5f8000-0x000000007e5f8fff] ACPI NVS [ 0.000000] BIOS-e820: [mem 0x000000007e5f9000-0x000000007e5f9fff] reserved [ 0.000000] BIOS-e820: [mem 0x000000007e5fa000-0x0000000087f62fff] usable [ 0.000000] BIOS-e820: [mem 0x0000000087f63000-0x000000008952bfff] reserved [ 0.000000] BIOS-e820: [mem 0x000000008952c000-0x0000000089599fff] ACPI NVS [ 0.000000] BIOS-e820: [mem 0x000000008959a000-0x00000000895fefff] ACPI data [ 0.000000] BIOS-e820: [mem 0x00000000895ff000-0x00000000895fffff] usable [ 0.000000] BIOS-e820: [mem 0x0000000089600000-0x000000008f7fffff] reserved [ 0.000000] BIOS-e820: [mem 0x00000000f0000000-0x00000000f7ffffff] reserved [ 0.000000] BIOS-e820: [mem 0x00000000fe010000-0x00000000fe010fff] reserved [ 0.000000] BIOS-e820: [mem 0x0000000100000000-0x000000086e7fffff] usable 

Рисунок 4-2

Наша блок- схема

Защищённый режим

Вплоть до этого момента мы работали в реальном режиме, который обладает ограничениями в адресации 20 битами, так как он не способен выполнять доступ к памяти свыше МБ. При помощи функции go_to_protected_mode() наше ядро переключило свой ЦПУ с реального режима на его защищённый режим. Защищённый режим обладает ограничением в адресации в 32 бита, а потому его ЦПУ способен выполнять доступ вплоть до 4 ГБ памяти. Проще говоря, в реальном режиме будут исполняться лишь те программы, которые обладают 16- битным наборов инструкций, к примеру, сам BIOS. В защищённом режиме будут запускаться только 32- битные программы. В защищённом режиме имеющееся ядро выполняет некоторые относящиеся к аппаратным средствам задачи и далее запускаем ЦПУ в длинном режиме.

Обратите,пожалуйста внимание, что эта книга следует архитектуре x86 Intel и обсуждения, относящиеся к реальному, защищённому и длинному режимам основаны на 64- битной архитектуре x86 Intel.

Длинный режим

Длинный режим не накладывает никаких ограничений на свой ЦПУ. Он способен применять всю установленную память. Помещение своег ЦПУ в длинный режим будет достигаться файлом head_64.S из arch/x86/boot/compressed/head_64.S . Он отвечает за следующее:

  1. Подготовку к длинному режиму означает, что он проверяет поддеривается ли длинный режим или нет.
  2. Вход вдлинный режим.
  3. Распаковку своего ядра.

Приводимые ниже функции вызываются из файла ассемблера head_64.S :

 $ cat arch/x86/boot/compressed/head_64.S | grep -i call call 1f call verify_cpu call get_sev_encryption_bit call 1f call 1f call .Ladjust_got * this function call. call paging_prepare * this function call. call cleanup_trampoline call 1f call .Ladjust_got call 1f * Relocate efi_config->call(). call make_boot_params call 1f * Relocate efi_config->call(). call efi_main call extract_kernel /* returns kernel location in %rax */ .quad efi_call 

verify_cpu

Проверяет наличие длинного режима ЦПУ

make_boot_params

Заботится о передаваемых начальным загрузчиком параметров времени запуска

efi_main

Относящееся к встроенному ПО наполнение

extract_kernel

Эта функция определена в arch/x86/boot/compressed_misc.c

Именно эта функция распаковывает vmlinux из vmlinuz

Для лучшего понимания обратитесь, пожалуйста к показанной на Рисунке 4-3 блок- схеме.

Рисунок 4-3

Наша обновлённая блок- схема

Постойте, однако: если наше ядро ещё пока не распаковано, тогда как мы продолжаем выполнение в данный момент? Тут настаёт время нашего длинного ответа.

Что распаковывает vmlinuz?

К данному моменту мы осознали, что именно GRUB загрузил необходимое ядро в память, но в то же самое время, мы отметили, что наш образ vmlinuz содержится в неком архиве. Итак, что же распаковывает этот образ? Делает ли это GRUB?

Нет, это не GRUB. Вместо этого именно само ядро распаковывает себя. Да, я сказал что именно само ядро распаковывает это ядро. Возможно, vmlinuz единственный в мире файл операционной системы, который сам извлекает себя. Но как это возможно распаковывать себя самостоятельно? Чтобы понять это давайте для начала разберёмся с vmlinuz .

" vm " из vmlinuz это сокращение для "virtual memory". на самых ранних стадиях разработки Linux само понятие виртуальной памяти ещё не было проработано, а потому когда оно было добавлено эти символы " vm " и были добавлены к общему названию ядра Linux. Символ " z " это сокращение для ziiped файла.

 $ file vmlinuz-5.0.9-301.fc30.x86_64 vmlinuz-5.0.9-301.fc30.x86_64: Linux kernel x86 boot executable bzImage, version 5.0.9-301.fc30.x86_64 (mockbuild@bkernel04.phx2.fedoraproject.org) #1 SMP Tue Apr 23 23:57:35 U, RO-rootFS, swap_dev 0x8, Normal VGA 

Как вы можете видеть vmlinuz это bzImage ( bzImage сокращение для "big zimage"). vmlinuz это сжатый файл реального исполняемого файла vmlinuz . Вы не сможете распаковать этот файл при помощи gunzip/bunzip или даже tar . Самым простым способом распаковки vmlinuz является использование файла сценария extract-vmlinuz , предоставляемого пакетом kernel-devel (в случае Fedora). Этот файл будет представлен в /usr/src/kernels//scripts/extract-vmlinux .

 # ./extract-vmlinux /boot/vmlinuz-5.3.7-301.fc31.x86_64 >gt; /boot/temp/vmlinux # file /boot/temp/* /boot/temp/vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=ec96b29d8e4079950644230c0b7868942bb70366, stripped 

Существуют разнообразные способы раскрытия файлов ядра vmlinux и vmlinuz .

 $ xxd vmlinux | less $ objdump vmlinux | less $ objdump vmlinux -D | less $ hexdump vmlinux | less $ od vmlinux | less 

Мы воспользуемся командой od с некими переключателями для открытия своего файла vmlinuz .

 $ od -A d -t x1 vmlinuz-5.0.9-301.fc30.x86_64 | less 0000000 4d 5a ea 07 00 c0 07 8c c8 8e d8 8e c0 8e d0 31 0000016 e4 fb fc be 40 00 ac 20 c0 74 09 b4 0e bb 07 00 0000032 cd 10 eb f2 31 c0 cd 16 cd 19 ea f0 ff 00 f0 00 0000048 00 00 00 00 00 00 00 00 00 00 00 00 82 00 00 00 0000064 55 73 65 20 61 20 62 6f 6f 74 20 6c 6f 61 64 65 0000080 72 2e 0d 0a 0a 52 65 6d 6f 76 65 20 64 69 73 6b 0000096 20 61 6e 64 20 70 72 65 73 73 20 61 6e 79 20 6b 0000112 65 79 20 74 6f 20 72 65 62 6f 6f 74 2e 2e 2e 0d 0000128 0a 00 50 45 00 00 64 86 04 00 00 00 00 00 00 00 0000144 00 00 01 00 00 00 a0 00 06 02 0b 02 02 14 80 37 0000160 8e 00 00 00 00 00 80 86 26 02 f0 48 00 00 00 02 0000176 00 00 00 00 00 00 00 00 00 00 20 00 00 00 20 00 0000192 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000208 00 00 00 c0 b4 02 00 02 00 00 00 00 00 00 0a 00 0000224 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * 0000256 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 0000272 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000288 00 00 00 00 00 00 00 00 00 00 80 39 8e 00 48 09 0000304 00 00 00 00 00 00 00 00 00 00 2e 73 65 74 75 70 0000320 00 00 e0 43 00 00 00 02 00 00 e0 43 00 00 00 02 0000336 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 0000352 50 60 2e 72 65 6c 6f 63 00 00 20 00 00 00 e0 45 0000368 00 00 20 00 00 00 e0 45 00 00 00 00 00 00 00 00 0000384 00 00 00 00 00 00 40 00 10 42 2e 74 65 78 74 00 0000400 00 00 80 f3 8d 00 00 46 00 00 80 f3 8d 00 00 46 0000416 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 0000432 50 60 2e 62 73 73 00 00 00 00 80 86 26 02 80 39 0000448 8e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000464 00 00 00 00 00 00 80 00 00 c8 00 00 00 00 00 00 0000480 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff 0000496 ff 22 01 00 38 df 08 00 00 00 ff ff 00 00 55 aa 0000512 eb 66 48 64 72 53 0d 02 00 00 00 00 00 10 c0 37 0000528 00 01 00 80 00 00 10 00 00 00 00 00 00 00 00 00 0000544 00 00 00 00 50 5a 00 00 00 00 00 00 ff ff ff 7f 0000560 00 00 00 01 01 15 3f 00 ff 07 00 00 00 00 00 00 0000576 00 00 00 00 00 00 00 00 b1 03 00 00 11 f3 89 00 0000592 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 0000608 00 c0 b4 02 90 01 00 00 8c d8 8e c0 fc 8c d2 39 0000624 c2 89 e2 74 16 ba 50 58 f6 06 11 02 80 74 04 8b # od -A d -t x1 /boot/vmlinuz-5.3.7-301.fc31.x86_64 | grep -i '1f 8b 08 00' 0018864 8f 1f 8b 08 00 00 00 00 00 02 03 ec fd 79 7c 54 

итак, по адресу 0018864 начинается реальное ядро ( vmlinux ), в то время как сам файл vmlinuz стартует с 0000000 . Это означает, что с 0000000 по 0018864 то что у нас имеется, это соответствующий заголовок нашего файла, такой как header.S , misc.c и т.п.. Именно он и распаковывает само реальное ядро ( vmlinux ) из vmlinuz . Вы можете рассматривать некий заголовок подобным нашлёпке поверх исполняемого файла vmlinux и когда эта нашлёпка доступна, он превращается в vmlinuz ю В своём следующем разделе мы увидим как соответствующая процедура ядра распаковыает vmlinuz .

extract_kernel

Давайте вернёмся назад к своей функции extract_kernel из arch/x86/boot/compressed/misc.c .

 asmlinkage __visible void *extract_kernel(void *rmode, memptr heap, unsigned char *input_data, unsigned long input_len, unsigned char *output, unsigned long output_len) 

Как вы можете видеть, эта функция принимает семь параметров.

rmode

Указатель на структуру boot_params , которая заполняется запускающим начальным загрузчиком.

heap

Указатель на файл boot_params , который представляет стартовый адрес самой ранней кучи запуска.

input_data

Указатель на наало самого схатого ядра или, иначе говоря, указатель на arch/x86/boot/compressed/vmlinux.bin.bz2 .

input_len

Размер нашего сжатого ядра

output

Значение начального адреса нашего распаковываемого дале ядра

output_len

Значение размера распаковываемого ядра

run_size

Общий объём пространства, необходимый для выполнения самого ядра, включая разделы .bss и .brk

Наряду с самим ядром, наш начальный загрузчик также подгрузил в память и initramfs. Об initramfs мы поговорим в Главе 5. итак, перед распаковкой необходимого образа ядра, наш заголовок или соответствующая процедура ядра должен позаботиться о том, что распаковка vmlinuz не перекроет уже загруженный образ initramfs. По этой причине функция extract_kernel также заботится о вычислении адресного пространства initramfs и выполнит выравнивание соответствующим образом распакованный образ своего ядра. После того как мы получили верный адрес по которому наш заголовок может распаковывать vmlinuz , он выделит это ядро там.

 340 asmlinkage __visible void *extract_kernel(void *rmode, memptr heap, 341 unsigned char *input_data, 342 unsigned long input_len, 343 unsigned char *output, 344 unsigned long output_len) 345 < 346 const unsigned long kernel_total_size = VO__end - VO__text; 347 unsigned long virt_addr = LOAD_PHYSICAL_ADDR; 348 unsigned long needed_size; 349 350 /* Retain x86 boot parameters pointer passed from startup_32/64. */ 351 boot_params = rmode; 352 353 /* Clear flags intended for solely in-kernel use. */ 354 boot_params->hdr.loadflags &= ~KASLR_FLAG; 355 356 sanitize_boot_params(boot_params); 357 358 if (boot_params->screen_info.orig_video_mode == 7) < 359 vidmem = (char *) 0xb0000; 360 vidport = 0x3b4; 361 >else < 362 vidmem = (char *) 0xb8000; 363 vidport = 0x3d4; 364 >365 366 lines = boot_params->screen_info.orig_video_lines; 367 cols = boot_params->screen_info.orig_video_cols; 368 369 console_init(); 370 371 /* 372 * Save RSDP address for later use. Have this after console_init() 373 * so that early debugging output from the RSDP parsing code can be 374 * collected. 375 */ 376 boot_params->acpi_rsdp_addr = get_rsdp_addr(); 377 378 debug_putstr("early console in extract_kernel\n"); 379 380 free_mem_ptr = heap; /* Heap */ 381 free_mem_end_ptr = heap + BOOT_HEAP_SIZE; 382 383 /* 384 * The memory hole needed for the kernel is the larger of either 385 * the entire decompressed kernel plus relocation table, or the 386 * entire decompressed kernel plus .bss and .brk sections. 387 * 388 * On X86_64, the memory is mapped with PMD pages. Round the 389 * size up so that the full extent of PMD pages mapped is 390 * included in the check against the valid memory table 391 * entries. This ensures the full mapped area is usable RAM 392 * and doesnt include any reserved areas. 393 */ 394 needed_size = max(output_len, kernel_total_size); 395 #ifdef CONFIG_X86_64 396 needed_size = ALIGN(needed_size, MIN_KERNEL_ALIGN); 397 #endif 398 399 /* Report initial kernel position details. */ 400 debug_putaddr(input_data); 401 debug_putaddr(input_len); 402 debug_putaddr(output); 403 debug_putaddr(output_len); 404 debug_putaddr(kernel_total_size); 405 debug_putaddr(needed_size); 406 407 #ifdef CONFIG_X86_64 408 /* Report address of 32-bit trampoline */ 409 debug_putaddr(trampoline_32bit); 410 #endif 411 412 choose_random_location((unsigned long)input_data, input_len, 413 (unsigned long *)&output, 414 needed_size, 415 &virt_addr); 416 417 /* Validate memory location choices. */ 418 if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1)) 419 error("Destination physical address inappropriately aligned"); 420 if (virt_addr & (MIN_KERNEL_ALIGN - 1)) 421 error("Destination virtual address inappropriately aligned"); 422 #ifdef CONFIG_X86_64 423 if (heap > 0x3fffffffffffUL) 424 error("Destination address too large"); 425 if (virt_addr + max(output_len, kernel_total_size) > KERNEL_IMAGE_SIZE) 426 error("Destination virtual address is beyond the kernel mapping area"); 427 #else 428 if (heap > ((-__PAGE_OFFSET-(128

Необходимый метод распаковки будет выбран в соответствии с методом сжатия, использованном на момент компиляции ядра. Доступные методы распаковки можно обнаружить в том же самом файле misc.c .

  57 #ifdef CONFIG_KERNEL_GZIP 58 #include "../../../../lib/decompress_inflate.c" 59 #endif 60 61 #ifdef CONFIG_KERNEL_BZIP2 62 #include "../../../../lib/decompress_bunzip2.c" 63 #endif 64 65 #ifdef CONFIG_KERNEL_LZMA 66 #include "../../../../lib/decompress_unlzma.c" 67 #endif 68 69 #ifdef CONFIG_KERNEL_XZ 70 #include "../../../../lib/decompress_unxz.c" 71 #endif 72 73 #ifdef CONFIG_KERNEL_LZO 74 #include "../../../../lib/decompress_unlzo.c" 75 #endif 

После того как наше ядро распаковано в памяти, из самой функции extract_kernelc будет получена точка входа этого раскрытого ядра и наш ЦПУ выполнит безусловный переход вовнутрь ядра.

Внутри установленного ядра

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

  • Наше ядро настроит размер стека самого ядра на 16 кБ в случае когда архитектура 64- битная. Это означает, что всякий новый процесс будет получать сво1 собственный стек ядра, который будет обладать в размере 16 кБ.
  • page_size будет установлен на 4 кБ, что является размером страницы по умолчанию для 64- битной архитектуры Intel.
  • Наше ядро подготовит оответствующий механизм обработки прерываний и исключительных ситуаций, также именуемый таблицей дескрипторов прерываний (IDT, interrupt descriptor table).
  • Ядро настроит необходимый механизм обработки отказов страниц.
  • Установленное ядро соберёт подробности своего файла initramfs, такие как название файла, размер, адрес, адрес перемещения, старшие и младшие номера нового корневого устройства и т.п. из /arch/x86/kernel/setup.c .
  • Далее оно выделит initramfs из его исходного кода в init/initramfs.c .
  • Наконец, оно запустит systemd при помощи функции start_kernel из init/main.c .

Вы можете обратить внимание, что это самый первый случай, когда мы выходим за рамки своего каталога arch . Это означает, что мы можем рассматривать данный код как не зависимый от архитектуры. После того как наше ядро запущено, оно выполняет множество вещей и нет практически никакой возможности охватить их все в данной книге. С точки зрения запуска лозунгом нашего ядра выступает запуск systemd из initramfs. Поскольку initramfs уже была загружена в память начальным загрузчиком, выделение необходимого ядра initramfs потребует подробностей файла самой initramfs, которые наше ядро получает из /arch/x86/kernel/setup.c .

 Initramfs file name, Initramfs file size, Initramfs files address, Initramfs files relocation address, Major and minor numbers on which initramfs will be mounted. 

После того как наше ядро получило подробности и своём файле initramfs, оно выделяет сам архив initramfs из файла init/initramfs.c . Подробнее мы обсудим как в точности наше ядро раскрывает в памяти initramfs в Главе 5. Для монтирования initramfs в качестве корневого устройствапотребуются виртуальные файловые системы, такие как proc , sys , dev и т.п., а потому наше ядро подготавливает их надлежащим образом.

 err = register_filesystem(&proc_fs_type); if (err) return; 

Наше ядро позднее смонтирует свю выделенную initramfs при помощи функции do_mount_root из init/do_mounts.c . Как только initramfs смонтирована в памяти, наше ядро запустит оттуда systemd. systemd будет запущен через ту же самую функцию start_kernel из файла init/main.c .

 asmlinkage void __init start_kernel(void) 

В целом, когда наша корневая файловая система готова, она включится вовнутрь своей корневой файловой системы и создаст два потока: PID 1 это процесс systemd, а PID 2 это kthread. Для лучшего понимания отсылаем вас к показанной на Рисунке 4-4 блок- схеме.

Рисунок 4-4



Наша вновь обновлённая блок- схема

Рисунок 4-5. отображает окончательную последовательность запуска которую мы обсудили на данный момент.

Рисунок 4-5

Наша последовательность запуска в некой блок- схеме

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

Linux Kernel EFI Boot Stub или «Сам себе загрузчик»

UEFI Tux Logo

Прочитав недавнюю статью Загрузка ОС Linux без загрузчика, понял две вещи: многим интересна «новинка», датируемая аж 2011 годом; автор не описал самого основного, без чего, собственно, и работать ничего не будет в некоторых случаях. Также была ещё одна статья, но либо она уже устарела, либо там опять таки много лишнего и недосказанного одновременно.

А конкретно, был упущен основной момент — сборочная опция ядра CONFIG_EFI_STUB . Так как в последних версиях U(lu/ku/edu/*etc*)buntu эта опция по умолчанию уже включена, никаких подозрений у автора не появилось.
Насколько мне известно, на текущий момент она включена в дистрибутивах указанных версий и выше: Arch Linux, Fedora 17, OpenSUSE 12.2 и Ubuntu 12.10. В комментах ещё упомянули, что Debian с ядром 2.6 умеет, но это не более, чем бэкпорт с последних версий. На этих дистрибутивах пересобирать вообще ничего не нужно! А ведь на других CONFIG_EFI_STUB, скорее всего, либо вообще отсутствует, т. к. опция доступна только с ядра версии 3.3.0 и выше, либо выключена по умолчанию. Соответственно, всё, описанное ниже, справедливо для ядра, собранного с опцией CONFIG_EFI_STUB.

Итак, что же такое Linux Kernel EFI Boot Stub?
Общая информация

А ни что иное, как… «exe-файл»! Да-да, «виндовый» PE/COFF. Ну, а точнее, только закос под него с небольшими модификациями, чтобы угодить загрузчику UEFI. Можно убедиться в этом, прочитав первые 2 байта ядра:

$ od /boot/vmlinuz-linux --address-radix=x --read-bytes=2 -t x1c 0000000 4d 5a M Z 0000002 

Знакомо, не правда ли? Как минимум тем, кто хоть раз «для интереса» открывал исполняемый файл MS-DOS или Windows в блокноте, хекс-редакторе или чём-то покруче. Это инициалы Марка Збиковски, который, собственно, и разработал данный формат файлов в MS-DOS. Сигнатура этой заглушки до сих пор висит рудиментом в современных исполнимых файлах Windows, сжирая со своим заголовком целых 64 байта на каждый файл!

DOS-заголовок попадает на legacy-код, который выполняется в случае загрузки ядра как бут-сектора, и ругается на манеру MS-DOS при запуске PE-файлов: «Direct floppy boot is not supported. Use a boot loader program instead. Remove disk and press any key to reboot . ». Поэтому информация из этого заголовка здесь является мусором, кроме, собственно, сигнатуры 'MZ' и адреса смещения следующего заголовка.

Идём дальше.
Спецификация PE/COFF говорит нам, что по смещению 0x3c находится 32-битное смещение второго заголовка с сигнатурой «PE\0\0»:

$ od /boot/vmlinuz-linux --address-radix=x --read-bytes=4 --skip-bytes=0x3c -t x4 00003c 000000b8 000040 

итак, смещение равно 0xb8, что справедливо для текущего stable-ядра x86_64 архитектуры, на x86 будет 0xa8. Читаем:

$ od /boot/vmlinuz-linux --address-radix=x --read-bytes=4 --skip-bytes=0xb8 -t x1c 0000b8 50 45 00 00 P E \0 \0 0000bc 

А вот и сигнатура второго заголовка! Как можно было догадаться, это аббревиатура от словосочетания Portable Executable, с которой и начинается полезная нагрузка в исполнимых файлах.

Даже загрузчик Windows плевал на половину полей этого заголовка, а уж UEFI они и вовсе не нужны, поэтому некоторые из них прописаны статически, важные же — заполняются во время сборки ядра. Множество «ненужных» полей, всяких таймстемпов, котрольных сумм и пр. просто остаются нулями. Заполняются в основном размеры, смещения, точка входа и т. д. Поэтому, можно с натяжкой назвать данный PE-файл полностью валидным. Однако, классические утилиты LordPE или PETools вполне себе довольствуются сигнатурами и рассказывают о файле всё, что им известно:

PE optional header

image

Основное отличие от «реальных» исполняемых файлов в Windows — это флаг Subsystem опционального заголовка, который выставляется в IMAGE_SUBSYSTEM_EFI_APPLICATION, а не в IMAGE_SUBSYSTEM_WINDOWS_GUI для графических или IMAGE_SUBSYSTEM_WINDOWS_CUI для символьных (консольных) приложений Windows соответственно.

Структура

В общем же, всё как в обычном PE-файле. На текущий момент стабильной версии 3.11.4 ядра Arch Linux из репозиториев, в нём содержатся 3 секции: '.setup', '.reloc' и '.text'.

  • Секция .setup, содержит в основном legacy-код для инициализации в случае загрузки в режиме совместимости. При загрузке же в UEFI mode, все переключения режимов процессора, начальные инициализации производит прошивка.
  • .reloc секция обязательно требуется загрузчиком, поэтому при сборке ядра создаётся пустая заглушка «чтоб было».
  • Самая интересная секция .code, собственно, содержит EntryPoint и основной код всего остального ядра. После того, как EFI-application найдено, загрузчик выполняет загрузочный сервис LoadImage, тем самым загружая весь образ в память. Тип резидентности зависит от поля Subsystem: EFI_APPLICATION будет выгружаться, когда отработает. EFI_DRIVER же может быть Unloadable и выгрузится только в случае критической ошибки. Далее передаётся управление на точку входа, обычно это функция efi_main() — аналог main() в C.
Основные требования

image

Прежде всего, необходимо активировать режим загрузки EFI-mode. Пункт может называться как вдумается вендору, обычно находится во вкладке Boot Options. Если увидели там что-то вроде Legacy Mode или CSM (Compatibility Support Mode), либо просто BOIS Mode, меняйте на что-то похожее: (U)EFI Mode, Enhanced Mode или Advanced Mode.

Если материнская плата имеет логотип «Windows 8 Ready!», то, скорее всего, режим EFI Boot Mode уже активирован по умолчанию.

В большинстве случаев, для загрузки ядра Linux в EFI-mode необходимо выключить опцию Secure Boot.

Разметка диска

Многие источники указывают, что обязательно нужна разметка диска GPT, а не MBR, но это не так. UEFI вполне себе умеет MBR. Другое дело, например, Windows насильно заставляет разбивать диск новым методом, чтобы грузиться в EFI mode и ругается на древность Master Boot Record'а. И правильно делает! Разметив диск «современно» ничего не потеряем, а только выиграем.
Во-первых, не будет проблем со всякими там Primary/Logical разделами, «туда не ходи — сюда ходи» и прочими рудиментами.
Во-вторых, хоть сейчас и продвигаются массово SolidState-диски, у которых объёмы пока не сильно удивляют, размером же обычной «вертушки» в несколько терабайт сейчас уже никого не удивишь. А ведь под MBR можно разметить раздел максимум около 2ТБ. GPT же, видит ну очень много, можно даже цифру не называть — относительно не скоро появятся диски таких размеров.
Ну и плюс всякие бонусы, типа дублирования GPT-записи в начале и конце диска, контрольные суммы целостности и т. п., добавляют желания не раздумывая размечать диск под GPT.

Статей, как разбить диск с помощью различных утилит в GNU/Linux можно найти огромное количество.

Отдельный раздел
Тип раздела

Спустя nn-цать лет разработки стандартов, инженеры таки решили, что хардкодить — не есть хорошо. Теперь не важно где находится наш загрузочный раздел, загрузчик UEFI делает очень просто: он перебирает все подряд разделы и диски и ищет один особенный. Особенность его заключается в том, что в случае с MBR-разметкой, он имеет тип с кодом 0xEF (как можно догадаться, от EFI). В случае разметки GPT — раздел с GUID равным C12A7328-F81F-11D2-BA4B-00A0C93EC93B.

Здесь существует некоторая неявность. Все утилиты для разметки, например, parted, имеют свойство установки и отображения флага «boot», который применяется к разделу. Так вот, в случае с MBR, такая возможность действительно имеется, т. е. существует реальный байт, который указывает БИОСу на «загрузочность» раздела. Этот флаг можно поставить на любой раздел, MBR которого, мы хотим скормить БИОСу для загрузки. Но, когда мы имеем дело с GPT, никакого флага в действительности нет! Под этим флагом parted понимает как раз GUID равный вышеуказанному. Т. е. по факту GPT boot flag = GPT EFI Partition!

parted

# parted /dev/sda -l Модель: ATA ST3750330AS (scsi) Диск /dev/sda: 750GB Размер сектора (логич./физич.): 512B/512B Таблица разделов: gpt Disk Flags: Номер Начало Конец Размер Файловая система Имя Флаги 1 1049kB 135MB 134MB fat32 EFI System загрузочный 2 135MB 269MB 134MB ext2 Linux filesystem 3 269MB 8859MB 8590MB linux-swap(v1) Linux swap 4 8859MB 30,3GB 21,5GB ext4 Linux filesystem 5 30,3GB 46,4GB 16,1GB ext4 Linux filesystem 6 46,4GB 67,9GB 21,5GB ext4 Linux filesystem 7 67,9GB 750GB 682GB xfs Linux filesystem 

gdisk же, не страдает этим:

# gdisk /dev/sda -l GPT fdisk (gdisk) version 0.8.7 Partition table scan: MBR: protective BSD: not present APM: not present GPT: present Found valid GPT with protective MBR; using GPT. Disk /dev/sda: 1465149168 sectors, 698.6 GiB Logical sector size: 512 bytes Disk identifier (GUID): 02D11900-D331-4114-A3D7-8493969EF533 Partition table holds up to 128 entries First usable sector is 34, last usable sector is 1465149134 Partitions will be aligned on 2048-sector boundaries Total free space is 2014 sectors (1007.0 KiB) Number Start (sector) End (sector) Size Code Name 1 2048 264191 128.0 MiB EF00 EFI System 2 264192 526335 128.0 MiB 8300 Linux filesystem 3 526336 17303551 8.0 GiB 8200 Linux swap 4 17303552 59246591 20.0 GiB 8300 Linux filesystem 5 59246592 90703871 15.0 GiB 8300 Linux filesystem 6 90703872 132646911 20.0 GiB 8300 Linux filesystem 7 132646912 1465149134 635.4 GiB 8300 Linux filesystem 

Вывод: если наш EFI-раздел на MBR — ставим тип раздела EFI Partition и boot flag. Если GPT — либо тип EFI Partition, либо boot flag, так как они представляют собой одно и то же.

Есть ещё всякие вещи, типа GPT legacy boot flag, который устанавливается в Protective MBR, и прочее, но всё это костыли, которые используются только в режиме совместимости. В режиме GPT UEFI Boot должны игнорироваться.

Файловая система

В разных источниках пишут по-разному. Кто-то говорит, что FAT16 можно использовать, кто-то даже FAT12 рекомендует. Но, не лучше ли последовать совету официальной спецификации? А она говорит, что системный раздел должен быть в FAT32. Для removable-media (USB HDD, USB Flash) — ещё и FAT12/FAT16 в придачу к FAT32.
Про размер раздела ничего не говорится. Однако, по причине начальных костыльных и баганых реализаций загрузчиков и прошивок, опытным путём народ выяснил, что во избежание различных «внезапностей», рекомендуется размер не менее 520МиБ (546МБ). Здесь как повезёт, проблем может не быть и с 32-мегабайтным разделом.

Структура директорий

После того, как загрузчик нашёл свой «меченый» раздел и убедился, что поддерживает файловую систему, он начинает выполнять все действия с путями, относительно корня раздела. Кроме того, все файлы в данном разделе должны находиться в директории \EFI\, которая, в свою очередь, является единственной в корне раздела. По соглашению, каждому вендору рекомендуется выделить себе папку с уникальным названием и поместить её в \EFI\, например: \EFI\redhat\, \EFI\microsoft\, \EFI\archlinux\. В директории вендора находятся непосредственно исполнимые efi-приложения. Рекомендуется один файл на одну архитектуру. Файлы должны иметь расширение .efi.

Для съёмных устройств предназначена директория \EFI\BOOT\. В ней так же рекомендуется не более одного файла для каждой архитектуры. В дополнение к этому, файл должен называться boot.efi. Например, \EFI\BOOT\bootx64.efi. Доступные архитектуры: ia32, x64, ia64, arm, aa64.

Доступ к NVRAM

По умолчанию, если ничего не записано в энергонезависимой памяти UEFI, будет загружаться \EFI\BOOT\bootx64.efi. Чтобы записать в NVRAM путь к необходимому приложению, можно воспользоваться утилитой efibootmgr. Попробуем вывести текущие записи:

# efibootmgr -v 

В некоторых дистрибутивах для работы этой утилиты требуется включенная опция ядра CONFIG_EFI_VARS.

Приступаем

image

Итак, мы разметили FAT32 EFI System Partition (ESP) размером 550МиБ . Либо, у нас стоит винда второй системой и уже сама создала его. Правда, создаёт она его обычно размером около 100МБ, но лично у меня проблем никогда не возникало.
В /boot уже имеется ядро с поддержкой EFI boot STUB.

Проверить
Чтобы проверить, была ли включена опция при сборке ядра, выполним:

$ zgrep CONFIG_EFI_STUB /proc/config.gz 
$ zgrep CONFIG_EFI_STUB /boot/config-`uname -r` 

CONFIG_EFI_STUB=y означает, что опция активна.

    Можно смонтировать ESP\\ на /boot через mount --bind, предварительно скопировав содержимое.

Исключения

Данный пункт подходит только для дистрибутивов, которые не содержат символические ссылки в каталоге /boot. Например, на openSUSE монтировать не удастся, т. к. он содержит там несколько ссылок, в том числе и на само ядро.

  • Используем efibootmgr, который умеет передавать параметры ядра.
  • Если efibootmgr брыкается, можно воспользоваться UEFI Shell, который, как и наше ядро, является EFI-приложением. Через его команду bcfg возможно редактировать пункты загрузки.
  • Может быть такой вариант: efibootmgr ругается на добавление параметров, значит прошивка не поддерживает их запись (либо просто кривая, что вероятнее). В прошлой статье в комментах упомянули параметр ядра efi_no_storage_paranoia , который может помочь. Но пользоваться им можно только если вы уверены в том, что ваша прошивка реализована полностью в соответствии со спецификацией! Разработчики предупреждают, что если вендор добавил костылей и отсебятины при реализации, есть неиллюзорная вероятность материализовать кирпич на месте материнской платы.
  • Можно также грузиться через UEFI Shell. Для него создаётся скриптstartup.nsh, в котором указывается команда загрузки ядра с нужным command line. А Shell, в свою очередь, добавляется как пункт загрузки.
  • Существует ещё одна проблема: добавление пункта возможно только для одного пути ядра, при этом ram-disk не видится. В большинстве статей рекомендуют при этом пересобирать ядро со встроенным initrd. Точно не знаю — проблема ли ядра это, или загрузчика. Но на текущий момент в 90% случаев всё поддерживается и пересобирать ядро не нужно.

Вероятная причина заблуждения

Рекомендации по встраиванию рам-диска в ядро пошли, скорее всего, из-за массового неверного указания пути к нему. В ранних реализациях EFI Boot Stub, ядро не плевалось ошибкой о неверном пути к ram-disk, а молча отказывалось грузиться. Видимо поэтому все начали массово внедрять его в ядро, решив, что он не поддерживается. Хотя поддержка параметра initrd существует с самого появления в ядре фичи Boot Stub.

ВАЖНО: Путь к ram-диску передаётся абсолютный через обратные слеши " \ ", а не прямые! Например, initrd=\EFI\archlinux\initramfs-linux.img.

Исключения для анархистов

На самом деле, ядра, версий выше 3.8.0-rc5 не видят разницы между прямым и обратным слешем — будет работать любой. Но вот с момента появления фичи Boot Stub в версии 3.2.0-rc5, путь, записанный через прямые слеши, ядро просто не видело и молча без ошибок отказывалось грузиться. Ругаться ошибками по этому поводу, оно научилось в версии 3.4.0.

  • Скачать первый попавшийся live-cd с поддержкой EFI boot. И из него уже воспользоваться командой efibootmgr.
  • Скачать UEFI Shell. Из него можно, как загрузиться в режиме EFI, просто указав ядро и ram disk, так и редактировать пункты загрузки.
Dualboot без загрузчика

Если у вас установлены 2 системы одновременно, и всё равно не хочется ставить сторонний загрузчик, можно добавить обе в пункты загрузки UEFI и подкорректировать предпочитаемый boot order. Загрузчик Windows обычно располагается в \EFI\Microsoft\BOOT\bootmgfw.efi.

Итого

Если всё сделано правильно, перегружаемся, вызываем Boot Menu, выбираем добавленный нами пункт и смотрим на почти мгновенную загрузку. В случае с SSD, FastBoot, Readahead и Arch Linux — около 3-4 секунд. Домашний сервер уже год загружается без всяких сторонних загрузчиков, используя EFI Boot STUB.
Конечно, выигрыш в скорости тут минимальный, но, как пишут знающие люди типа Roderick Smith, иногда в режиме EFI Boot происходит «более адекватная» инициализация оборудования, чем в режимах совместимости.

Заключение

По причине относительной сырости прошивок UEFI и совершенно различных реализаций, я не стал приводить примеры кода. В каждом случае может существовать своя проблема. Надеюсь, описанное мной поможет понять общий принцип и применить к своему случаю.
Также рекомендую прошиться последней версией UEFI с сайта производителя материнской платы.

Литература

Официальные спецификации UEFI
Roderick W. Smith's Web Page — творчество автора многих утилит, связанных с EFI, загрузчиками и разметкой диска.
ArchWiki: UEFI Bootloaders — бессменная и одна из лучших и полных вики по GNU/Linux одного из дистрибутивов.
Официальная спецификация PE/COFF

Настройка⚓︎

В данной главе подробно рассказано о настройке ядра из исходного кода.

Подготовка⚓︎

Подготовьте пакет к компиляции, выполнив следующую команду:

 make mrproper 

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

Обратите внимание что после распаковки пакета с исходным кодом не следует полагаться на его "чистоту".

Инструменты настройки параметров ядра⚓︎

Конфигурация ядра хранится в файле .config . Именно этот файл следует отредактировать, указав необходимые опции, в соответствии с вашим оборудованием и предпочтениями.

Для наглядности и облегчения восприятия, настройку ядра можно произвести при помощи утилит, предоставляющих графический или псевдографический интерфейс:

  • make xconfig – при использовании графической среды KDE
  • make gconfig – при использовании графической среды GNOME
  • make menuconfig – псевдографический режим
  • make config – вариант настройки, выводящий запросы на задание значений каждого параметра ядра. (Не позволяет изменить уже заданные параметры)

Практически все варианты (за исключением последнего) позволяют получать краткую справку по каждому параметру, производить поиск нужного параметра (или раздела), добавлять в конфигурацию дополнительные компоненты, драйверы, а также показывают, каким образом конкретный компонент может быть сконфигурирован — как компонент, встроенный в ядро или как загружаемый модуль, а также поддерживает ли он вообще вариант компиляции в качестве загружаемого модуля.

Создание конфигурации⚓︎

Хорошей отправной точкой для настройки ядра может стать запуск команды make defconfig .

 make defconfig 

Будет создана базовая конфигурация с настройками по умолчанию с учётом архитектуры машины. Параметры берутся из архитектурно-зависимых defconfig файлов.

Настройка параметров⚓︎

Предупреждение

Убедитесь в том, что вы включили/отключили/указали указанные ниже параметры

настройки. В ином случае система может работать неправильно или вовсе не загрузится:

 General setup --> [ ] Auditing Support [CONFIG_AUDIT] [*] Control Group support [CONFIG_CGROUPS] [ ] Enable deprecated sysfs features to support old userspace tools [CONFIG_SYSFS_DEPRECATED] [*] Configure standard kernel features (expert users) [CONFIG_EXPERT] ---> [*] open by fhandle syscalls [CONFIG_FHANDLE] Processor type and features ---> [*] Enable seccomp to safely compute untrusted bytecode [CONFIG_SECCOMP] Firmware Drivers ---> [*] Export DMI identification via sysfs to userspace [CONFIG_DMIID] Networking support ---> [CONFIG_NET] Networking options ---> Packet socket [CONFIG_PACKET] The IPv6 Protocol ---> [CONFIG_IPV6] Device Drivers ---> Generic Driver Options ---> [ ] Support for uevent helper [CONFIG_UEVENT_HELPER] [*] Maintain a devtmpfs filesystem to mount at /dev [CONFIG_DEVTMPFS] Firmware Loader ---> [ ] Enable the firmware sysfs fallback mechanism [CONFIG_FW_LOADER_USER_HELPER] File systems ---> [*] Inotify support for userspace [CONFIG_INOTIFY_USER] Pseudo filesystems ---> [*] Tmpfs POSIX Access Control Lists [CONFIG_TMPFS_POSIX_ACL] 

Обратите внимание

Указанные ниже параметры используйте на своё усмотрение.

Поддержка файловых систем⚓︎

Обратите внимание на раздел File systems . Включите поддержку требуемых файловых систем. Обязательно включите The Extended 4 (ext4) filesystem для её поддержки.

EFI⚓︎

Если вы будете использовать EFI, необходимо обеспечить его поддержку:

 Processor type and features ---> [*] EFI runtime service support [CONFIG_EFI] [*] EFI stub support [CONFIG_EFI_STUB] Firmware Drivers ---> EFI (Extensible Firmware Interface) Support ---> < >EFI Variable Support via sysfs [CONFIG_EFI_VARS] [*] Export efi runtime maps to sysfs [CONFIG_EFI_RUNTIME_MAP] Enable the block layer ---> Partition Types ---> [*] Advanced partition selection [CONFIG_PARTITION_ADVANCED] [*] EFI GUID Partition support [CONFIG_EFI_PARTITION] Device Drivers ---> Graphics support ---> Frame buffer Devices ---> Support for frame buffer devices ---> [CONFIG_FB] [*] EFI-based Framebuffer support [CONFIG_FB_EFI] Console display driver support ---> [*] Framebuffer Console support [CONFIG_FRAMEBUFFER_CONSOLE] File systems ---> Pseudo filesystems ---> EFI Variable filesystem [CONFIG_EFIVAR_FS] 

FUSE⚓︎

FUSE (англ. filesystem in userspace — «файловая система в пользовательском пространстве») — свободный модуль для ядер Unix-подобных операционных систем, позволяющий разработчикам создавать новые типы файловых систем, доступные для монтирования пользователями без привилегий (прежде всего — виртуальных файловых систем); это достигается за счёт запуска кода файловой системы в пользовательском пространстве, в то время как модуль FUSE предоставляет связующее звено для актуальных интерфейсов ядра. C использованием средств FUSE разработаны, в частности, SSHFS, NTFS-3G, GlusterFS, ZFS.

 File systems ---> FUSE (Filesystem in Userspace) support [CONFIG_FUSE_FS] 

Файловые системы Windows⚓︎

 File systems --->  MSDOS fs support [CONFIG_MSDOS_FS] VFAT (Windows-95) fs support [CONFIG_VFAT_FS] 

XFS⚓︎

XFS — высокопроизводительная 64-битная журналируемая файловая система, созданная компанией Silicon Graphics для собственной операционной системы IRIX. 1 мая 2001 года Silicon Graphics выпустила XFS под GNU General Public License (Linux версия 2.2). XFS отличается от других файловых систем тем, что она изначально была рассчитана для использования на дисках большого объёма (более 2 терабайт).

 File systems ---> XFS filesystem support [CONFIG_XFS_FS] 

LVM⚓︎

При необходимости включите поддержу LVM.

LVM — это метод распределения пространства жёсткого диска по логическим томам, размер которых можно легко менять, в отличие от разделов.

С LVM пространство жёсткого диска или набора дисков распределяется по физическим томам. Физический том не может располагаться более чем на одном диске.

Физические тома собираются в группы логических томов, за исключением раздела /boot. Раздел /boot не может находиться в группе логических томов, так как в этом случае загрузчику не удастся его прочитать. Если корневой раздел (/) находится на логическом томе, создайте отдельный раздел /boot вне группы томов.

 Device Drivers ---> [*] Multiple devices driver support (RAID and LVM) ---> [CONFIG_MD] Device mapper support [CONFIG_BLK_DEV_DM] Crypt target support [CONFIG_DM_CRYPT] Snapshot target [CONFIG_DM_SNAPSHOT] Thin provisioning target [CONFIG_DM_THIN_PROVISIONING] Mirror target [CONFIG_DM_MIRROR] Kernel hacking ---> Generic Kernel Debugging Instruments ---> [*] Magic SysRq key [CONFIG_MAGIC_SYSRQ] 

Cryptographic API⚓︎

Cryptographic API предлагает богатый набор криптографических шифров, а также другие механизмы и методы преобразования данных для их вызова. Cryptographic API предоставляет различные вызовы API для следующих типов шифров:

  • Симметричные шифры
  • Шифры AEAD
  • Дайджест сообщения, включая дайджест сообщения с ключом
  • Генерация случайных чисел
  • Интерфейс пользовательского пространства
 Device Drivers ---> [*] Multiple devices driver support (RAID and LVM) ---> [CONFIG_MD] Device mapper support [CONFIG_BLK_DEV_DM] Crypt target support [CONFIG_DM_CRYPT] Cryptographic API ---> XTS support [CONFIG_CRYPTO_XTS] SHA224 and SHA256 digest algorithm [CONFIG_CRYPTO_SHA256] AES cipher algorithms [CONFIG_CRYPTO_AES] User-space interface for symmetric key cipher algorithms [CONFIG_CRYPTO_USER_API_SKCIPHER] For tests: Twofish cipher algorithm [CONFIG_CRYPTO_TWOFISH] 

Драйверы устройств⚓︎

В разделе Device Drivers - нужно пройтись по разделам и включить драйвера для своего оборудования - нестандартные жёсткие диски, мышки, USB устройства, веб-камеры, Bluetooth, Wi-Fi адаптеры, принтеры и т. д.

Если вы собирали пакет pciutils , то просмотреть, какое оборудование подключено к вашему ПК, можно командой:

 lspci 

Сборка⚓︎

Когда все параметры настроены, можно приступать к сборке.

 make 

Обратите внимание

Обратите внимание, что процесс сборки ядра может проходить длительное время (от 4.4 до 66.0 SBU). Это во многом зависит от установленных параметров конфигурации.

Установите модули ядра:

 make modules_install 

Установка⚓︎

После завершения сборки необходимо выполнить еще несколько шагов. Некоторые файлы должны быть скопированы в каталог /boot .

Путь к образу ядра зависит от используемой платформы. Имя файла, указанное ниже, может иметь произвольное наименование, на ваш вкус, но имя файла должно начинаться с vmlinuz для обеспечения совместимости автоматической настройки процесса загрузки. При выполнении следующей команды будет считаться, что используется архитектура x86:

 cp -iv arch/x86/boot/bzImage /boot/vmlinuz-5.14.14-my-kernel 

System.map файл, внутри которого находится символьная таблица адресов функций и процедур, используемых ядром операционной системы Linux. В этой таблице перечислены имена переменных и функций и их адреса в памяти компьютера. Эта таблица весьма полезна при отладке ядра в случае Kernel panic или Linux oops. System.map генерируется при компиляции ядра. Выполните следующую команду для установки файла System.map :

 cp -iv System.map /boot/System.map-5.14.14 

Файл конфигурации ядра .config , полученный в результате настройки make menuconfig содержит в себе все опции конфигурации скомпилированного ядра. Хорошей идеей будет оставить этот файл для будующей работы:

 cp -iv .config /boot/config-5.14.14 

Для облегчения обновления ядра создайте символическую ссылку:

 ln -svf vmlinuz-5.14.14-my-kernel /boot/vmlinuz 

При обновлении ядра будет достаточно установить новую версию и обновить символическую ссылку.

Установка документации⚓︎

Установите документацию, если она необходима

 install -d /usr/share/doc/linux-5.14.14 cp -rv Documentation/* /usr/share/doc/linux-5.14.14 

Настройка каталога с исходным кодом⚓︎

Важно отметить, что файлы в каталоге исходных кодов ядра не принадлежат пользователю root . Всякий раз, когда пакет распаковывается от пользователя root (как это и выполнялось внутри среды chroot ), файлы имеют те идентификаторы пользователя и группы, которые были назначены при распаковке. Обычно это не вызывает проблем для других устанавливаемых пакетов, так как каталог с исходными кодами удаляется после установки пакета. Однако исходный код ядра Linux часто сохраняется в течение длительного времени. Из-за этого существует вероятность того, что идентификатор пользователя, используемый при распаковке, будет назначен другому пользователю. В таком случае этот пользователь будет иметь доступ на запись в этот каталог.

Во многих случаях конфигурация ядра должна быть обновлена для пакетов, которые

будут установлены вами позднее. В отличие от других пакетов удалять дерево исходного кода ядра не требуется после компиляции и установки.

Если вы планируете оставить каталог с исходным кодом ядра, выполните команду:

 chown -R 0:0 /usr/src/linux-5.14.14 

Заголовочные файлы, расположенные в системном каталоге /usr/include , должны

всегда быть те, которые использовались при компиляции Glibc . Их никогда не следует заменять на чистые заголовочные файлы ядра или любые другие подготовленные заголовочные файлы.

Настройка порядка загрузки модулей Linux⚓︎

Обычно модули Linux загружаются автоматически, но иногда требуется определённый порядок. Программа, которая загружает модули, modprobe или insmod , использует файл /etc/ modprobe.d/usb.conf как раз для этой цели. Этот файл должен быть создан так, что, если USB-драйверы ( ehci_hcd , ohci_hcd и uhci_hcd ) были созданы в виде модулей, то они будут загружены в требуемом порядке: ehci_hcd должен быть загружен до ohci_hcd и uhci_hcd для того, чтобы избежать предупреждений во время загрузки.

Создайте новый файл /etc/modprobe.d/usb.conf , выполнив следующую команду:

 install -v -m755 -d /etc/modprobe.d cat > /etc/modprobe.d/usb.conf "EOF" # Begin /etc/modprobe.d/usb.conf install ohci_hcd /sbin/modprobe ehci_hcd ; /sbin/modprobe -i ohci_hcd ; true install uhci_hcd /sbin/modprobe ehci_hcd ; /sbin/modprobe -i uhci_hcd ; true # End /etc/modprobe.d/usb.conf EOF 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *