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 :
Exemple avec un nombre de remplacement défini sur 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
- Incluez les bibliothèques. Pour utiliser
libufdt
, incluezlibfdt
pour les structures de données et les API :#include <libfdt.h> #include <ufdt_overlay.h>
- 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);
- Superposez les DT :
- 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;
- 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);
- Utilisez
merged_fdt
pour obtenir la taille dedtc_totalsize()
:merged_fdt_size = dtc_totalsize(merged_fdt);
- Transmettez la DT fusionnée pour démarrer le noyau :
my_kernel_entry(0, machine_type, merged_fdt);
- Utilisez
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); }