На этой странице обсуждаются оптимизации, которые вы можете внести в реализацию наложения дерева устройств (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 = "..."; }; |
либуфдт
Хотя последняя libfdt
поддерживает DTO, рекомендуется ли использовать libufdt
для реализации DTO (исходный код AOSP по адресу platform/system/libufdt
). libufdt
создает реальную древовидную структуру (несведенное дерево устройств или ufdt ) из плоского дерева устройств (FDT), поэтому он может улучшить объединение двух файлов .dtb
из O(N 2 ) в O(N), где N — количество узлов в дереве.
Тестирование производительности
По данным внутреннего тестирования Google, использование libufdt
на узлах DT 2405 .dtb
и 283 .dtbo
приводит к получению размеров файлов 70 618 и 8 566 байт после компиляции. По сравнению с реализацией DTO , перенесенной из FreeBSD (время выполнения 124 мс), время выполнения DTO libufdt
составляет 10 мс.
Тестирование производительности устройств Pixel сравнивало libufdt
и libfdt
. Эффект количества базовых узлов аналогичен, но включает в себя следующие различия:
- 500 операций наложения (добавления или переопределения) имеют разницу во времени от 6 до 8 раз.
- 1000 операций наложения (добавления или переопределения) имеют разницу во времени от 8 до 10 раз.
Пример с добавлением счетчика, установленного в X:
Рисунок 1. Добавляемый счетчик равен X.
Пример с переопределяющим счетчиком, установленным на X:
Рисунок 2. Переопределяющее значение равно X.
libufdt
разработан с использованием некоторых API-интерфейсов libfdt
и структур данных. При использовании libufdt
вы должны включить и связать libfdt
(однако в вашем коде вы можете использовать API libfdt
для работы с DTB или DTBO).
API-интерфейс libufdt DTO
Основной 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
— это основное ОУ, а overlay_fdt
— это буфер, содержащий содержимое файла .dtbo
. Возвращаемое значение — это новый буфер, содержащий объединенное ОУ (или null
в случае ошибки). Объединенное DT форматируется в FDT, которое вы можете передать ядру при запуске ядра.
Новый буфер из возвращаемого значения создается с помощью dto_malloc()
, который вы должны реализовать при портировании libufdt
в загрузчик. Справочные реализации см. в sysdeps/libufdt_sysdeps_*.c
.
Ограничения корневого узла
Вы не можете наложить новый узел или свойство на корневой узел основного ОУ, поскольку операции наложения зависят от меток. Поскольку основное ОУ должно определять метку, а ОУ наложения назначает узлы, на которые будут наложены метки, вы не можете задать метку для корневого узла (и, следовательно, не можете наложить корневой узел).
Поставщики SoC должны определить возможность наложения основного ОУ; 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 при использовании версии 1 заголовка таблицы DT. При использовании заголовка 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>
- Загрузите основное ОУ и наложенное ОУ. Загрузите
.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); }