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

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

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

Исходная командная строка ядра в дереве устройств (DT) расположена в узле chosen/bootargs . Загрузчик должен объединить это местоположение с другими источниками командной строки ядра:

/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};

DTO не может объединить значения из основного DT и Overlay 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 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

  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);
}
,

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

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

The original kernel command line in device tree (DT) is located in the chosen/bootargs node. The bootloader must concatenate this location with other sources of kernel command line:

/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};

DTO не может объединить значения из основного DT и Overlay 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 наложений (добавление или переопределение) имеют разницу во времени от 6x до 8x
  • 1000 Overlay (добавление или переопределение)

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

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

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

Вы не можете наложить новый узел или свойство на корневой узел основного ОУ, поскольку операции наложения зависят от меток. Поскольку основной DT должен определить метку, а наложенный DT назначает узлы, которые должны быть наложены на метки, вы не можете дать метку для корневого узла (и, следовательно, не может наложить корневой узел).

Поставщики SoC должны определить возможность наложения основного ОУ; ODM/OEM могут только добавлять или переопределять узлы с метками, определенными поставщиком SoC. В качестве обходного пути вы можете определить узел odm под корневым узлом в базовом DT, позволяя всем узлам ODM в наложенном DT добавлять новые узлы. В качестве альтернативы вы можете поместить все узлы, связанные с SoC, в базовом DT в узел soc под корневым узлом, как описано ниже:

main.dts alplay.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

  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);
}
,

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

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

Исходная командная строка ядра в дереве устройств (DT) расположена в узле chosen/bootargs . Загрузчик должен объединить это местоположение с другими источниками командной строки ядра:

/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};

DTO не может объединить значения из основного DT и Overlay 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 мс.

Тестирование производительности для пиксельных устройств по сравнению с 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 является основным dt, а 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

  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);
}