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
- 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 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);
- Superposez les DT :
- 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;
- 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);
- Utilisez
merged_fdt
pour obtenir la taille dedtc_totalsize()
:merged_fdt_size = dtc_totalsize(merged_fdt);
- Transmettez le DT fusionné 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); }