Otimizar DTOs

Esta página discute otimizações que podem ser feitas na implementação da Sobreposição da árvore de dispositivos (DTO, na sigla em inglês) descreve restrições contra a sobreposição do nó raiz e detalha como configurar sobreposições compactadas na imagem de DTBO. Ele também fornece amostras código e instruções de implementação.

Linha de comando do kernel

A linha de comando do kernel original na árvore de dispositivos (DT) está localizada no diretório Nó chosen/bootargs. O carregador de inicialização precisa concatenar local com outras fontes da linha de comando do kernel:

/dts-v1/;

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

A DTO não pode concatenar valores da DT principal e da sobreposição. Portanto, você precisa colocar a linha de comando do kernel da DT principal em chosen/bootargs e a linha de comando do kernel da DT de sobreposição em chosen/bootargs_ext. O carregador de inicialização pode concatenar as localizações e passa o resultado para o kernel.

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

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

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

Libufdt

Embora a versão mais recente libfdt oferece suporte a DTO, é recomendável usar libufdt para implementar a DTO (origem do AOSP em platform/system/libufdt). libufdt cria uma estrutura de árvore real (árvore de dispositivos não simplificada, ou ufdt) da árvore de dispositivos nivelados (FDT), para que possa melhorar a fusão de dois arquivos .dtb de O(N2) para O(N), em que N é o e o número de nós na árvore.

Teste de desempenho

Nos testes internos do Google, usando libufdt na 2405 .dtb e 283 .dtbo nós da DT resultam em tamanhos de arquivo de 70.618 e 8.566 bytes após a compilação. Em comparação com um DTO implementação transferida do FreeBSD (tempo de execução de 124 ms), libufdt O tempo de execução da DTO é de 10 ms.

Teste de desempenho para dispositivos Pixel em comparação com libufdt e libfdt. O número de efeitos dos nós base é semelhante, mas inclui as seguintes diferenças:

  • 500 operações de sobreposição (anexação ou substituição) têm um tempo de 6 a 8 vezes diferença
  • 1.000 operações de sobreposição (anexação ou substituição) têm um tempo de 8 a 10 vezes diferença

Exemplo com a contagem de anexação definida como X:

Figura 1. A contagem anexa é X.

Exemplo com a contagem de substituição definida como X:

Figura 2. A contagem de substituição é X.

O libufdt foi desenvolvido com algumas APIs e dados de libfdt. estruturas de trabalho. Ao usar libufdt, você precisa incluir e vincular libfdt (no entanto, no seu código, é possível usar libfdt para operar DTB ou DTBO).

API libufdt DTO

A API principal para DTO em libufdt é a seguinte:

struct fdt_header *ufdt_apply_overlay(
        struct fdt_header *main_fdt_header,
        size_t main_fdt_size,
        void *overlay_fdt,
        size_t overlay_size);

O parâmetro main_fdt_header é a DT principal e overlay_fdt é o buffer que contém o conteúdo de um arquivo .dtbo. O valor de retorno é um novo buffer contendo o DT mesclado (ou null em caso de erro). A DT mesclada é formatada no FDT, que pode ser passado para o kernel ao iniciá-lo.

O novo buffer do valor de retorno é criado por dto_malloc(). que você precisa implementar ao transferir libufdt para o carregador de inicialização. Para implementações de referência, consulte sysdeps/libufdt_sysdeps_*.c:

Restrições do nó raiz

Não é possível sobrepor um novo nó ou uma nova propriedade no nó raiz da TD principal. porque as operações de sobreposição dependem de rótulos. Como a DT principal precisa definir uma e a DT de sobreposição atribuir os nós para serem sobrepostos com rótulos, não pode atribuir um rótulo ao nó raiz (e, portanto, não pode sobrepor o ).

Os fornecedores de SoC precisam definir a capacidade de sobreposição da DT principal. ODMs/OEMs só podem anexar ou substituir nós com rótulos definidos pelo fornecedor do SoC. Como alternativa, defina um nó odm na na DT de base, permitindo que todos os nós ODM na DT de sobreposição adicionem novos nós. Como alternativa, você pode colocar todos os nós relacionados a SoC na DT de base em um soc no nó raiz, conforme descrito abaixo:

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 sobreposições compactadas

O Android 9 adiciona suporte ao uso de sobreposições compactadas na imagem de DTBO ao usar a versão 1 do cabeçalho da tabela de DT. Ao usar o cabeçalho de DTBO v1, os quatro bits menos significativos do campo de flags em dt_table_entry indicam o formato de compactação da entrada da 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 */
};

Atualmente, as compactações zlib e gzip são compatíveis.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

O Android 9 adiciona suporte a testes compactados são sobreposições ao teste de VtsFirmwareDtboVerification para ajudar verificar a exatidão do aplicativo de sobreposição.

Exemplo de implementação de DTO

As instruções a seguir mostram um exemplo de implementação de DTO com libufdt (exemplo de código abaixo).

Exemplos de instruções de DTO

  1. Incluir bibliotecas. Para usar libufdt, inclua libfdt para estruturas de dados e APIs:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
    
  2. Carregue a DT principal e a sobreponha. Carregar .dtb e .dtbo do armazenamento para a memória (as etapas exatas dependem do projeto). Neste ponto, Você terá o buffer e o tamanho 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. Sobreponha os DTs:
    1. Use ufdt_install_blob() para receber o cabeçalho FDT para a DT principal:
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
      
    2. Chame ufdt_apply_overlay() para a DTO para receber uma DT mesclada no FDT formato:
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
      
    3. Use merged_fdt para ver o tamanho de dtc_totalsize():
      merged_fdt_size = dtc_totalsize(merged_fdt);
      
    4. Passe a DT mesclada para iniciar o kernel:
      my_kernel_entry(0, machine_type, merged_fdt);
      

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