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:
Esempio con conteggio prioritario impostato su 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
- Includi biblioteche. Per utilizzare
libufdt
, includilibfdt
per strutture dati e API:#include <libfdt.h> #include <ufdt_overlay.h>
- 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);
- Sovrapponi i DT:
- 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;
- 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);
- Usa
merged_fdt
per ottenere la dimensione didtc_totalsize()
:merged_fdt_size = dtc_totalsize(merged_fdt);
- Passa il DT unito per avviare il kernel:
my_kernel_entry(0, machine_type, merged_fdt);
- Usa
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); }