Optimisation des DTO

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

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 de superposition, vous devez donc mettre la ligne de commande du noyau de la DT principale dans chosen/bootargs et la ligne de commande du noyau de la DT de superposition dans chosen/bootargs_ext . Bootloader 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

Alors que la dernière libfdt prend 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 d'améliorer la fusion de deux fichiers .dtb de O(N 2 ) à O(N), où N est le nombre de nœuds dans l'arbre.

Test de performance

Dans les tests internes de Google, l'utilisation de libufdt sur les nœuds 2405 .dtb et 283 .dtbo DT donne 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 de libufdt DTO est de 10 ms.

Les tests de performances pour les 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 une différence de temps de 6x à 8x
  • 1000 opérations de superposition (ajout ou remplacement) ont une différence de temps de 8x à 10x

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

Figure 1. Le nombre d'ajouts est X

Exemple avec un décompte prioritaire 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 (toutefois, dans votre code, vous pouvez utiliser l'API libfdt pour faire fonctionner DTB ou DTBO).

API DTO libufdt

L'API principale vers 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 buffer contenant la DT fusionnée (ou null en cas d'erreur). La DT fusionnée est formatée en FDT, que vous pouvez transmettre au noyau lors du démarrage du noyau.

Le nouveau tampon à partir de la valeur de retour est créé par dto_malloc() , que vous devez implémenter lors du portage libufdt dans le bootloader. 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 affecte les nœuds à superposer avec des étiquettes, vous ne pouvez pas donner d'étiquette pour le nœud racine (et donc ne pouvez pas superposer le nœud racine).

Les fournisseurs de SoC doivent définir la capacité de superposition de la DT principale ; Les ODM/OEM peuvent uniquement ajouter ou remplacer des nœuds avec des étiquettes définies par le fournisseur SoC. Pour contourner le problème, vous pouvez définir un nœud odm sous le nœud racine dans la DT de base, permettant à tous les nœuds ODM dans la DT superposée 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 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 {
        ...
    };
    ...
};

Utilisation de 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 du tableau 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 d'implémentation 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. Inclure 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. Charger la DT principale et la DT superposée. Chargez .dtb et .dtbo du stockage dans la mémoire (les étapes exactes dépendent de votre conception). A ce stade, vous devriez avoir le buffer 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. Superposer 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. Passez 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);
}