En esta página, se analizan las optimizaciones que puedes realizar en la implementación de la superposición del árbol de dispositivos (DTO), se describen las restricciones para superponer el nodo raíz y se detalla cómo configurar las superposiciones comprimidas en la imagen de DTBO. También se proporcionan instrucciones y código de implementación de muestra.
Línea de comandos del kernel
La línea de comandos original del kernel en el árbol de dispositivos (DT) se encuentra en el
chosen/bootargs nodo. El bootloader debe concatenar esta
ubicación con otras fuentes de la línea de comandos del kernel:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
La DTO no puede concatenar valores del DT principal y el DT de superposición, por lo que
debes colocar la línea de comandos del kernel del DT principal en
chosen/bootargs y la línea de comandos del kernel del DT de superposición en
chosen/bootargs_ext. Luego, el bootloader puede concatenar estas
ubicaciones y pasar el resultado al kernel.
| main.dts | overlay.dts |
|---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; |
/dts-v1/;
/plugin/;
&chosen {
bootargs_ext = "...";
}; |
libufdt
Si bien la versión más reciente de
libfdt
admite DTO, se recomienda usar libufdt para implementar DTO
(fuente de AOSP en
platform/system/libufdt).
libufdt crea una estructura de árbol real (árbol de dispositivos no aplanado,
o ufdt) a partir del árbol de dispositivos aplanado (FDT), por lo que puede mejorar la
combinación de dos archivos .dtb de O(N2) a O(N), donde N es la
cantidad de nodos en el árbol.
Pruebas de rendimiento
En las pruebas internas de Google, el uso de libufdt en 2405
.dtb y 283 .dtbo nodos DT genera tamaños de archivo de
70,618 y 8,566 bytes después de la compilación. En comparación con una
DTO
implementación transferida desde FreeBSD (tiempo de ejecución de 124 ms), libufdt
el tiempo de ejecución de DTO es de 10 ms.
En las pruebas de rendimiento para dispositivos Pixel, se compararon libufdt y
libfdt. El efecto de la cantidad de nodos base es similar, pero incluye
las siguientes diferencias:
- 500 operaciones de superposición (agregar o anular) tienen una diferencia de tiempo de 6 a 8 veces.
- 1,000 operaciones de superposición (agregar o anular) tienen una diferencia de tiempo de 8 a 10 veces.
Ejemplo con el recuento de anexos establecido en X:

Figura 1: El recuento de anexos es X.
Ejemplo con el recuento de anulaciones establecido en X:

Figura 2: El recuento de anulaciones es X.
libufdt se desarrolla con algunas libfdt APIs y estructuras de datos. Cuando usas libufdt, debes incluir y vincular
libfdt (sin embargo, en tu código, puedes usar la API de libfdt
para operar DTB o DTBO).
API de DTO de libufdt
La API principal para DTO en libufdt es la siguiente:
struct fdt_header *ufdt_apply_overlay(
struct fdt_header *main_fdt_header,
size_t main_fdt_size,
void *overlay_fdt,
size_t overlay_size);
El parámetro main_fdt_header es el DT principal y
overlay_fdt es el búfer que contiene el contenido de un
.dtbo archivo. El valor que se muestra es un búfer nuevo que contiene el
DT combinado (o null en caso de error). El DT combinado tiene el formato
en FDT, que puedes pasar al kernel cuando lo inicias.
El búfer nuevo a partir del valor que se muestra es creado por dto_malloc(),
que debes implementar cuando transfieras libufdt al bootloader.
Para obtener implementaciones de referencia, consulta
sysdeps/libufdt_sysdeps_*.c.
Restricciones del nodo raíz
No puedes superponer un nodo o una propiedad nuevos en el nodo raíz del DT principal porque las operaciones de superposición dependen de las etiquetas. Debido a que el DT principal debe definir una etiqueta y el DT de superposición asigna los nodos que se superpondrán con etiquetas, no puedes asignar una etiqueta para el nodo raíz (y, por lo tanto, no puedes superponer el nodo raíz).
Los proveedores de SoC deben definir la capacidad de superposición del DT principal; los ODM/OEM solo pueden
agregar o anular nodos con etiquetas definidas por el proveedor de SoC. Como
solución alternativa, puedes definir un odm nodo en el
nodo raíz en el DT base, lo que permite que todos los nodos ODM en el DT de superposición agreguen nodos nuevos.
Como alternativa, puedes colocar todos los nodos relacionados con SoC en el DT base en un
soc nodo en el nodo raíz, como se describe a continuación:
| 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 { ... }; ... }; |
Usa superposiciones comprimidas
Android 9 agrega compatibilidad para usar superposiciones comprimidas en la imagen de DTBO cuando se usa la versión 1 del encabezado de la tabla DT. Cuando se usa el encabezado DTBO v1, los cuatro bits menos significativos del campo de marcas en dt_table_entry indican el formato de compresión de la entrada 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 */ };
Actualmente, se admiten las compresiones zlib y gzip.
enum dt_compression_info { NO_COMPRESSION, ZLIB_COMPRESSION, GZIP_COMPRESSION };
Android 9 agrega compatibilidad para probar superposiciones comprimidas
a la prueba VtsFirmwareDtboVerification para ayudarte
a verificar la exactitud de la app de superposición.
Ejemplo de implementación de DTO
En las siguientes instrucciones, se explica una implementación de muestra de DTO
con libufdt (código de muestra a continuación).
Instrucciones de DTO de muestra
- Incluye bibliotecas. Para usar
libufdt, incluyelibfdtpara las estructuras de datos y las APIs:#include <libfdt.h> #include <ufdt_overlay.h>
- Carga el DT principal y el DT de superposición. Carga
.dtby.dtbodesde el almacenamiento a la memoria (los pasos exactos dependen de tu diseño). En este punto, debes tener el búfer y el tamaño de.dtb/.dtbo:main_size = my_load_main_dtb(main_buf, main_buf_size)
overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
- Superpón los DT:
- Usa
ufdt_install_blob()para obtener el encabezado FDT del DT principal:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Llama a
ufdt_apply_overlay()a DTO para obtener un DT combinado en formato FDT:merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size); - Usa
merged_fdtpara obtener el tamaño dedtc_totalsize():merged_fdt_size = dtc_totalsize(merged_fdt);
- Pasa el DT combinado para iniciar el kernel:
my_kernel_entry(0, machine_type, merged_fdt);
- Usa
Código de DTO de muestra
#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); }