На этой странице обсуждаются оптимизации, которые вы можете применить к реализации наложения дерева устройств (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
- Включить библиотеки. Чтобы использовать
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); }