Оптимизация 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 = "...";
};

либуфдт

Хотя последняя версия 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 заголовка таблицы дерева устройств. При использовании заголовка 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' 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. Наложение DT:
    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);
}