Оптимизация DTO

На этой странице обсуждаются оптимизации, которые вы можете применить к реализации наложения дерева устройств (DTO), описываются ограничения на наложение корневого узла и подробно описывается, как настроить сжатые наложения в образе DTBO. Также предоставляются примеры инструкций по реализации и кода.

Командная строка ядра

Исходная командная строка ядра в дереве устройств (DT) находится в узле chosen/bootargs . Загрузчик должен объединить это местоположение с другими источниками командной строки ядра:

/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};

DTO не может объединять значения из основного DT и оверлейного DT, поэтому необходимо поместить командную строку ядра основного DT в chosen/bootargs , а командную строку ядра оверлейного DT в chosen/bootargs_ext . Затем загрузчик может объединить эти расположения и передать результат ядру.

main.dts наложение.dts
/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};
/dts-v1/;
/plugin/;

&chosen {
  bootargs_ext = "...";
};

libufdt

Хотя последняя версия libfdt поддерживает DTO, рекомендуется использовать libufdt для реализации DTO (исходный файл AOSP по адресу platform/system/libufdt ). libufdt создает настоящую древовидную структуру (несокращённое дерево устройств, или ufdt ) из сокращённого дерева устройств (FDT), поэтому она может улучшить слияние двух файлов .dtb с O(N 2 ) до O(N), где N — количество узлов в дереве.

Тестирование производительности

В ходе внутреннего тестирования Google использование libufdt на 2405 узлах .dtb и 283 узлах .dtbo DT приводит к размерам файлов 70 618 и 8 566 байт после компиляции. По сравнению с реализацией DTO , перенесенной из FreeBSD (время выполнения 124 мс), время выполнения libufdt DTO составляет 10 мс.

Тестирование производительности для устройств Pixel по сравнению libufdt и libfdt . Количество базовых узлов влияет аналогично, но включает следующие различия:

  • 500 операций наложения (добавления или переопределения) имеют разницу во времени от 6x до 8x
  • 1000 операций наложения (добавления или переопределения) имеют разницу во времени от 8x до 10x

Пример с количеством добавлений, равным X:

Рисунок 1. Количество добавлений равно X.

Пример с переопределяющим счетчиком, установленным на X:

Рисунок 2. Переопределяющее число — X.

libufdt разработан с некоторыми API libfdt и структурами данных. При использовании libufdt необходимо включить и связать libfdt (однако в своем коде вы можете использовать API libfdt для работы с DTB или DTBO).

API DTO libufdt

Основной API для DTO в libufdt выглядит следующим образом:

struct fdt_header *ufdt_apply_overlay(
        struct fdt_header *main_fdt_header,
        size_t main_fdt_size,
        void *overlay_fdt,
        size_t overlay_size);

Параметр main_fdt_header — это основной DT, а overlay_fdt — это буфер, содержащий содержимое файла .dtbo . Возвращаемое значение — это новый буфер, содержащий объединенный DT (или null в случае ошибки). Объединенный DT форматируется в FDT, который можно передать ядру при запуске ядра.

Новый буфер из возвращаемого значения создается dto_malloc() , который следует реализовать при портировании libufdt в загрузчик. Для справочных реализаций обратитесь к sysdeps/libufdt_sysdeps_*.c .

Ограничения корневого узла

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

Поставщики SoC должны определить возможность наложения основного DT; ODM/OEM могут добавлять или переопределять только узлы с метками, определенными поставщиком SoC. В качестве обходного пути вы можете определить узел odm под корневым узлом в базовом DT, что позволит всем узлам ODM в оверлейном DT добавлять новые узлы. В качестве альтернативы вы можете поместить все узлы, связанные с SoC, в базовом DT в узел soc под корневым узлом, как описано ниже:

main.dts наложение.dts
/dts-v1/;

/ {
    compatible = "corp,bar";
    ...

    chosen: chosen {
        bootargs = "...";
    };

    /* nodes for all soc nodes */
    soc {
        ...
        soc_device@0: soc_device@0 {
            compatible = "corp,bar";
            ...
        };
        ...
    };

    odm: odm {
        /* reserved for overlay by odm */
    };
};
/dts-v1/;
/plugin/;

/ {
};

&chosen {
    bootargs_ex = "...";
};

&odm {
    odm_device@0 {
        ...
    };
    ...
};

Использовать сжатые наложения

В Android 9 добавлена ​​поддержка использования сжатых наложений в образе DTBO при использовании заголовка таблицы DT версии 1. При использовании заголовка DTBO v1 четыре младших бита поля флагов в dt_table_entry указывают формат сжатия записи DT.

struct dt_table_entry_v1 {
  uint32_t dt_size;
  uint32_t dt_offset;  /* offset from head of dt_table_header */
  uint32_t id;         /* optional, must be zero if unused */
  uint32_t rev;        /* optional, must be zero if unused */
  uint32_t flags;      /* For version 1 of dt_table_header, the 4 least significant bits
                        of 'flags' are used to indicate the compression
                        format of the DT entry as per the enum 'dt_compression_info' */
  uint32_t custom[3];  /* optional, must be zero if unused */
};

В настоящее время поддерживаются сжатия zlib и gzip .

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

В Android 9 добавлена ​​поддержка тестирования сжатых оверлеев в тест VtsFirmwareDtboVerification , чтобы помочь вам проверить правильность приложения оверлея.

Пример реализации DTO

Следующие инструкции проведут вас через пример реализации DTO с помощью libufdt (пример кода ниже).

Образец инструкции DTO

  1. Включить библиотеки. Чтобы использовать libufdt , включите libfdt для структур данных и API:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
  2. Загрузите основной DT и оверлейный DT. Загрузите .dtb и .dtbo из хранилища в память (точные шаги зависят от вашего дизайна). На этом этапе у вас должен быть буфер и размер .dtb / .dtbo :
    main_size = my_load_main_dtb(main_buf, main_buf_size)
    overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
  3. Наложение DT:
    1. Используйте ufdt_install_blob() , чтобы получить заголовок FDT для основного DT:
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
    2. Вызовите ufdt_apply_overlay() в DTO, чтобы получить объединенный DT в формате FDT:
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
    3. Используйте merged_fdt для получения размера dtc_totalsize() :
      merged_fdt_size = dtc_totalsize(merged_fdt);
    4. Передайте объединенный DT для запуска ядра:
      my_kernel_entry(0, machine_type, merged_fdt);

Пример кода DTO

#include <libfdt.h>
#include <ufdt_overlay.h>



{
  struct fdt_header *main_fdt_header;
  struct fdt_header *merged_fdt;

  /* load main dtb into memory and get the size */
  main_size = my_load_main_dtb(main_buf, main_buf_size);

  /* load overlay dtb into memory and get the size */
  overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);

  /* overlay */
  main_fdt_header = ufdt_install_blob(main_buf, main_size);
  main_fdt_size = main_size;
  merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                  overlay_buf, overlay_size);
  merged_fdt_size = dtc_totalsize(merged_fdt);

  /* pass to kernel */
  my_kernel_entry(0, machine_type, merged_fdt);
}