Optimierung von DTOs

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

Auf dieser Seite werden Optimierungen erörtert, die Sie an Ihrer DTO-Implementierung vornehmen können, Einschränkungen für das Überlagern des Stammknotens beschrieben und detailliert beschrieben, wie komprimierte Überlagerungen im DTBO-Image konfiguriert werden. Es enthält auch Beispiele für Implementierungsanweisungen und Code.

Kernel-Befehlszeile

Die ursprüngliche Kernel-Befehlszeile im Gerätebaum befindet sich im chosen/bootargs -Knoten. 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 Überlagerungs-DT verketten, daher müssen Sie die Kernel-Befehlszeile der Haupt-DT in chosen/bootargs und die Kernel-Befehlszeile der Überlagerungs-DT in chosen/bootargs_ext . 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 zu verwenden, um DTO zu implementieren (AOSP-Quelle unter platform/system/libufdt ). libufdt baut eine echte Baumstruktur (nicht abgeflachter Gerätebaum oder ufdt ) aus dem abgeflachten Gerätebaum (FDT) auf, sodass es das Zusammenführen von zwei .dtb Dateien von O(N 2 ) zu O(N) verbessern kann, wobei N ist die Anzahl der Knoten im Baum.

Leistungstest

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

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

  • 500 Überlagerungsvorgänge (Anhängen oder Überschreiben) haben eine 6- bis 8-fache Zeitdifferenz
  • 1000 Überlagerungsvorgänge (Anhängen oder Überschreiben) haben eine 8- bis 10-fache Zeitdifferenz

Beispiel mit auf X gesetzter anhängender Anzahl:

Abbildung 1. Anhängende Anzahl ist 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 und verknüpfen (in Ihrem Code können Sie jedoch die libfdt API verwenden, um DTB oder DTBO zu betreiben).

libufdt-DTO-API

Die Haupt-API für DTO in libufdt ist 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 das Haupt-DT und overlay_fdt ist der Puffer, der den Inhalt einer .dtbo -Datei enthält. Der Rückgabewert ist ein neuer Puffer, der das zusammengeführte 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 implementieren sollten, wenn Sie libufdt in den Bootloader portieren. Referenzimplementierungen finden Sie unter sysdeps/libufdt_sysdeps_*.c .

Root-Knoten-Einschränkungen

Sie können einen neuen Knoten oder eine neue Eigenschaft nicht in den Stammknoten des Haupt-DT überlagern, da Überlagerungsvorgänge auf Beschriftungen angewiesen sind. Da das Haupt-BMK ein Label definieren muss und das Overlay-BMK die zu überlagernden Knoten mit Labels zuweist, können Sie dem Root-Knoten kein Label geben (und können daher den Root-Knoten 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. Als Problemumgehung können Sie einen ODM-Knoten unter dem odm in der Basis-DT definieren, sodass alle ODM-Knoten in der Overlay-DT neue Knoten hinzufügen können. Alternativ könnten Sie alle SoC-bezogenen Knoten im Basis-DT in einen soc -Knoten unter dem Stammknoten legen, wie unten beschrieben:

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

Android 9 fügt Unterstützung für die Verwendung komprimierter Overlays im DTBO-Image hinzu, wenn Version 1 des Headers der Gerätestrukturtabelle verwendet wird. Wenn der DTBO-Header v1 verwendet wird, 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, damit Sie die Korrektheit der Overlay-Anwendung überprüfen können.

Beispiel für eine DTO-Implementierung

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

Beispiel-DTO-Anweisungen

  1. Bibliotheken einbeziehen. Um libufdt zu verwenden, schließen libfdt für Datenstrukturen und APIs ein:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
    
  2. Haupt-BMK und Overlay-BMK laden. Laden .dtb und .dtbo aus dem Speicher in den Speicher (die genauen Schritte hängen von Ihrem Design ab). An dieser Stelle sollten Sie den Puffer und die Größe von .dtb / .dtbo :
    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 BMK:
    1. Verwenden Sie ufdt_install_blob() , um den FDT-Header für das Haupt-DT abzurufen:
      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 ein zusammengeführtes 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 das zusammengeführte 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);
}