Ottimizza i gruppi dediti al commercio di stupefacenti

Questa pagina illustra le ottimizzazioni che puoi apportare all'implementazione dell'overlay ad albero dei dispositivi (DTO), descrive le limitazioni alla sovrapposizione del nodo principale e descrive nel dettaglio come configurare overlay compressi nell'immagine DTBO. Fornisce anche esempi le istruzioni e il codice per l'implementazione.

Riga di comando kernel

La riga di comando originale del kernel nella struttura ad albero dei dispositivi (DT) si trova nella chosen/bootargs nodo. Il bootloader deve concatenare questa impostazione con altre origini della riga di comando del kernel:

/dts-v1/;

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

Un DTO non può concatenare i valori del DT principale e del DT sovrapposto, perciò devi inserire la riga di comando del kernel del DT principale chosen/bootargs e la riga di comando kernel del DT overlay in chosen/bootargs_ext. Il bootloader può quindi concatenare questi più posizioni e passare il risultato al kernel.

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

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

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

Libufdt

Anche se l'ultima versione libfdt supporta DTO, si consiglia di utilizzare libufdt per implementare DTO (Sorgente AOSP in platform/system/libufdt). libufdt crea una vera struttura ad albero (albero dei dispositivi non appiattito, o ufdt) dalla struttura ad albero dei dispositivi bidimensionali (FDT), in modo da poter migliorare unione di due file .dtb da O(N2) a O(N), dove N è di nodi nell'albero.

Test delle prestazioni

Nei test interni di Google, utilizzando libufdt su 2405 .dtb e 283 .dtbo nodi DT generano dimensioni di file pari a 70.618 e 8.566 byte dopo la compilazione. Confrontato con un DTO implementazione portata da FreeBSD (124 ms di runtime), libufdt Il tempo di esecuzione del DTO è di 10 ms.

I test delle prestazioni per i dispositivi Pixel hanno confrontato libufdt e libfdt. L'effetto del numero di nodi base è simile, ma include le seguenti differenze:

  • 500 operazioni di sovrapposizione (aggiunta o override) tempi da 6 a 8x differenza
  • 1000 operazioni di overlay (aggiunta o override) tempi da 8 a 10 volte differenza

Esempio con conteggio aggiunto impostato su X:

Figura 1. Il conteggio aggiunto è X.

Esempio con conteggio per l'override impostato su X:

Figura 2. Il conteggio per l'override è X.

libufdt è sviluppato con alcune API e dati di libfdt le nostre strutture. Quando utilizzi libufdt, devi includere e collegare libfdt (tuttavia, nel tuo codice puoi utilizzare libfdt API per il funzionamento di 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 che include i contenuti di .dtbo file. Il valore restituito è un nuovo buffer contenente DT unito (o null in caso di errore). Il DT unito è formattato in FDT, che puoi passare al kernel all'avvio.

Il nuovo buffer del valore restituito viene creato da dto_malloc(), che dovresti implementare durante il trasferimento di libufdt in bootloader. Per le implementazioni di riferimento, consulta sysdeps/libufdt_sysdeps_*.c.

Limitazioni del nodo radice

Non puoi sovrapporre un nuovo nodo o una nuova proprietà nel nodo principale del DT principale poiché le operazioni di overlay si basano sulle etichette. Poiché il DT principale deve definire e l'overlay DT assegna i nodi da sovrapporre alle etichette, non possono assegnare un'etichetta al nodo radice (e quindi non possono sovrapporsi al nodo radice ).

i fornitori di SoC devono definire la capacità di sovrapposizione del DT principale; Gli ODM/OEM possono solo aggiungere o eseguire l'override dei nodi con etichette definite dal fornitore del SoC. Come alternativa, puoi definire un nodo odm nella nodo radice nel DT di base, che consente a tutti i nodi ODM nel DT in overlay di aggiungere nuovi nodi. In alternativa, potresti inserire tutti i nodi relativi a SoC nel DT di base in un soc nel nodo principale, 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 {
        ...
    };
    ...
};

Utilizzare overlay compressi

Android 9 aggiunge il supporto per l'utilizzo di overlay compressi nell'immagine DTBO quando viene utilizzata la versione 1 dell'intestazione della tabella DT. Quando si utilizza l'intestazione DTBO v1, i quattro bit meno significativi del campo flags in dt_table_entry indica 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' 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 */
};

Attualmente sono supportate le compressione zlib e gzip.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 aggiunge il supporto per i test compressi overlay al test VtsFirmwareDtboVerification per aiutarti verificare la correttezza dell'app overlay.

Esempio di implementazione di DTO

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

Istruzioni di esempio per DTO

  1. Includi librerie. Per utilizzare libufdt, includi libfdt per strutture dati e API:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
    
  2. Carica il DT principale e il DT overlay. Carica .dtb e .dtbo dall'archiviazione alla memoria (i passaggi esatti dipendono dal 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. Sovrapposizione dei DT:
    1. Utilizza 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 FDT formato:
      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);
      

Esempio di codice DTO

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