Auf dieser Seite werden Optimierungsmöglichkeiten für Ihre 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 müssen Sie 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 Sie libufdt
verwenden, müssen Sie libfdt
implementiert und verlinkt haben. Sie können in Ihrem 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 sollten Sie implementieren, wenn Sie libufdt
in den Bootloader portieren.
Referenzimplementierungen finden Sie unter sysdeps/libufdt_sysdeps_*.c
.
Beschränkungen für den Stammknoten
Die Overlay-Operationen basieren auf Labels, daher können Sie 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, können Sie 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 können Sie 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 können Sie 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 Sie die Overlay-App auf ihre Korrektheit überprüfen können.
Beispiel-DTO-Implementierung
Nachfolgend finden Sie die Anleitung für eine Beispielimplementierung des DTO mit libufdt
(Beispielcode siehe unten).
Beispiel-DTO-Anleitung
- Binde die Bibliotheken ein. Wenn Sie
libufdt
verwenden möchten, fügen Sielibfdt
für Datenstrukturen und APIs hinzu:#include <libfdt.h> #include <ufdt_overlay.h>
- Laden Sie den Haupt- und den Overlay-Gerätebaum. Laden Sie
.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
sollten Sie 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);
- Erstellen Sie ein Overlay für die Gerätebäume:
- Rufen Sie 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;
- Rufen Sie
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);
- Verwenden Sie
merged_fdt
, um die Größe vondtc_totalsize()
abzurufen:merged_fdt_size = dtc_totalsize(merged_fdt);
- Übergeben Sie den zusammengeführten Gerätebaum, um den Kernel zu starten:
my_kernel_entry(0, machine_type, merged_fdt);
- Rufen Sie 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); }