Optimiser les DTO

Cette page traite des optimisations que vous pouvez apporter à votre implémentation DTO, décrit les restrictions concernant la superposition du nœud racine et détaille comment 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 noyau

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

/dts-v1/;

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

DTO ne peut pas concaténer les valeurs de la DT principale et de la DT superposée, vous devez donc placer la ligne de commande du noyau de la DT principale dans chosen/bootargs et la ligne de commande du noyau de la DT superposée dans chosen/bootargs_ext . Le chargeur de démarrage peut ensuite concaténer ces emplacements et transmettre le résultat au noyau.

main.dts superposition.dts
/dts-v1/;

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

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

libufdt

Bien que la dernière libfdt prenne en charge DTO, il est recommandé d'utiliser libufdt pour implémenter DTO (source AOSP sur platform/system/libufdt ). libufdt construit une véritable arborescence (arborescence de périphériques non aplatie, ou ufdt ) à partir de l'arborescence de périphériques aplatie (FDT), afin de pouvoir améliorer la fusion de deux fichiers .dtb de O(N 2 ) à O(N), où N est le nombre de nœuds dans l'arborescence.

Test de performance

Dans les tests internes de Google, l'utilisation libufdt sur 2 405 nœuds .dtb et 283 .dtbo DT 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 (durée d'exécution de 124 ms), la durée d'exécution libufdt DTO est de 10 ms.

Les tests de performances des appareils Pixel ont comparé 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 remplacement) ont un décalage horaire de 6x à 8x
  • 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 décompte prioritaire est X

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

API DTO libufdt

L'API principale de DTO dans libufdt est la suivante :

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 la DT principale et overlay_fdt est le tampon contenant le contenu d'un fichier .dtbo . La valeur de retour est un nouveau tampon contenant la DT fusionnée (ou null en cas d'erreur). Le DT fusionné est formaté en FDT, que vous pouvez transmettre au noyau lors du démarrage du noyau.

Le nouveau tampon de la valeur de retour est créé par dto_malloc() , que vous devez implémenter lors du portage libufdt dans le chargeur de démarrage. Pour les implémentations de référence, reportez-vous à sysdeps/libufdt_sysdeps_*.c .

Restrictions du nœud racine

Vous ne pouvez pas superposer un nouveau nœud ou une nouvelle propriété dans le nœud racine de la DT principale, car les opérations de superposition reposent sur des étiquettes. Étant donné que la DT principale doit définir une étiquette et que la DT de superposition attribue les nœuds à superposer avec des étiquettes, vous ne pouvez pas donner d'étiquette pour le 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 peuvent uniquement ajouter ou remplacer des nœuds avec des étiquettes définies par le fournisseur SoC. Pour contourner ce problème, vous pouvez définir un nœud odm sous le nœud racine dans le DT de base, permettant ainsi à tous les nœuds ODM du DT de superposition d'ajouter de nouveaux nœuds. Alternativement, vous pouvez 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 superposition.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 ajoute la prise en charge de l'utilisation de superpositions compressées dans l'image DTBO lors de l'utilisation de la version 1 de l'en-tête de la table de l'arborescence des appareils. Lors de l'utilisation de l'en-tête DTBO v1, les quatre bits les moins significatifs du champ flags 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' 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 */
};

Actuellement, les compressions zlib et gzip sont prises en charge.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

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

Exemple de mise en œuvre de DTO

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

Exemples 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 superposé. Chargez .dtb et .dtbo du stockage vers la mémoire (les étapes exactes dépendent de votre conception). À ce stade, vous devriez avoir le 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 la DT principale :
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
      
    2. Appelez ufdt_apply_overlay() à DTO pour obtenir une DT fusionnée 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 la DT fusionnée 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);
}