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
- 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 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);
- Superposez les DT :
- 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;
- 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);
- 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); }