Questa pagina illustra le ottimizzazioni che è possibile apportare all'implementazione DTO, descrive le restrizioni contro la sovrapposizione del nodo radice e descrive in dettaglio come configurare le sovrapposizioni compresse 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 i 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 (fonte AOSP su platform/system/libufdt
). libufdt
costruisce una struttura ad albero reale (albero dei dispositivi non appiattito, o ufdt ) dall'albero dei dispositivi appiattiti (FDT), in modo da poter 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 2405 .dtb
e 283 .dtbo
DT produce file di dimensioni di 70.618 e 8.566 byte dopo la compilazione. Rispetto a un'implementazione DTO portata da FreeBSD (124 ms di runtime), il runtime di libufdt
DTO è di 10 ms.
I test delle prestazioni per i dispositivi Pixel hanno confrontato libufdt
e libfdt
. L'effetto del numero di nodi di base è simile, ma include le seguenti differenze:
- 500 operazioni di sovrapposizione (aggiungi o sostituisci) hanno una differenza di tempo da 6x a 8x
- 1000 operazioni di sovrapposizione (aggiungi o sostituisci) hanno una differenza di tempo da 8x a 10x
Esempio con conteggio aggiunto impostato su X:
Esempio con conteggio prioritario impostato su X:
libufdt
è sviluppato con alcune API e strutture dati di libfdt
. Quando si utilizza libufdt
, è necessario includere e collegare libfdt
(tuttavia, nel codice è possibile utilizzare l'API libfdt
per operare DTB o DTBO).
libufdt API DTO
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 durante il porting 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 di 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 con le etichette, non è possibile assegnare 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 aggiungere o sovrascrivere nodi con etichette definite dal fornitore del SoC. Come soluzione alternativa, puoi 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, puoi 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 overlay compressi nell'immagine DTBO quando si usa la versione 1 dell'intestazione della tabella dell'albero del dispositivo. 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 il test degli overlay compressi al test VtsFirmwareDtboVerification
per aiutarti a verificare la correttezza dell'applicazione di overlay.
Esempio di implementazione DTO
Le istruzioni seguenti illustrano un'implementazione di esempio di DTO con libufdt
(codice di esempio di seguito).
Esempio di istruzioni DTO
- Includi librerie. Per utilizzare
libufdt
, includilibfdt
per le strutture di dati e le API:#include <libfdt.h> #include <ufdt_overlay.h>
- Carica DT principale e sovrapponi DT. Carica
.dtb
e.dtbo
dalla memoria alla 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
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); }