На этой странице рассказывается, как оптимизировать реализацию наложения дерева устройств (DTO), описываются ограничения на наложение корневого узла и приводятся сведения о том, как настроить сжатые наложения в образе DTBO. Также в нем приведены примеры инструкций по реализации и кода.
Командная строка ядра
Исходная командная строка ядра в дереве устройств (ДУ) находится в узле chosen/bootargs. Загрузчик должен объединить это местоположение с другими источниками командной строки ядра:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
DTO не может объединять значения из основного DT и наложенного DT, поэтому вам нужно поместить командную строку ядра основного DT в chosen/bootargs, а командную строку ядра наложенного DT – в chosen/bootargs_ext. Загрузчик операционной системы может объединить эти местоположения и передать результат ядру.
| main.dts | overlay.dts |
|---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; |
/dts-v1/;
/plugin/;
&chosen {
bootargs_ext = "...";
}; |
libufdt
Хотя последняя версия libfdt поддерживает DTO, для реализации DTO рекомендуется использовать libufdt (исходный код AOSP можно найти на странице platform/system/libufdt). libufdt создает реальную структуру дерева (несжатое дерево устройств, или ufdt) из сжатого дерева устройств (FDT), поэтому может улучшить объединение двух файлов .dtb с O(N2) до O(N), где N – количество узлов в дереве.
Испытание эффективности
Внутреннее тестирование Google показало, что при использовании libufdt на 2405 узлах .dtb и 283 узлах .dtbo размер файла после компиляции составляет 70 618 и 8566 байт соответственно. По сравнению с реализацией DTO, перенесенной из FreeBSD (время выполнения 124 мс), libufdt время выполнения DTO составляет 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).
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 в случае ошибки). Объединенное дерево устройств форматируется в виде плоского дерева устройств, которое можно передать ядру при его запуске.
Новый буфер из возвращаемого значения создается с помощью функции dto_malloc(), которую необходимо реализовать при переносе libufdt в загрузчик.
Примеры реализации можно найти на странице sysdeps/libufdt_sysdeps_*.c.
Ограничения для корневого узла
Нельзя наложить новый узел или свойство на корневой узел основного дерева устройств, поскольку операции наложения зависят от ярлыков. Поскольку в основном ДУ должен быть определен ярлык, а в оверлейном ДУ узлам, которые будут перекрыты, назначаются ярлыки, вы не можете указать ярлык для корневого узла (и, следовательно, не можете перекрыть корневой узел).
Поставщики SoC должны определить возможность наложения основного ДУ; ODM/OEM могут только добавлять или переопределять узлы с ярлыками, заданными поставщиком SoC. В качестве обходного решения вы можете определить узел odm под корневым узлом в базовом ДУ, чтобы все узлы ODM в наложенном ДУ могли добавлять новые узлы.
Вы также можете поместить все узлы, связанные с SoC, в базовый DT в узел soc под корневым узлом, как описано ниже:
| main.dts | overlay.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 версии 1 четыре младших бита поля flags в 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 с помощью libufdt (образец кода см. ниже).
Инструкции по работе с примером объекта передачи данных
- Включить библиотеки. Чтобы использовать
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);
- Наложите друг на друга два изображения:
- Чтобы получить заголовок FDT для основного ДУ, используйте
ufdt_install_blob():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); - Чтобы получить размер
dtc_totalsize(), используйтеmerged_fdt:merged_fdt_size = dtc_totalsize(merged_fdt);
- Передайте объединенное ДУ, чтобы запустить ядро:
my_kernel_entry(0, machine_type, merged_fdt);
- Чтобы получить заголовок 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); }