Загрузочные разделы поставщика

В Android 11 появилась концепция универсального образа ядра (GKI). Чтобы разрешить загрузку произвольного устройства с помощью GKI, устройства Android 11 могут использовать заголовок загрузочного образа версии 3. В версии 3 вся информация, относящаяся к определенному поставщику, удаляется из раздела boot и переносится в новый раздел vendor_boot. Устройство ARM64 с Android 11 и ядром Linux 5.4 должно поддерживать раздел vendor_boot и обновленный формат раздела boot, чтобы пройти тестирование с помощью GKI.

На устройствах с Android 12 можно использовать заголовок загрузочного образа версии 4, который поддерживает включение нескольких виртуальных дисков поставщика в раздел vendor_boot. Несколько фрагментов виртуального диска поставщика объединяются один за другим в разделе виртуального диска поставщика. Таблица ramdisk поставщика используется для описания структуры раздела ramdisk поставщика и метаданных каждого фрагмента ramdisk поставщика.

Структура разделов

Раздел vendor_boot обновляется с помощью виртуальной системы A/B и защищен проверкой при запуске Android.

Версия 3

Раздел состоит из заголовка, виртуального диска поставщика и дерева устройств (DTB).

Раздел Количество страниц
Заголовок загрузки поставщика (n страниц) n = (2112 + page_size - 1) / page_size
Ramdisk поставщика (o страниц) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p страниц) p = (dtb_size + page_size - 1) / page_size

Версия 4

Раздел состоит из заголовка, раздела ramdisk поставщика (состоящего из всех фрагментов ramdisk поставщика, объединенных вместе), большого двоичного объекта дерева устройств (DTB) и таблицы ramdisk поставщика.

Раздел Количество страниц
Заголовок загрузки от поставщика (n страниц) n = (2128 + page_size - 1) / page_size
Фрагменты ramdisk поставщика (o страниц) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p страниц) p = (dtb_size + page_size - 1) / page_size
Таблица виртуального диска поставщика (страницы q) q = (vendor_ramdisk_table_size + page_size - 1) / page_size
Bootconfig (r страниц) r = (bootconfig_size + page_size - 1) / page_size

Заголовок загрузки поставщика

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

Версия 3

struct vendor_boot_img_hdr_v3
{
#define VENDOR_BOOT_MAGIC_SIZE 8
    uint8_t magic[VENDOR_BOOT_MAGIC_SIZE];
    uint32_t header_version;
    uint32_t page_size;           /* flash page size we assume */

    uint32_t kernel_addr;         /* physical load addr */
    uint32_t ramdisk_addr;        /* physical load addr */

    uint32_t vendor_ramdisk_size; /* size in bytes */

#define VENDOR_BOOT_ARGS_SIZE 2048
    uint8_t cmdline[VENDOR_BOOT_ARGS_SIZE];

    uint32_t tags_addr;           /* physical addr for kernel tags */

#define VENDOR_BOOT_NAME_SIZE 16
    uint8_t name[VENDOR_BOOT_NAME_SIZE]; /* asciiz product name */
    uint32_t header_size;         /* size of vendor boot image header in
                                   * bytes */
    uint32_t dtb_size;            /* size of dtb image */
    uint64_t dtb_addr;            /* physical load address */

};

Версия 4

struct vendor_boot_img_hdr_v4
{
#define VENDOR_BOOT_MAGIC_SIZE 8
    uint8_t magic[VENDOR_BOOT_MAGIC_SIZE];
    uint32_t header_version;
    uint32_t page_size;           /* flash page size we assume */

    uint32_t kernel_addr;         /* physical load addr */
    uint32_t ramdisk_addr;        /* physical load addr */

    uint32_t vendor_ramdisk_size; /* size in bytes */

#define VENDOR_BOOT_ARGS_SIZE 2048
    uint8_t cmdline[VENDOR_BOOT_ARGS_SIZE];

    uint32_t tags_addr;           /* physical addr for kernel tags */

#define VENDOR_BOOT_NAME_SIZE 16
    uint8_t name[VENDOR_BOOT_NAME_SIZE]; /* asciiz product name */
    uint32_t header_size;         /* size of vendor boot image header in
                                   * bytes */
    uint32_t dtb_size;            /* size of dtb image */
    uint64_t dtb_addr;            /* physical load address */

    uint32_t vendor_ramdisk_table_size; /* size in bytes for the vendor ramdisk table */
    uint32_t vendor_ramdisk_table_entry_num; /* number of entries in the vendor ramdisk table */
    uint32_t vendor_ramdisk_table_entry_size; /* size in bytes for a vendor ramdisk table entry */
    uint32_t bootconfig_size; /* size in bytes for the bootconfig section */
};

#define VENDOR_RAMDISK_TYPE_NONE 0
#define VENDOR_RAMDISK_TYPE_PLATFORM 1
#define VENDOR_RAMDISK_TYPE_RECOVERY 2
#define VENDOR_RAMDISK_TYPE_DLKM 3

struct vendor_ramdisk_table_entry_v4
{
    uint32_t ramdisk_size; /* size in bytes for the ramdisk image */
    uint32_t ramdisk_offset; /* offset to the ramdisk image in vendor ramdisk section */
    uint32_t ramdisk_type; /* type of the ramdisk */
#define VENDOR_RAMDISK_NAME_SIZE 32
    uint8_t ramdisk_name[VENDOR_RAMDISK_NAME_SIZE]; /* asciiz ramdisk name */

#define VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE 16
    // Hardware identifiers describing the board, soc or platform which this
    // ramdisk is intended to be loaded on.
    uint32_t board_id[VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE];
};
  • vendor_ramdisk_size – общий размер всех фрагментов RAM-диска поставщика.
  • ramdisk_type – тип виртуального диска. Возможные значения:
    • VENDOR_RAMDISK_TYPE_NONE указывает, что значение не задано.
    • VENDOR_RAMDISK_TYPE_PLATFORM ramdisks содержат данные, относящиеся к определенной платформе. Загрузчик всегда должен загружать их в память.
    • VENDOR_RAMDISK_TYPE_RECOVERY ramdisk содержит ресурсы для восстановления. Загрузчик должен загрузить их в память при запуске режима восстановления.
    • VENDOR_RAMDISK_TYPE_DLKM ramdisks contain dynamic loadable kernel modules.
  • ramdisk_name – уникальное название RAM-диска.
  • board_id – это вектор идентификаторов оборудования, заданных поставщиком.

Поддержка загрузчика ОС

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

Загрузчик должен загрузить общий ramdisk в память сразу после ramdisk поставщика (форматы CPIO, Gzip и lz4 поддерживают такую конкатенацию). Не выравнивайте по страницам универсальный образ RAM-диска и не добавляйте никаких других пробелов между ним и концом RAM-диска поставщика в памяти. После декомпрессии ядра оно извлекает объединенный файл в initramfs, в результате чего получается файловая структура, представляющая собой общий виртуальный диск, наложенный на файловую структуру виртуального диска поставщика.

Поскольку общий виртуальный диск и виртуальный диск поставщика объединяются, они должны быть в одном формате. В загрузочном образе GKI используется сжатый по алгоритму LZ4 универсальный RAM-диск, поэтому устройство, совместимое с GKI, должно использовать сжатый по алгоритму LZ4 RAM-диск поставщика. Ниже показана конфигурация для этого.

Требования к загрузчику для поддержки bootconfig описаны в разделе Реализация bootconfig.

Несколько виртуальных дисков от поставщиков (версия 4)

При использовании заголовка образа загрузки версии 4 загрузчик может выбрать для загрузки в качестве initramfs во время загрузки подмножество или все образы RAM-диска поставщика. Таблица RAM-дисков поставщика содержит метаданные каждого RAM-диска и может помочь загрузчику операционной системы решить, какие RAM-диски загружать. Загрузчик может выбирать порядок загрузки выбранных ramdisk поставщика, если общий ramdisk загружается последним.

Например, чтобы сэкономить ресурсы, загрузчик операционной системы может не загружать RAM-диски поставщика типа VENDOR_RAMDISK_TYPE_RECOVERY во время обычной загрузки. В этом случае в память загружаются только RAM-диски поставщика типов VENDOR_RAMDISK_TYPE_PLATFORM и VENDOR_RAMDISK_TYPE_DLKM. С другой стороны, vendor ramdisk типов VENDOR_RAMDISK_TYPE_PLATFORM, VENDOR_RAMDISK_TYPE_RECOVERY и VENDOR_RAMDISK_TYPE_DLKM загружаются в память при загрузке в режиме восстановления.

Кроме того, загрузчик может игнорировать таблицу RAM-диска поставщика и загружать весь раздел RAM-диска поставщика. Это имеет тот же эффект, что и загрузка всех фрагментов ramdisk поставщика в раздел vendor_boot.

Как обеспечить поддержку

Чтобы реализовать поддержку загрузки поставщика для устройства:

  • Установите для параметра BOARD_BOOT_HEADER_VERSION значение 3 или больше.

  • Установите для параметра BOARD_RAMDISK_USE_LZ4 значение true, если ваше устройство соответствует требованиям GKI или использует сжатый по алгоритму lz4 универсальный RAM-диск.

  • Установите для параметра BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE подходящий размер, учитывая модули ядра, которые должны быть размещены на RAM-диске поставщика.

  • Обновите AB_OTA_PARTITIONS, чтобы включить vendor_boot и любые списки разделов OTA на устройстве, относящиеся к конкретному поставщику.

  • Скопируйте устройство fstab в /first_stage_ramdisk в разделе vendor_boot, а не в разделе boot. Пример:$(LOCAL_PATH)/fstab.hardware:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.$(PRODUCT_PLATFORM).

Чтобы включить в vendor_boot несколько образов ОЗУ поставщиков:

  • Задайте для параметра BOARD_BOOT_HEADER_VERSION значение 4.
  • Установите для BOARD_VENDOR_RAMDISK_FRAGMENTS список логических имен фрагментов ramdisk поставщика, которые будут включены в vendor_boot.

  • Чтобы добавить готовый образ RAM-диска поставщика, задайте для параметра BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILT путь к готовому образу.

  • Чтобы добавить ramdisk поставщика DLKM, задайте для параметра BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRS список каталогов модулей ядра, которые нужно включить.

  • Задайте для параметра BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGS аргументы mkbootimg. Это аргументы --board_id[0-15] и --ramdisk_type для фрагмента ramdisk поставщика. Для виртуального диска поставщика DLKM значением --ramdisk_type по умолчанию будет DLKM, если не указано иное.

Чтобы создать ресурсы для восстановления в виде отдельного образа диска recovery в vendor_boot:

  • Задайте для параметра BOARD_BOOT_HEADER_VERSION значение 4.
  • Задайте для параметра BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT значение true.
  • Задайте для параметра BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT значение true.
  • Это добавит фрагмент ramdisk поставщика, у которого ramdisk_name – recovery, а ramdisk_type – VENDOR_RAMDISK_TYPE_RECOVERY. Затем виртуальный диск содержит все файлы восстановления, которые устанавливаются в $(TARGET_RECOVERY_ROOT_OUT).

Аргументы mkbootimg

Аргумент Описание
--ramdisk_type Тип RAM-диска. Возможные варианты: NONE, PLATFORM, RECOVERY или DLKM.
--board_id[0-15] Укажите вектор board_id. По умолчанию используется вектор 0.

Пример конфигурации:

BOARD_KERNEL_MODULE_DIRS := foo bar baz
BOARD_BOOT_HEADER_VERSION := 4
BOARD_VENDOR_RAMDISK_FRAGMENTS := dlkm_foobar
BOARD_VENDOR_RAMDISK_FRAGMENT.dlkm_foobar.KERNEL_MODULE_DIRS := foo bar
BOARD_VENDOR_RAMDISK_FRAGMENT.dlkm_foobar.MKBOOTIMG_ARGS := --board_id0 0xF00BA5 --board_id1 0xC0FFEE

В результате vendor_boot будет содержать два фрагмента ramdisk поставщика. Первый из них – это "стандартный" RAM-диск, который содержит каталог DLKM baz и остальные файлы в $(TARGET_VENDOR_RAMDISK_OUT). Второй – это ramdisk dlkm_foobar, который содержит каталоги DLKM foo и bar, а значение по умолчанию для --ramdisk_type – DLKM.