Optimiser des DTO

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

Ligne de commande du kernel

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 la 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 superposé. 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 superposé dans chosen/bootargs_ext. Le bootloader peut ensuite concatenater 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 prenne en charge la DTO, il est recommandé d'utiliser libufdt pour implémenter la DTO (source AOSP sur platform/system/libufdt). libufdt crée une structure d'arborescence réelle (arborescence de périphérique non aplatie, ou ufdt) à partir de l'arborescence de périphérique aplatie (FDT). Il peut ainsi 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 nœuds .dtb et 283 nœuds de transfert de données .dtbo entraîne une taille de fichier de 70 618 et 8 566 octets après la compilation. Par rapport à une implémentation DTO portée depuis FreeBSD (124 ms d'exécution), l'exécution DTO de libufdt est de 10 ms.

Tests de performances pour les appareils Pixel par rapport à libufdt et libfdt. L'effet du nombre de nœuds de base est similaire, mais inclut les différences suivantes:

  • 500 opérations de superposition (ajout ou forçage) présentent une différence de temps de 6 à 8 fois
  • 1 000 opérations de superposition (ajout ou remplacement) ont un décalage horaire de 8 à 10 fois

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

Figure 1 : Le nombre d'ajouts est X.

Exemple avec un nombre de remplacement défini sur X :

Figure 2. Le nombre à remplacer est X.

libufdt est développé avec certaines API et structures de données libfdt. Lorsque vous utilisez libufdt, vous devez inclure et associer libfdt (cependant, dans votre code, vous pouvez utiliser l'API libfdt pour utiliser 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). La DT fusionnée est mise en forme au format FDT, que vous pouvez transmettre au noyau au démarrage.

Le nouveau tampon à partir de la valeur de retour 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 les nœuds racine

Vous ne pouvez pas superposer un nouveau nœud ou une nouvelle propriété au nœud racine de la table de données principale, car les opérations de superposition reposent sur des libellés. Étant donné que la table de données principale doit définir un libellé et que la table de données de superposition attribue des libellés aux nœuds à superposer, vous ne pouvez pas attribuer de libellé au nœud racine (et donc pas le superposer).

Les fournisseurs de SoC doivent définir la capacité de superposition du DT principal. Les ODM/OEM ne peuvent ajouter ni 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 la 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 prend en charge l'utilisation de 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 les moins significatifs 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 compatibles.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 permet de tester les superpositions compressées dans le test VtsFirmwareDtboVerification pour vous aider à vérifier la validité de l'application de superposition.

Exemple d'implémentation de DTO

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

Exemples d'instructions pour les 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 la DT principale et la DT superposée. Chargez .dtb et .dtbo depuis le stockage vers la mémoire (la procédure exacte dépend de votre conception). À ce stade, vous devez 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 du DT principal :
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
      
    2. Appelez ufdt_apply_overlay() à DTO pour 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);
}