Optimierung von DTOs

Auf dieser Seite werden Optimierungen besprochen, die Sie an Ihrer DTO-Implementierung vornehmen können, Einschränkungen gegen die Überlagerung des Stammknotens beschrieben und detailliert beschrieben, wie komprimierte Überlagerungen im DTBO-Image konfiguriert werden. Es enthält außerdem Beispiele für Implementierungsanweisungen und Code.

Kernel-Befehlszeile

Die ursprüngliche Kernel-Befehlszeile im Gerätebaum befindet sich im Knoten chosen/bootargs . Der Bootloader muss diesen Speicherort mit anderen Quellen der Kernel-Befehlszeile verketten:

/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};

DTO kann keine Werte aus Haupt-DT und Overlay-DT verketten, daher müssen Sie die Kernel-Befehlszeile des Haupt-DT in chosen/bootargs und die Kernel-Befehlszeile des Overlay-DT in chosen/bootargs_ext einfügen. Der Bootloader kann diese Speicherorte dann verketten und das Ergebnis an den Kernel übergeben.

main.dts overlay.dts
/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};
/dts-v1/;
/plugin/;

&chosen {
  bootargs_ext = "...";
};

libufdt

Während die neueste libfdt DTO unterstützt, wird empfohlen, libufdt zur Implementierung von DTO zu verwenden (AOSP-Quelle unter platform/system/libufdt ). libufdt erstellt aus dem Flattened Device Tree (FDT) eine echte Baumstruktur (Un-Flattened Device Tree oder ufdt ), sodass die Zusammenführung zweier .dtb Dateien von O(N 2 ) nach O(N) verbessert werden kann, wobei N ist die Anzahl der Knoten im Baum.

Leistungstest

In internen Tests von Google führt die Verwendung libufdt auf 2405 .dtb und 283 .dtbo DT-Knoten zu Dateigrößen von 70.618 und 8.566 Bytes nach der Kompilierung. Im Vergleich zu einer von FreeBSD portierten DTO-Implementierung (124 ms Laufzeit) beträgt die DTO-Laufzeit libufdt 10 ms.

Leistungstests für Pixel-Geräte verglichen libufdt und libfdt . Der Effekt der Anzahl der Basisknoten ist ähnlich, weist jedoch die folgenden Unterschiede auf:

  • 500 Overlay-Vorgänge (Anhängen oder Überschreiben) haben einen 6- bis 8-fachen Zeitunterschied
  • 1000 Overlay-Vorgänge (Anhängen oder Überschreiben) haben einen 8- bis 10-fachen Zeitunterschied

Beispiel mit auf X gesetzter Anhängeanzahl:

Abbildung 1. Die Anzahl der Anhänge beträgt X

Beispiel mit auf X gesetzter überschreibender Anzahl:

Abbildung 2. Überschreibende Anzahl ist X

libufdt wird mit einigen libfdt APIs und Datenstrukturen entwickelt. Wenn Sie libufdt verwenden, müssen Sie libfdt einschließen und verknüpfen (Sie können jedoch in Ihrem Code die libfdt API verwenden, um DTB oder DTBO zu betreiben).

libufdt DTO-API

Die Haupt-API für DTO in libufdt lautet wie folgt:

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-DT und overlay_fdt ist der Puffer, der den Inhalt einer .dtbo Datei enthält. Der Rückgabewert ist ein neuer Puffer, der den zusammengeführten DT enthält (oder null im Fehlerfall). Das zusammengeführte DT ist in FDT formatiert, das Sie beim Starten des Kernels an den Kernel übergeben können.

Der neue Puffer aus dem Rückgabewert wird von dto_malloc() erstellt, was Sie beim Portieren libufdt in den Bootloader implementieren sollten. Referenzimplementierungen finden Sie unter sysdeps/libufdt_sysdeps_*.c .

Einschränkungen für Root-Knoten

Sie können einen neuen Knoten oder eine neue Eigenschaft nicht über den Stammknoten des Haupt-DT legen, da Überlagerungsvorgänge auf Beschriftungen basieren. Da das Haupt-DT eine Beschriftung definieren muss und die Overlay-DT die Knoten zuweist, die mit Beschriftungen überlagert werden sollen, können Sie keine Beschriftung für den Wurzelknoten vergeben (und daher den Wurzelknoten nicht überlagern).

SoC-Anbieter müssen die Overlay-Fähigkeit des Haupt-DT definieren; ODM/OEMs können Knoten nur mit vom SoC-Anbieter definierten Labels anhängen oder überschreiben. Um dieses Problem zu umgehen, können Sie einen odm Knoten unter dem Stammknoten im Basis-DT definieren, sodass alle ODM-Knoten im Overlay-DT neue Knoten hinzufügen können. Alternativ können Sie alle SoC-bezogenen Knoten im Basis-DT wie unten beschrieben in einem soc Knoten unter dem Stammknoten platzieren:

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 {
        ...
    };
    ...
};

Verwendung komprimierter Overlays

Android 9 fügt Unterstützung für die Verwendung komprimierter Overlays im DTBO-Bild hinzu, wenn Version 1 des Gerätebaumtabellenheaders verwendet wird. Bei Verwendung des DTBO-Headers v1 geben die vier niedrigstwertigen Bits des Flags-Felds in dt_table_entry das Komprimierungsformat des DT-Eintrags 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' 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 */
};

Derzeit werden zlib und gzip Komprimierungen unterstützt.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 fügt dem VtsFirmwareDtboVerification Test Unterstützung für das Testen komprimierter Overlays hinzu, um Ihnen bei der Überprüfung der Richtigkeit der Overlay-Anwendung zu helfen.

Beispiel-DTO-Implementierung

Die folgenden Anweisungen führen Sie durch eine Beispielimplementierung von DTO mit libufdt (Beispielcode unten).

Beispiel-DTO-Anweisungen

  1. Binden Sie Bibliotheken ein. Um libufdt zu verwenden, schließen Sie libfdt für Datenstrukturen und APIs ein:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
    
  2. Haupt-BMK und Overlay-BMK laden. Laden Sie .dtb und .dtbo vom Speicher in den Speicher (die genauen Schritte hängen von Ihrem Design ab). Zu diesem Zeitpunkt sollten Sie über den Puffer und die Größe von .dtb / .dtbo verfügen:
    main_size = my_load_main_dtb(main_buf, main_buf_size)
    
    overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
    
  3. Überlagern Sie die DTs:
    1. Verwenden Sie ufdt_install_blob() , um den FDT-Header für den Haupt-DT zu erhalten:
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
      
    2. Rufen Sie ufdt_apply_overlay() für DTO auf, um einen zusammengeführten DT im FDT-Format zu erhalten:
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
      
    3. Verwenden Sie merged_fdt , um die Größe von dtc_totalsize() zu erhalten:
      merged_fdt_size = dtc_totalsize(merged_fdt);
      
    4. Übergeben Sie den zusammengeführten DT, um den Kernel zu starten:
      my_kernel_entry(0, machine_type, merged_fdt);
      

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);
}