На этой странице обсуждаются возможные оптимизации реализации наложения дерева устройств (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 мс), время выполнения DTO libufdt
составляет 10 мс.
Сравнение производительности libufdt
и libfdt
для устройств Pixel. Количество базовых узлов влияет аналогично, но есть следующие различия:
- 500 операций наложения (добавления или переопределения) имеют разницу во времени в 6–8 раз.
- 1000 операций наложения (добавления или переопределения) имеют разницу во времени в 8–10 раз.
Пример с числом добавлений, равным X:
Рисунок 1. Количество добавленных элементов равно X.
Пример с переопределением счетчика, установленным на X:
Рисунок 2. Переопределяющее значение счетчика — X.
libufdt
разработана с использованием некоторых API и структур данных libfdt
. При использовании libufdt
необходимо включить и подключить libfdt
(однако в своём коде вы можете использовать API libfdt
для работы с DTB или DTBO).
libufdt DTO API
Основной 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 версии 1 четыре младших бита поля флагов в 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
- Включите библиотеки. Чтобы использовать
libufdt
, включитеlibfdt
для структур данных и API:#include <libfdt.h> #include <ufdt_overlay.h>
- Загрузите основной 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);
- Наложение DT:
- Используйте
ufdt_install_blob()
для получения заголовка FDT для основного DT:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Вызовите
ufdt_apply_overlay()
в DTO, чтобы получить объединенный DT в формате FDT:merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size);
- Используйте
merged_fdt
для получения размераdtc_totalsize()
:merged_fdt_size = dtc_totalsize(merged_fdt);
- Передайте объединенный 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); }