Загрузчик U-Boot

О документе

Данный документ описывает особенности работы загрузчика U-Boot для модулей на базе микросхемы 1892ВА018 (aka СКИФ, Scythian, MCom-03).

Возможности

  • Поддержка модулей на базе MCom-03:

    • ROCK Pi N10 с установленным процессорным модулем ELV-MC03-SMARC r1.1;

    • NGFW-CB r1.0 с установленным процессорным модулем ELV-MC03-SMARC r1.1;

    • ELV-SMARC-CB r1.0 с установленным процессорным модулем ELV-MC03-SMARC r1.1;

    • ELV-SMARC-CB r2.10.3 с установленным процессорным модулем ELV-MC03-SMARC r1.1;

    • ELV-SMARC-CB r3.2.1 с установленным процессорным модулем ELV-MC03-SMARC r1.1;

    • ELV-SMARC-CB r3.2.1 с установленным процессорным модулем ELV-MC03-SMARC r2.7.1;

    • Трастфон-Э ОПН r1.1 с установленным процессорным модулем Трастфон-Э ПМ r1.5;

    • MCom-03 BuB r1.3.x, r1.5.x (aka IPCom BuB).

  • Загрузчик исполняется на ARM CPU.

  • Поддержка загрузки ядра Linux по схеме Generic Distro и UEFI.

  • Поддержка загрузки PXE.

  • Поддержка UART0.

  • Поддержка GEMAC0, GEMAC1 (10/100/1000 Мбит/с).

  • Поддержка SDMMC0, SDMMC1.

  • Поддержка QSPI0, QSPI1.

  • Поддержка USB 2.0 и 3.0.

  • Поддержка переменных окружения в памятях QSPI0, QSPI1.

  • Поддержка заводских настроек.

  • Поддержка автоопределения DTB Linux на основе ID EEPROM платы-носителя SMARC.

Исходный код

Git-репозиторий исходного кода загрузчика — https://github.com/elvees/u-boot/tree/mcom03.

Основные файлы поддержки микросхемы:

  • arch/arm/dts/mcom03*.dts,

  • board/elvees/,

  • configs/mcom03*_defconfig,

  • include/configs/mcom03.h.

Конфигурация и сборка

В U-Boot используется единый файл конфигурации для всех поддерживаемых модулей — mcom03_defconfig.

Результатом сборки исходных кодов загрузчика является образ u-boot.bin и набор бинарных файлов Device Tree (DTB). Образ доступен в корневой директории загрузчика после завершения сборки, а DTB — в директории arch/arm/dts.

Примечание

Загрузчик, выполняемый до U-Boot, должен загрузить DTB для соответствующей платы в DDR по предопределённому адресу, после чего U-Boot считает DTB из этого адреса.

Для сборки загрузчика на ПК требуется кросс-компилятор aarch64-linux-gnu.

Переменные окружения, влияющие на сборку загрузчика:

  • CROSS_COMPILE — префикс кросс-компилятора;

  • PATH — пути для поиска используемых приложений.

Официальная инструкция по сборке U-Boot https://u-boot.readthedocs.io/en/latest/build/gcc.html.

Пример сборки загрузчика для модуля MCom-03 BuB r1.3.0:

export PATH=<compiler-path>:$PATH
export CROSS_COMPILE=aarch64-linux-
make mcom03_defconfig
make -j $(nproc)

Загрузка ядра Linux

Выбор DTB ядра

Выбор DTB Linux для загрузки ядра осуществляется путём установки U-Boot переменной board по следующему алгоритму:

../_images/u-boot-env-board.svg

Примечание

  • ID EEPROM платы-носителя согласно спецификации SMARC HW Specification Version 2.1;

  • на ROCK Pi N10 отсутствует ID EEPROM.

Внимание

Значение параметра FDT в extlinux.conf имеет приоритет выше, чем значение переменной board. Подробнее см. Прошивка SD-карты.

Для модулей ELV-MC03-SMARC поддерживаются два варианта загрузки Linux:

  • с указанием платы-носителя в свойстве compatible DTS U-Boot. Данный способ не отличается от запуска других модулей и требует прошивки SBL с нужным DTB U-Boot. Например, для запуска ELV-MC03-SMARC r1.0 на ELV-SMARC-CB r3.3.0 необходимо использовать sbl-xip-mcom03-elvmc03smarc-r1.0-elvsmarccb-r3.3.0.bin.

  • с использованием автоматического определения платы-носителя.

Автоматическое определение плат-носителей SMARC

Для поддержки загрузки SMARC-модуля на произвольной плате-носителе необходимо:

  1. прошить QSPI-память SMARC-модуля файлом sbl-xip-mcom03-elvmc03smarc-<revision>-smarccommoncb-r1.0.bin, где <revision> — ревизия модуля SMARC;

  2. прошить ID EEPROM платы-носителя строкой с её наименованием.

Имя платы носителя можно получить из имени DTB-файла ядра Linux. Например, для ELV-SMARC-CB r3.3.0 в ID EEPROM необходимо записать строку elvsmarccb-r3.3.0:

/boot/elvees/mcom03-elvmc03smarc-r2.9.1-elvsmarccb-r3.3.0.dtb
                                        ^^^^^^^^^^^^^^^^^

Примечание

DTB в директории /boot/elvees создаются из соответствующих DTS.

Выбор метода загрузки

U-Boot выполняет поиск загрузочных разделов (разделы имеющее флаг boot) по приоритету описанному в переменной boot_targets (usb, mmc1, mmc0 и т. д.). В найденном загрузочном разделе производится поиск файла /boot/extlinux/extlinux.conf. Если файл найден, U-Boot загружает ядро методом Generic Distro. Если /boot/extlinux/extlinux.conf не найден, то U-Boot проверяет тип загрузочного раздела. Если тип соответствует ESP, то начинается загрузка по UEFI. В противном случае выполняется попытка загрузки со следующего загрузочного раздела.

Generic Distro (extlinux.conf)

Подробнее см. Generic Distro. Порядок загрузки:

  • SD-карта (или eMMC, USB-накопитель) содержит файл extlinux.conf, образы ядер Linux, DTB-файлы, rootfs.

  • U-Boot перебирает источники загрузки SDMMC0, SDMMC1, USB, монтирует файловые системы с носителей подключенных к SD-контроллерам MCom-03, USB-контроллерам, проверяет наличие extlinux.conf. Порядок носителей определяется переменной boot_targets.

  • U-Boot запускает ядро в соответствии с первым обнаруженным extlinux.conf (копирует DTB и ядро в ОЗУ, передаёт управление ядру).

В схеме загрузки Distro нет стандартного механизма, с помощью которого можно зафиксировать загрузку с определенного устройства на шине USB. При наличии нескольких подключенных USB-накопителей на одной шине USB при отсутствии загрузочного раздела попытки загрузки прекращаются после первой неудачи. Подробнее см. описание проблемы на GitHub.

UEFI

Подробнее см. UEFI on U-Boot, UEFI Global Variables. Порядок загрузки:

  • U-Boot ищет файл ubootefi.var (хранилище UEFI-переменных).

  • Если файл ubootefi.var найден, то U-Boot загружает файл согласно приоритету загрузки, указанному в переменной BootOrder. Иначе U-Boot загружает fallback file \EFI\BOOT\bootaa64.efi (загрузчик ОС и т. п.)

  • U-Boot загружает нужный DTB ядра из директории dtb в корне ESP.

  • U-Boot передает управление приложению UEFI.

Если на нескольких блочных устройствах будет хотя бы по одному ESP, то загрузка UEFI-приложения всегда будет производиться с первого найденного ESP-раздела c учетом приоритета загрузки, описанного в переменной boot_targets.

Карта памяти

Ниже представлена диаграмма, на которой изображена карта памяти на разных этапах загрузки Linux.

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

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

../_images/u-boot-layout.svg

Переменные окружения

Загрузчик поддерживает настройку через переменные окружения.

Во время запуска загрузчик выполняет поиск переменных окружения, сохраненных в QSPI флеш-памяти:

  • при успешном обнаружении устанавливаются переменные окружения, сохраненные в QSPI флеш-памяти;

  • в случае ошибки устанавливаются переменные окружения, заданные по умолчанию в коде U-Boot (см. файл include/configs/mcom03.h).

U-Boot использует переменную first_boot_checker для определения первой загрузки после перепрошивки. В случае, если переменная не определена, то U-Boot создаёт переменную и сохраняет все переменные окружения в секцию переменных окружения в QSPI. Принудительное сохранение переменных окружения необходимо для чтения/записи переменных из ОС (утилиты fw_setenv/fw_printenv).

В MCom-03 U-Boot определен ряд переменных окружения, которые могут быть установлены через заводские настройки, переопределены пользователем или через U-Boot DTB:

board

Имя модуля, используемое для выбора Linux DTB.

Данная переменная устанавливается автоматически, подробнее см. Выбор DTB ядра.

board_override

Имя модуля, заданное пользователем. См. board. Переменная предназначена для отладки и не должна использоваться в готовых изделиях.

ethaddr

MAC-адрес контроллера Ethernet.

При наличии переменной, U-Boot использует указанный адрес для инициализации контроллерa Ethernet и добавляет свойство local-mac-address в узел контроллерa внутри Device Tree. Поиск узла осуществляется посредством псевдонима ethernet узла aliases. Таким образом, U-Boot и Linux используют один MAC-адрес во время работы. При отсутствии переменной U-Boot и Linux генерируют произвольные MAC-адреса независимо.

При загрузке модуля выполняется чтение заводских настроек и анализ наличия переменной factory_eth0_mac. Если factory_eth0_mac существует, а ethaddr не существует, то ethaddr создаётся и заполняется значением factory_eth0_mac. В противном случае ethaddr не изменяется.

ethaddr1

MAC-адрес второго контроллера Ethernet. Переменная настраивается аналогично ethaddr, при анализе заводских настроек используется factory_eth1_mac.

factory_board

Имя модуля из заводских настроек.

Переменная создаётся динамически и не должна изменяться пользователем. Переменная не используется в случае отсутствия заводских настроек. См. board.

factory_eth0_mac

MAC-адрес Ethernet из заводских настроек.

Переменная создаётся динамически и не должна изменяться пользователем. Переменная не используется в случае отсутствия заводских настроек. См. ethaddr.

Важно

MAC-адрес должен использовать двоеточие для разделения групп октетов (AA:BB:CC:DD:EE:FF), в противном случае поведение загрузчика неопределено.

factory_serial

Серийный номер модуля из заводских настроек.

Переменная создаётся динамически и не должна изменяться пользователем. Переменная не используется в случае отсутствия заводских настроек. См. также serial#.

serial#

Серийный номер модуля.

Значение переменной записывается в свойство serial-number в DTB (свойство доступно в Linux). При загрузке модуля выполняется чтение заводских настроек и анализ наличия переменной factory_serial. Если factory_serial существует, а serial# не существует, то serial# создаётся и заполняется значением factory_serial. В противном случае serial# не изменяется.

Секция переменных окружения находится в предпоследнем 64 килобайтном секторе флеш-памяти.

Примеры «сброса» секции переменных окружения:

  • Для очистки секции переменных окружения с использованием mcom03-flash-tools на модуле с флеш-памятью размером 16 МБ:

    mcom03-flash --port /dev/ttyUSBx erase qspi0 64K --offset 0xFE_0000
    
  • Для копирования переменных по умолчанию в секцию переменных окружения в терминале U-Boot:

    env default -a
    saveenv
    

Заводские настройки

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

Перезапись заводских настроек не должна быть возможна без специальных мер по отключению аппаратной защиты (rescue mode или прошивка специальной версией с отключенной защитой).

Поддержка в U-Boot

Блок настроек хранится в микросхеме eMMC в стандартном разделе Boot partition 0 (далее — boot0). Раздел не имеет таблицы разделов и отформатирован в файловую систему EXT4.

Настройки загрузчика хранятся в текстовом файле uboot-factory.env. В каждой строке файла содержатся пары <переменная>=<значение>. Имена переменных должны иметь префикс factory_ и не должны пересекаться со множеством стандартных переменных U-Boot.

В разделе могут храниться прочие файлы, для которых требуется аппаратно гарантировать неизменяемость во время работы ОС Linux и загрузчика.

Загрузчик считывает настройки из раздела boot0 первого eMMC (mmc 0.1:0) и устанавливает стандартные переменные окружения U-Boot.

Примечание

Переменные factory_* имеют меньший приоритет по сравнению со стандартными переменными.

MMC-устройство с блоком заводских настроек должно обнаруживаться первым. Порядок инициализации должен обеспечиваться в DTS. Например, если eMMC-память с разделом заводских настроек подключена к контроллеру SDMMC1, то в DTS загрузчика для модуля должно выполняться переопределение устройств:

#include "mcom03.dtsi"

/ {
    model = ...;
    compatible = ...;
    aliases {
        mmc0 = &sdhci1;
        ....
    };
};

Примечание

Аналогичное объявление в DTS ядра Linux гарантирует фиксированное имя устройства /dev/mmcblk0boot0 в ОС.

При переменной factory_wp=1 U-Boot включает защиту eMMC Power-On Write Protection boot0 раздела. Защита раздела включается до исполнения команд в терминале U-Boot. Данная переменная игнорируется, если U-Boot собран с опцией CONFIG_MCOM03_EMMC_BOOT_WP_DISABLED=y.

state "boot0 protected" as protected
state "boot0 unprotected" as unprotected

unprotected: U-Boot doesn't set Power-On write protection
protected: U-Boot sets Power-On write protection

[*] -down-> unprotected
unprotected -right-> protected: U-Boot reads factory_wp=1
protected -left-> unprotected: custom U-Boot image is flashed to QSPI

Рис. 2 Состояния защиты блока заводских настроек

Если U-Boot собран с опцией CONFIG_MCOM03_DISABLE_FACTORY=y, то U-Boot не обращается к разделу boot0.

Первичная прошивка настроек

  1. Включить питание устройства.

  2. Дождаться загрузки Linux на устройстве.

  3. Из командной строки Linux на устройстве выполнить команды:

    # by default kernel always mounts MMC boot partition as read-only
    echo 0 > /sys/block/mmcblk0boot0/force_ro
    
    mkfs.ext4 /dev/mmcblk0boot0
    
    mkdir -p /media/factory
    mount -t auto -o rw,noexec,nodev,nosuid,sync /dev/mmcblk0boot0 /media/factory
    
    # NOTE: factory_wp=1 is optional and can be absent
    # NOTE: <MAC-ADDRESS-...> hex groups *must* be colon-separated (AA:BB:CC..),
    #       behavior is undefined otherwise
    cat > /media/factory/uboot-factory.env <<EOF
    factory_wp=1
    factory_board=<board>
    factory_eth0_mac=<MAC-ADDRESS-0>
    factory_eth1_mac=<MAC-ADDRESS-1>
    factory_serial=<SERIAL-NUMBER>
    EOF
    
    sync
    
    umount /media/factory
    

Перепрошивка настроек

  1. Собрать U-Boot c опцией CONFIG_MCOM03_EMMC_BOOT_WP_DISABLED=y.

  2. Прошить QSPI-память.

  3. Сбросить питание устройства.

  4. Выполнить первичную прошивку настроек.

  5. (при необходимости) Прошить QSPI-память образом U-Boot без вышеуказанной опции.

Драйвер QSPI

Драйвер контроллера QSPI реализует ведущее устройство на шине SPI согласно классу UCLASS_SPI (см. doc/driver-model/spi-howto.rst) модели драйверов U-Boot.

Драйвер поддерживает спецификацию ведущего и ведомых устройств на шине SPI согласно doc/device-tree-bindings/spi/spi-bus.txt.

Возможности драйвера:

  • Поддержка до четырех ведомых устройств.

  • Передача в формате CPHA = 0 (см. флаг SPI_CPHA режима).

  • Полярность сигнала синхронизации CPOL = 0 (см. флаг SPI_CPOL режима).

  • Сигнал CS в активном состоянии «логический 0» (см. флаг SPI_CS_HIGH режима).

  • Передача слов данных старшим битом вперед (см. флаг SPI_LSB_FIRST режима).

  • Ведущее устройство на шине SPI (см. флаг SPI_SLAVE режима).

  • Пересылка в режиме full-duplex (в режиме NORMAL):

    • Передача данных по одному выводу на шине SPI (см. флаг SPI_TX_BYTE режима).

    • Прием данных по одному выводу на шине SPI (см. флаг SPI_RX_SLOW режима).

  • Пересылка в режиме half-duplex (в режиме QUAD):

    • Передача данных по 4 линиям на шине SPI (см. флаг SPI_TX_QUAD).

    • Прием данных по 4 линиям на шине SPI (см. флаг SPI_RX_QUAD).

Ограничения драйвера:

  • Не поддерживается установка скорости передачи. Частота на шине SPI равна частоте clk_ext, которая указывается в свойстве clocks в device tree. Частота clk_ext должна быть не больше частоты clk_axi на шине AXI.

  • Размер данных должен быть кратен восьми битам.

  • Передача в формате CPHA = 1 не поддерживается (флаг SPI_CPHA режима должен быть равен 0).

  • Полярность сигнала синхронизации CPOL = 1 не поддерживается (флаг SPI_CPOL режима должен быть равен 0).

  • Сигнал CS в активном состоянии «логическая 1» не поддерживается (флаг SPI_CS_HIGH режима должен быть равен 0).

  • Передача слов данных младшим битом вперед не поддерживается (флаг SPI_LSB_FIRST режима должен быть равен 0).

  • Передача/прием данных по одной линии не поддерживается (флаг SPI_3WIRE режима должен быть равен 0).

  • Режим loopback не поддерживается (флаг SPI_LOOP режима должен быть равен 0).

  • Режим ведомого устройства на шине SPI не поддерживается (флаг SPI_SLAVE режима должен быть равен 0).

  • Пропуск принятых байтов данных перед преамбулой не поддерживается (флаг SPI_PREAMBLE режима должен быть равен 0).

  • Передача данных по двум выводам на шине SPI не поддерживается (флаг SPI_TX_DUAL режима должен быть равен 0).

  • Прием данных по двум выводам на шине SPI не поддерживается (флаг SPI_RX_DUAL режима должен быть равен 0).

При инициализации драйвер выполняет:

  • захват и включение тактового сигнала;

  • вывод контроллера QSPI из состояния сброса;

  • настройку контактных площадок в подсистеме HSPERIPH для внешних выводов контроллера QSPI1 на микросхеме MCom-03;

  • начальную настройку контроллера QSPI.

При удалении драйвер выполняет:

  • выключение тактового сигнала;

  • ввод контроллера QSPI в состояние сброса.

Поддержка ELV-MC03-SMARC

Загрузчик и переменные окружения размещаются в QSPI0. Загрузчик поддерживает нижеперечисленные интерфейсы модуля. Наименования интерфейсов приведены в соответствии со спецификацией SMARC 2.1. В скобках указаны контроллеры MCom-03.

Интерфейс

Примечание

eMMC (SDMMC0)

Поддерживается HS200

GBE0, GBE1

Два порта Ethernet 10/100/1000 Мбит/с

SDIO (SDMMC1)

QSPI0

Память установлена на SMARC-модуле. Поддерживается только однобитный режим

SER1 (UART0)

Консоль MCom-03

USB0, USB1, USB2, USB4, USB5 (USB1)

USB 2.0 или 3.0 в зависимости от порта

При загрузке с использованием автоматического определения плат-носителей доступны только следующие интерфейсы: SDIO, SER1, USB0, USB1 и GBE0.

Поддержка MCom-03 BuB

Загрузчик и переменные окружения размещаются в QSPI0.

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

Интерфейс

Примечание

eMMC

Память eMMC подключена к SDMMC1 MCom-03

Ethernet

Два порта Ethernet 10/100/1000 Мб/с

SD-карта

Поддерживается Default Speed. SD-карта подключена к SDMMC0 MCom-03

QSPI0

Подключена съемная Flash-память. Поддерживается только однобитный режим

QSPI1

Подключена съемная Flash-память. Поддерживается только однобитный режим

UART0

Консоль MCom-03

USB0

Только USB 2.0. Подключен к USB0 MCom-03

USB1

Только USB 2.0. Подключен к USB1 MCom-03

Инструкции по конфигурации

Загрузка Linux по TFTP и NFS

Для загрузки Linux по TFTP и корневой файловой системы по NFS с динамическим получением IP-адреса модулю необходимо:

  1. Настроить на ПК серверы TFTP и NFS:

    • В качестве директории для NFS-сервера использовать смонтированный образ rootfs.ext2.

    • В корне TFTP-сервера необходимо:

      • Скопировать образ ядра Image и директорию elvees с dtb-файлами для ядра.

      • Создать директорию pxelinux.cfg и создать в ней файл default-arm-mcom03 с содержимым:

        TIMEOUT 3
        
        LABEL MCom-03 Buildroot
          MENU LABEL PXE boot
          LINUX /Image
          FDTDIR /elvees
          APPEND console=ttyS0,115200 root=/dev/nfs rw nfsroot=<ip_address>:/,vers=4,tcp ip=dhcp earlycon
        
      • Изменить <ip_address> в файле default-arm-mcom03 на IP-адрес NFS-сервера. (не рекомендуется использовать имя хоста NFS-сервера вместо IP-адреса).

  2. Включить модуль, дождаться загрузки терминала U-Boot, установить IP-адрес TFTP-сервера:

    setenv serverip <ip_address>
    
  3. Установить переменную boot_targets:

    setenv boot_targets pxe
    
  4. (Опционально) Сохранить переменные окружения:

    saveenv
    
  5. Выполнить команду загрузки:

    boot