Google is committed to advancing racial equity for Black communities. See how.
Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Ottimizzazione dei DTO

Questa pagina discute le ottimizzazioni che è possibile apportare all'implementazione DTO, descrive le restrizioni contro la sovrapposizione del nodo radice e spiega in dettaglio come configurare gli overlay compressi nell'immagine DTBO. Fornisce inoltre istruzioni e codice di implementazione di esempio.

Riga di comando del kernel

La riga di comando del kernel originale nell'albero dei dispositivi si trova nel nodo chosen/bootargs . Il bootloader deve concatenare questa posizione con altre fonti della riga di comando del kernel:

/dts-v1/;

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

DTO non può concatenare valori dal DT principale e dal DT overlay, quindi è necessario inserire la riga di comando del kernel del DT principale in chosen/bootargs e la riga di comando del kernel del DT overlay in chosen/bootargs_ext . Bootloader può quindi concatenare queste posizioni e passare il risultato al kernel.

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

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

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

libufdt

Sebbene l'ultima libfdt supporti DTO, si consiglia di utilizzare libufdt per implementare DTO (sorgente AOSP su platform/system/libufdt ). libufdt costruisce una struttura ad albero reale (albero dei dispositivi non appiattito o ufdt ) dall'albero dei dispositivi appiattito (FDT), quindi può migliorare l'unione di due file .dtb da O (N 2 ) a O (N), dove N è il numero di nodi nell'albero.

Test delle prestazioni

Nei test interni di Google, l'utilizzo di libufdt sui nodi DT 2405 .dtb e 283 .dtbo produce file di dimensioni di 70.618 e 8.566 byte dopo la compilazione. Rispetto a un'implementazione DTO portata da FreeBSD (runtime di 124 ms), il runtime di libufdt DTO è di 10 ms.

I test delle prestazioni per i dispositivi Pixel hanno confrontato libufdt e libfdt . Il numero di effetti dei nodi di base è simile, ma include le seguenti differenze:

  • 500 operazioni di sovrapposizione (aggiunta o sostituzione) hanno una differenza di tempo da 6x a 8x
  • 1000 operazioni di sovrapposizione (aggiunta o sostituzione) hanno una differenza di tempo da 8x a 10x

Esempio con l'aggiunta del conteggio impostato su X:

Figura 1. Il conteggio delle aggiunte è X

Esempio con conteggio prioritario impostato su X:

Figura 2. Conteggio override è X

libufdt è sviluppato con alcune API libfdt e strutture dati. Quando si utilizza libufdt , è necessario includere e collegare libfdt (tuttavia, nel codice è possibile utilizzare l'API libfdt per utilizzare DTB o DTBO).

API DTO libufdt

L'API principale per DTO in libufdt è la seguente:

struct fdt_header *ufdt_apply_overlay(
        struct fdt_header *main_fdt_header,
        size_t main_fdt_size,
        void *overlay_fdt,
        size_t overlay_size);

Il parametro main_fdt_header è il DT principale e overlay_fdt è il buffer contenente il contenuto di un file .dtbo . Il valore restituito è un nuovo buffer contenente il DT unito (o null in caso di errore). Il DT unito è formattato in FDT, che puoi passare al kernel all'avvio del kernel.

Il nuovo buffer dal valore restituito viene creato da dto_malloc() , che dovresti implementare quando libufdt porting di libufdt nel bootloader. Per le implementazioni di riferimento, fare riferimento a sysdeps/libufdt_sysdeps_*.c

Restrizioni del nodo radice

Non è possibile sovrapporre un nuovo nodo o una nuova proprietà al nodo radice del DT principale perché le operazioni di sovrapposizione si basano sulle etichette. Poiché il DT principale deve definire un'etichetta e il DT overlay assegna i nodi da sovrapporre alle etichette, non è possibile fornire un'etichetta per il nodo radice (e quindi non è possibile sovrapporre il nodo radice).

I fornitori di SoC devono definire la capacità di sovrapposizione del DT principale; Gli ODM / OEM possono solo accodare o sovrascrivere i nodi con etichette definite dal fornitore del SoC. Come soluzione alternativa, è possibile definire un nodo odm sotto il nodo radice in DT di base, consentendo a tutti i nodi ODM in DT overlay di aggiungere nuovi nodi. In alternativa, è possibile inserire tutti i nodi relativi a SoC nel DT di base in un nodo soc sotto il nodo radice come descritto di seguito:

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

Utilizzo di sovrapposizioni compresse

Android 9 aggiunge il supporto per l'utilizzo di sovrapposizioni compresse nell'immagine DTBO quando si utilizza la versione 1 dell'intestazione della tabella della struttura ad albero dei dispositivi. Quando si utilizza l'intestazione DTBO v1, i quattro bit meno significativi del campo flag in dt_table_entry indicano il formato di compressione della voce 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' 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 */
};

Attualmente sono supportate le compressioni zlib e gzip .

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 aggiunge il supporto per testare gli overlay compressi al test VtsFirmwareDtboVerification per aiutarti a verificare la correttezza dell'applicazione overlay.

Esempio di implementazione DTO

Le seguenti istruzioni libufdt attraverso un'implementazione di esempio di DTO con libufdt (codice di esempio di seguito).

Istruzioni DTO di esempio

  1. Includi biblioteche. Per utilizzare libufdt , includi libfdt per strutture dati e API:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
    
  2. Carica DT principale e DT in sovrimpressione. Carica .dtb e .dtbo dalla memoria in memoria (i passaggi esatti dipendono dal tuo progetto). A questo punto, dovresti avere il buffer e la dimensione di .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. Sovrapponi i DT:
    1. Usa ufdt_install_blob() per ottenere l'intestazione FDT per il DT principale:
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
      
    2. Chiama ufdt_apply_overlay() a DTO per ottenere un DT unito in formato FDT:
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
      
    3. Usa merged_fdt per ottenere la dimensione di dtc_totalsize() :
      merged_fdt_size = dtc_totalsize(merged_fdt);
      
    4. Passa il DT unito per avviare il kernel:
      my_kernel_entry(0, machine_type, merged_fdt);
      

Codice DTO di esempio

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