Как загружается Raspberry Pi

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

Это детальное описание процесса загрузки Raspberry Pi, собранное из различных источников, но в основном с официального форума. Прежде вам нужно знать, что RPi не загружается как обычный настольный компьютер. Видеоядро (VIdeoCore), или графический процессор, на самом деле загружается прежде, чем запускается процессор ARM.

SoC (система на чипе) содержит центральный процессор ARM, графический процессор, ПЗУ, ОЗУ и много других вещей. Можно представить, что SoC – это как ваши материнская плата и процессор объединённые вместе в единый чип.

Когда вы включаете вашу Raspberry Pi, первые биты кода запускаются из ПЗУ, который встраивается в чип в процессе изготовления RPi. Это, так называемая, первая стадия бутлоадера. SoC жестко запрограммирован для запуска этого кода при запуске на небольшом ядре RISC (процессор с сокращенным набором команд). Он используется для монтирования загрузочного раздела FAT32 на вашей SD-карте, чтобы вторая стадия бутлоадера стала доступной. Что из себя представляет вторая стадия бутлоадера, которая хранится на SD-карте? Это файл bootcode.bin. Вы могли видеть этот файл, если смотрели содержимое SD-карты в Windows. В Raspberry Pi 4 этот файл хранится в EEPROM объёмом 512KB. А теперь кое-что любопытное. Первая стадия бутлоадера до сих пор не инициализировала ваш процессор ARM (то есть процессор находится в сброшенном состоянии) или ваше ОЗУ. Таким образом, вторая стадия бутлоадера также запускается на GPU. Файл bootloader.bin загружается в 4-полосный ассоциативный кэш L2 GPU объёмом 128 КБ и затем выполняется. Он инициализирует ОЗУ и загружает файл start.elf, который так же расположен на SD-карте. Это третья стадия бутлоадера и она является самой важной. Это прошивка GPU, то есть она содержит настройки или в нашем случае содержит инструкции по загрузке настроек из файла config.txt, который также находится на SD-карте. В файле config.txt прописываются различные опции для загрузки системы, например, использование overscan, адрес загрузки ядра, режим работы HDMI и т.д. Этот файл служит примерно для тех же целей, что и меню BIOS на настольном компьютере.

Файл start.elf также разделяет ОЗУ между GPU и CPU. Как память будет распределяться прописано в файле fixup.dat. Файл fixup.dat необходим для корректной работы Raspberry Pi 3B. Без этого файла системе будут доступны только 256 Мб оперативной памяти. ARM имеет доступ только к адресному пространству, оставленному адресным пространством GPU. Например, если под GPU отведены адреса 0x0000F000 – 0x0000FFFF, то ARM будет иметь доступ к адресам 0x00000000 – 0x0000EFFF. (Это не реальные диапазоны, это просто пример использования.) Физические адреса, воспринимаемые ядром ARM, фактически проецируются MMU (блоком управления памятью) видеоядра на другой адрес в видеоядре (0xC0000000 и выше). Файл config.txt загружается после того, как разделение памяти уже произошло, таким образом вы не можете задать значение этого разделения в файле config.txt. Однако на SD-карте находятся другие elf-файлы, которые по-разному делят память. Таким образом, в зависимости от ваших нужд, вы можете переименовать эти файлы в start.elf и с помощью этого файла загружать RPi. В современной прошивке эту функциональность сделали в динамическом режиме. В Pi GPU – Царь и Бог!

  • start.elf (start4.elf) – основная прошивка
  • start_x.elf (start4x.elf) включает драйвера камеры и кодек,
  • start_db.elf (start4db.elf) – отладочная версия прошивки
  • start_cd.elf + fixup_cd.dat (start4cd.elf + fixup4cd.dat) – это обрезанная версия без поддержки аппаратных блоков, таких как кодеки и 3D, для использования когда в файле config.txt задана опция gpu_mem=16

Кроме этого на этом же этапе загружается дерево устройства – файл с расширением .dtb. В этом файле описываются параметры встроенных устройств и степень возможного управления ими. start.elf подбирает и загружает нужный dtb-файл согласно модели и ревизии Raspberry Pi, на которой осуществляется запуск.

Вот некоторые из деревьев и соответствующие им модели Raspberry Pi:

  • bcm2708-rpi-0-w.dtb: Pi Zero W
  • bcm2708-rpi-b.dtb: Pi Model B and Model A
  • bcm2708-rpi-b-plus.dtb: Pi B+, A+ and Zero
  • bcm2708-rpi-cm.dtb: Compute Module (a minimal dtb, intended to be a starting point)
  • bcm2709-rpi-2-b.dtb: Pi 2B
  • bcm2710-rpi-3-b.dtb: Pi 3B
  • bcm2710-rpi-3-b-plus.dtb: Pi 3B+ and 3A+
  • bcm2710-rpi-cm3.dtb:Compute Module 3

Помимо загрузки config.txt и разделения ОЗУ, start.elf также загружает файл cmdline.txt, если он существует. Он содержит параметры командной строки для любого ядра, которое должно быть загружено. Это подводит нас к заключительному этапу процесса загрузки. Наконец, start.elf загружает kernel.img, двоичный файл, содержащий ядро ОС, и выполняет сброс CPU. Затем процессор ARM выполняет все инструкции, содержащиеся в файле kernel.img, тем самым загружая операционную систему. Это может быть ядро Linux, какой-то другой загрузчик (например, U-Boot) или приложение bare metal.

start.elf загружает kernel.img. Затем он читает config.txt, cmdline.txt и bcm2835.dtb

Есть файл dtb существует, он загружается по адресу 0x100, в противном случае адрес 0x100 заполняется данными ATAGs. В обоих случаях kernel загружается по адресу 0×8000. Если же в файле конфигурации установлена переменная disable_commandline_tags, то kernel загружается по адресу 0x0.

kernel.img по умолчанию загружается по адресу 0x8000 на 32-х разрядной системе и по адресу 0x80000 на 64-х разрядной. Однако если в config.txt переменная kernel_old установлена в 1, то kernel.img будет загружен по адресу 0x0. Можно также задать и свой адрес загрузки, указав его в переменной kernel_address файла конфигурации.

Существует 4 разновидности ядер, которые соответствуют разным типам процессоров. Загрузчик ищет и загружает максимально возможное ядро, подходящее для конкретной модели Raspberry Pi, и если не находит его, то переходит к следующему подходящему ядру.

  • kernel8.img — для многоядерного ARMv8, загрузка в 64-битном режиме (Pi 4, Pi 3 и Pi 2B rev1.2)
  • kernel7l.img — для многоядерного ARMv7, загрузка в 32-битном режиме (Pi 4)
  • kernel7.img — для многоядерного ARMv7, загрузка в 32-битном режиме (Pi 2, Pi 3 и Compute Module 3)
  • kernel.img — для одноядерного ARMv6, загрузка в 32-битном режиме (Pi 1, Pi Zero и Compute Module)

Например, на Raspberry Pi 3 загрузчик прежде всего будет пытаться загрузить kernel8.img, а если его не окажется на носителе, то следующим будет kernel7.img. Если же загрузчик не найдёт и его, то загрузка на этом прекратится, а мы так и продолжим созерцать на экране большой цветной квадрат.

Кроме того, имя kernel*.img можно изменить на другое, прописав его в файле config.txt, например:

[pi2]
kernel=uboot_rpi_2.bin

Важно знать, что после запуска операционной системы код GPU не выгружается. По сути, start.elf — это не просто прошивка GPU, а проприетарная операционная система под названием VideoCore OS (ThreadX OS). Когда обычной ОС (Linux) требуется элемент, к которому она напрямую не имеет доступа, Linux взаимодействует с VCOS, используя систему обмена сообщениями почтового ящика (mailbox messaging system).

Когда GPU позволит CPU загрузить ядро Linux, он не просто уходит со сцены, работая лишь как графический процессор. Нет, GPU по-прежнему главный. Вы когда-нибудь думали, кто выводит эти логотипы, когда Pi подключается к HDMI? Или эти символы молнии или температуры в предупреждающих значках? Вот именно, это делает система ThreadX на GPU, а Linux вообще не знает, что происходит.

Для большей наглядности привожу этапы загрузки в таблице:

ЭтапИсточник данныхГде хранитсяКуда загружаетсяЧем выполняетсяЧто делает
IмикрокодROMROMGPUМонтирует загрузочный раздел FAT32 SD-карты
IIbootcode.binSD (EEPROM для RPi 4)L2 кэшGPUИнициализирует ОЗУ и загружает туда файл start.elf
IIIstart.elfSDRAMGPUЭто проприетарная прошивка GPU — ThreadX OS:
— делит память между GPU и CPU
— читает файл настроек config.txt
— загружает файл kernel.img
— читает файл cmdline.txt, если он существует
— выполняет сброс CPU, т.е. включает его
IVkernel.imgSDRAMCPUИсполняет код для процессора ARM

Оставить ответ

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

Защита от спама * Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.