Google is committed to advancing racial equity for Black communities. See how.
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Optimización de DTO

Esta página analiza las optimizaciones que puede realizar en su implementación de DTO, describe las restricciones contra la superposición del nodo raíz y detalla cómo configurar superposiciones comprimidas en la imagen de DTBO. También proporciona instrucciones y código de implementación de muestra.

Línea de comando del kernel

La línea de comandos del kernel original en el árbol de dispositivos se encuentra en el nodo chosen/bootargs . El cargador de arranque debe concatenar esta ubicación con otras fuentes de la línea de comandos del kernel:

/dts-v1/;

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

DTO no puede concatenar valores de DT principal y DT superpuesto, por lo que debe poner la línea de comando del kernel del DT principal en chosen/bootargs y la línea de comando del kernel del DT superpuesto en chosen/bootargs_ext . El cargador de arranque 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 última libfdt compatible con DTO, se recomienda usar libufdt para implementar DTO (fuente AOSP en platform/system/libufdt ). libufdt construye una estructura de árbol real (árbol de dispositivo no plano, o ufdt ) a partir del árbol de dispositivo plano (FDT), por lo que puede mejorar la fusión de dos archivos .dtb de O (N 2 ) a O (N), donde N es el número de nodos del árbol.

Pruebas de rendimiento

En las pruebas internas de Google, el uso de libufdt en 2405 .dtb y 283 .dtbo nodos DT da como resultado tamaños de archivo de 70,618 y 8,566 bytes después de la compilación. En comparación con una implementación de DTO transferida desde FreeBSD (tiempo de ejecución de 124 ms), el tiempo de ejecución de libufdt DTO es de 10 ms.

Las pruebas de rendimiento para dispositivos Pixel compararon libufdt y libfdt . El efecto del número de nodos base es similar, pero incluye las siguientes diferencias:

  • 500 operaciones de superposición (agregar o anular) tienen una diferencia de tiempo de 6x a 8x
  • 1000 operaciones de superposición (agregar o anular) tienen una diferencia de tiempo de 8x a 10x

Ejemplo con un recuento agregado establecido en X:

Figura 1. El recuento de anexos es X

Ejemplo con el recuento de reemplazo establecido en X:

Figura 2. El recuento anulado es X

libufdt se desarrolla con algunas API y estructuras de datos de libfdt . Al usar libufdt , debe incluir y vincular libfdt (sin embargo, en su código puede usar la API de libfdt para operar DTB o DTBO).

API libufdt DTO

La API principal de 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 archivo .dtbo . El valor de retorno es un nuevo búfer que contiene el DT combinado (o null en caso de error). El DT combinado está formateado en FDT, que puede pasar al kernel al iniciar el kernel.

El nuevo búfer del valor de retorno es creado por dto_malloc() , que debe implementar cuando libufdt al gestor de arranque. Para implementaciones de referencia, consulte sysdeps/libufdt_sysdeps_*.c

Restricciones del nodo raíz

No puede superponer un nuevo nodo o propiedad en el nodo raíz del DT principal porque las operaciones de superposición se basan en etiquetas. Debido a que el DT principal debe definir una etiqueta y el DT de superposición asigna los nodos para que se superpongan con etiquetas, no puede dar una etiqueta para el nodo raíz (y, por lo tanto, no puede 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, puede definir un nodo odm debajo del nodo raíz en DT base, lo que permite que todos los nodos ODM en DT superpuesto agreguen nuevos nodos. Alternativamente, puede colocar todos los nodos relacionados con SoC en el DT base en un nodo soc debajo del 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 {
        ...
    };
    ...
};

Usar superposiciones comprimidas

Android 9 agrega soporte para usar superposiciones comprimidas en la imagen DTBO cuando se usa la versión 1 del encabezado de la tabla del árbol de dispositivos. Cuando se usa el encabezado DTBO v1, los cuatro bits menos significativos del campo de banderas 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' 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 */
};

Actualmente, se admiten las compresiones zlib y gzip .

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 agrega soporte para probar superposiciones comprimidas a la prueba VtsFirmwareDtboVerification para ayudarlo a verificar la exactitud de la aplicación de superposición.

Ejemplo de implementación de DTO

Las siguientes instrucciones lo libufdt través de una implementación de muestra de DTO con libufdt (código de muestra a continuación).

Ejemplo de instrucciones DTO

  1. Incluya bibliotecas. Para usar libufdt , incluya libfdt para estructuras de datos y API:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
    
  2. Cargar DT principal y superponer DT. Cargue .dtb y .dtbo desde el almacenamiento a la memoria (los pasos exactos dependen de su diseño). En este punto, debería 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);
    
  3. Superponga los DT:
    1. Utilice ufdt_install_blob() para obtener el encabezado FDT para el DT principal:
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
      
    2. Llame 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);
      
    3. Utilice merged_fdt para obtener el tamaño de dtc_totalsize() :
      merged_fdt_size = dtc_totalsize(merged_fdt);
      
    4. Pase el DT combinado para iniciar el kernel:
      my_kernel_entry(0, machine_type, merged_fdt);
      

Ejemplo de código 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);
}