Auf dieser Seite werden Optimierungsmöglichkeiten für deine Gerätebaum-Overlays (Device Tree Overlays, DTOs) vorgestellt, Beschränkungen für Overlays auf dem Stammknoten beschrieben und Details zur Konfiguration komprimierter Overlays auf einem DTBO-Image (Device Tree Blob Overlay) erläutert. Außerdem werden Beispiel-Implementierungsanleitungen und ‑Code bereitgestellt.
Kernel-Befehlszeile
Die ursprüngliche Kernel-Befehlszeile im Gerätebaum befindet sich im chosen/bootargs
-Knoten. Der Bootloader muss diesen Parameter mit anderen Quellen der Kernel-Befehlszeile verketten:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
Das DTO kann keine Werte aus dem Haupt-Gerätebaum und dem Overlay-Gerätebaum verketten, daher musst du die Kernel-Befehlszeile des Haupt-Gerätebaums in chosen/bootargs
und die Kernel-Befehlszeile des Overlay-Gerätebaums in chosen/bootargs_ext
speichern. Der Bootloader kann dann diese Parameter verketten und das Ergebnis an den Kernel übergeben.
main.dts | overlay.dts |
---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; |
/dts-v1/; /plugin/; &chosen { bootargs_ext = "..."; }; |
libufdt
Die neueste Version von libfdt
unterstützt zwar DTOs, aber es wird dennoch empfohlen, für die DTO-Implementierung libufdt
zu verwenden (AOSP-Quelle unter platform/system/libufdt
). libufdt
erstellt aus dem zusammengeführten Gerätebaum (Flattened Device Tree, FDT) eine echte Baumstruktur (einen nicht zusammengeführten Gerätebaum oder ufdt), um das Zusammenführen der beiden .dtb
-Dateien aus O(N2) zu O(N) zu verbessern, wobei N die Anzahl der Knoten im Baum ist.
Leistungstests
Bei den internen Tests von Google nach der Kompilierung führte die Verwendung von libufdt
bei 2.405 .dtb
- und 283 .dtbo
-Gerätebaum-Knoten zu Dateigrößen von 70.618 und 8.566 Byte. Die von FreeBSD portierte DTO-Implementierung hatte eine Laufzeit von 124 ms, die libufdt
-DTO-Laufzeit betrug 10 ms.
Bei Leistungstests von Pixel-Geräten wurden libufdt
und libfdt
miteinander verglichen. Die Auswirkung durch die Anzahl der Basis-Knoten ist ähnlich, aber es wurden folgende Unterschiede gemessen:
- 500 Overlay-Operationen (Anhängen oder Überschreiben) dauerten 6- bis 8‑mal länger.
- 1.000 Overlay-Operationen (Anhängen oder Überschreiben) dauerten 8- bis 10‑mal länger.
Beispiel mit der Anzahl der Operationen für das Anhängen auf der X-Achse:
Abbildung 1: Anzahl der Operationen für das Anhängen auf der X-Achse
Beispiel mit der Anzahl der Operationen für das Überschreiben auf der X-Achse:
Abbildung 2: Anzahl der Operationen für das Überschreiben auf der X-Achse
libufdt
wird mit einigen APIs und Datenstrukturen von libfdt
entwickelt. Wenn du libufdt
verwendest, musst du libfdt
implementiert und verlinkt haben. Du kannst in deinem Code aber die libfdt
-API für DTB oder DTBO verwenden.
DTO-API in libufdt
Die Haupt-API für DTO in libufdt
lautet:
struct fdt_header *ufdt_apply_overlay( struct fdt_header *main_fdt_header, size_t main_fdt_size, void *overlay_fdt, size_t overlay_size);
Der Parameter main_fdt_header
ist der Haupt-Gerätebaum und overlay_fdt
ist der Zwischenspeicher mit den Inhalten einer .dtbo
-Datei. Der zurückgegebene Wert ist ein neuer Zwischenspeicher mit dem zusammengeführten Gerätebaum (oder null
bei einem Fehler). Der zusammengeführte Gerätebaum ist als FDT formatiert, sodass er beim Start an den Kernel übergeben werden kann.
Der neue Zwischenspeicher aus dem zurückgegebenen Wert wird mit dto_malloc()
erstellt. Diese Funktion solltest du implementieren, wenn du libufdt
in den Bootloader portierst.
Referenzimplementierungen findest du unter sysdeps/libufdt_sysdeps_*.c
.
Beschränkungen für den Stammknoten
Die Overlay-Operationen basieren auf Labels, daher kannst du in den Stammknoten des Haupt-Gerätebaums keine neuen Knoten oder Eigenschaften einfügen. Da für den Haupt-Gerätebaum ein Label definiert werden muss und bei dem Overlay-Gerätebaum die Labels über die Knoten gelegt werden, kannst du kein Label für den Stammknoten festlegen (und daher auch kein Overlay für den Stammknoten erstellen).
SoC-Anbieter müssen die Overlay-Funktion des Haupt-Gerätebaums definieren, ODM/OEMs können nur Knoten anhängen oder überschreiben, für die der SoC-Anbieter ein Label festgelegt hat. Als Workaround kannst du einen odm
-Knoten im Stammknoten des Haupt-Gerätebaums festlegen, sodass für alle ODM-Knoten im Overlay-Gerätebaum neue Knoten hinzugefügt werden können.
Alternativ kannst du alle SoC-Knoten im Haupt-Gerätebaum in einen soc
-Knoten im Stammknoten verschieben. Das funktioniert folgendermaßen:
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 { ... }; ... }; |
Komprimierte Overlays verwenden
Bei Verwendung von Version 1 des Headers der Gerätebaum-Tabelle werden unter Android 9 auch komprimierte Overlays im DTBO-Image unterstützt. Wenn Version 1 des DTBO-Headers verwendet wird, geben die vier LSB (Least Significant Bits) im Feld „Flags“ im dt_table_entry das Komprimierungsformat des Gerätebaumeintrags an.
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 */ };
Derzeit werden zlib
- und gzip
-Komprimierungen unterstützt.
enum dt_compression_info { NO_COMPRESSION, ZLIB_COMPRESSION, GZIP_COMPRESSION };
Unter Android 9 werden im Rahmen des VtsFirmwareDtboVerification
-Tests auch Tests der komprimierten Overlays unterstützt, damit du die Overlay-App auf ihre Korrektheit überprüfen kannst.
Beispiel-DTO-Implementierung
Nachfolgend findest du die Anleitung für eine Beispielimplementierung des DTO mit libufdt
(Beispielcode siehe unten).
Beispiel-DTO-Anleitung
- Binde die Bibliotheken ein. Wenn du
libufdt
verwenden möchtest, fügelibfdt
für Datenstrukturen und APIs hinzu:#include <libfdt.h> #include <ufdt_overlay.h>
- Lade den Haupt- und den Overlay-Gerätebaum. Lade
.dtb
und.dtbo
aus dem Speicher in den Arbeitsspeicher. Die einzelnen Schritte variieren je nach Design. Den Zwischenspeicher und die Größe von.dtb
/.dtbo
solltest du an dieser Stelle bereits kennen:main_size = my_load_main_dtb(main_buf, main_buf_size)
overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
- Erstelle ein Overlay für die Gerätebäume:
- Rufe mithilfe von
ufdt_install_blob()
den FDT-Header für den Haupt-Gerätebaum ab:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Rufe
ufdt_apply_overlay()
für das DTO ab, um einen zusammengeführten Gerätebaum im FDT-Format zu erhalten:merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size);
- Verwende
merged_fdt
, um die Größe vondtc_totalsize()
abzurufen:merged_fdt_size = dtc_totalsize(merged_fdt);
- Übergib den zusammengeführten Gerätebaum, um den Kernel zu starten:
my_kernel_entry(0, machine_type, merged_fdt);
- Rufe mithilfe von
Beispiel-DTO-Code
#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); }