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

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

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

Исходная командная строка ядра в дереве устройств находится в узле 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 операций наложения (добавления или замены) имеют разницу во времени от 6 до 8 раз.
  • 1000 операций наложения (добавления или замены) имеют разницу во времени от 8x до 10x

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

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

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

Рис. 2. Количество переопределений равно X

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

libufdt API 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 — это основное DT, а overlay_fdt — это буфер, содержащий содержимое файла .dtbo . Возвращаемое значение — новый буфер, содержащий объединенное DT (или null в случае ошибки). Объединенное DT формируется в FDT, который вы можете передать ядру при запуске ядра.

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

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

Вы не можете наложить новый узел или свойство на корневой узел основного 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 при использовании версии 1 заголовка таблицы дерева устройств. При использовании заголовка DTBO v1 четыре младших бита поля 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' will be 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. Загрузите основное ОУ и оверлейное ОУ. Загрузите .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. Наложение ДТ:
    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);
}