Optimiser des DTO

Cette page aborde les optimisations que vous pouvez apporter à votre implémentation de superposition d'arborescence d'appareils (DTO), décrit les restrictions concernant la superposition du nœud racine et explique comment configurer les superpositions compressées dans l'image DTBO. Il fournit également des instructions et du code d'implémentation.

Ligne de commande du noyau

La ligne de commande du noyau d'origine dans l'arborescence des périphériques (DT) se trouve dans le nœud chosen/bootargs. Le bootloader doit concaténer cet emplacement avec d'autres sources de ligne de commande du noyau :

/dts-v1/;

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

Le DTO ne peut pas concaténer les valeurs du DT principal et du DT de superposition. Vous devez donc placer la ligne de commande du noyau du DT principal dans chosen/bootargs et la ligne de commande du noyau du DT de superposition dans chosen/bootargs_ext. Le bootloader peut ensuite concaténer ces emplacements et transmettre le résultat au noyau.

main.dts overlay.dts
/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};
/dts-v1/;
/plugin/;

&chosen {
  bootargs_ext = "...";
};

libufdt

Bien que la dernière version de libfdt soit compatible avec DTO, il est recommandé d'utiliser libufdt pour implémenter DTO (source AOSP sur platform/system/libufdt). libufdt crée une véritable structure arborescente (arborescence de périphériques non aplatie, ou ufdt) à partir de l'arborescence de périphériques aplatie (FDT), ce qui peut améliorer la fusion de deux fichiers .dtb de O(N2) à O(N), où N est le nombre de nœuds dans l'arborescence.

Tests de performances

Lors des tests internes de Google, l'utilisation de libufdt sur 2 405 .dtb et 283 nœuds DT .dtbo entraîne des tailles de fichier de 70 618 et 8 566 octets après compilation. Par rapport à une implémentation DTO portée depuis FreeBSD (124 ms de temps d'exécution), le temps d'exécution de libufdt DTO est de 10 ms.

Tests de performances pour les appareils Pixel comparés libufdt et libfdt. L'effet du nombre de nœuds de base est similaire, mais présente les différences suivantes :

  • 500 opérations de superposition (ajout ou remplacement) présentent une différence de temps de 6 à 8
  • 1 000 opérations de superposition (ajout ou remplacement) présentent une différence de temps de 8 à 10

Exemple avec le nombre d'ajouts défini sur X :

Figure 1 : Le nombre d'ajouts est de X.

Exemple avec le nombre de remplacements défini sur X :

Figure 2. Le nombre de remplacements est de X.

libufdt est développé avec des API libfdt et des structures de données. Lorsque vous utilisez libufdt, vous devez inclure et associer libfdt (toutefois, dans votre code, vous pouvez utiliser l'API libfdt pour faire fonctionner DTB ou DTBO).

API DTO libufdt

Voici la principale API pour les organisations de trafic des 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);

Le paramètre main_fdt_header est le DT principal et overlay_fdt est le tampon contenant le contenu d'un fichier .dtbo. La valeur renvoyée est un nouveau tampon contenant le DT fusionné (ou null en cas d'erreur). Le DT fusionné est mis en forme au format FDT, que vous pouvez transmettre au noyau lors du démarrage de celui-ci.

Le nouveau tampon de la valeur renvoyée est créé par dto_malloc(), que vous devez implémenter lors du portage de libufdt dans le bootloader. Pour les implémentations de référence, consultez sysdeps/libufdt_sysdeps_*.c.

Restrictions concernant le nœud racine

Vous ne pouvez pas superposer un nouveau nœud ni une nouvelle propriété dans le nœud racine du DT principal, car les opérations de superposition reposent sur des libellés. Étant donné que le DT principal doit définir un libellé et que le DT de superposition attribue les nœuds à superposer avec des libellés, vous ne pouvez pas attribuer de libellé au nœud racine (et ne pouvez donc pas superposer le nœud racine).

Les fournisseurs de SoC doivent définir la capacité de superposition du DT principal. Les ODM/OEM ne peuvent qu'ajouter ou remplacer des nœuds avec des libellés définis par le fournisseur de SoC. Pour contourner ce problème, vous pouvez définir un nœud odm sous le nœud racine dans le DT de base, ce qui permet à tous les nœuds ODM du DT de superposition d'ajouter de nouveaux nœuds. Vous pouvez également placer tous les nœuds liés au SoC dans le DT de base dans un nœud soc sous le nœud racine, comme décrit ci-dessous :

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 {
        ...
    };
    ...
};

Utiliser des superpositions compressées

Android 9 permet d'utiliser des superpositions compressées dans l'image DTBO lorsque vous utilisez la version 1 de l'en-tête de table DT. Lorsque vous utilisez l'en-tête DTBO v1, les quatre bits de poids faible du champ d'indicateurs dans dt_table_entry indiquent le format de compression de l'entrée 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 */
};

Actuellement, les compressions zlib et gzip sont acceptées.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 ajoute la prise en charge des tests de superposition compressée au test VtsFirmwareDtboVerification pour vous aider à vérifier l'exactitude de l'application de superposition.

Exemple d'implémentation de DTO

Les instructions suivantes vous guident à travers un exemple d'implémentation de DTO avec libufdt (exemple de code ci-dessous).

Exemple d'instructions DTO

  1. Incluez les bibliothèques. Pour utiliser libufdt, incluez libfdt pour les structures de données et les API :
    #include <libfdt.h>
    #include <ufdt_overlay.h>
  2. Chargez le DT principal et le DT de superposition. Chargez .dtb et .dtbo du stockage dans la mémoire (les étapes exactes dépendent de votre conception). À ce stade, vous devriez avoir la mémoire tampon et la taille de .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. Superposez les DT :
    1. Utilisez ufdt_install_blob() pour obtenir l'en-tête FDT pour le DT principal :
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
    2. Appelez ufdt_apply_overlay() pour obtenir un DTO afin d'obtenir un DT fusionné au format FDT :
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
    3. Utilisez merged_fdt pour obtenir la taille de dtc_totalsize() :
      merged_fdt_size = dtc_totalsize(merged_fdt);
    4. Transmettez le DT fusionné pour démarrer le noyau :
      my_kernel_entry(0, machine_type, merged_fdt);

Exemple de code 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);
}