Esta página discute as otimizações que você pode fazer na implementação da sobreposição da árvore de dispositivos (DTO), descreve as restrições contra a sobreposição do nó raiz e detalha como configurar sobreposições compactadas na imagem DTBO. Ela também oferece instruções e código de implementação de amostra.
Linha de comando do kernel
A linha de comando original do kernel na árvore de dispositivos (DT) está localizada no nó
chosen/bootargs
. O carregador de inicialização precisa concatenar esse local com outras fontes da linha de comando do kernel:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
O DTO não pode concatenar valores do DT principal e do DT de sobreposição. Portanto, coloque a linha de comando do kernel do DT principal em chosen/bootargs
e a linha de comando do kernel do DT de sobreposição em chosen/bootargs_ext
. O carregador de inicialização pode concatenar esses locais e transmitir o resultado ao kernel.
main.dts | overlay.dts |
---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; |
/dts-v1/; /plugin/; &chosen { bootargs_ext = "..."; }; |
libufdt
Embora o libfdt
mais recente seja compatível com DTO, é recomendável usar libufdt
para implementar DTO (fonte AOSP em platform/system/libufdt
). O libufdt
cria uma estrutura de árvore real (árvore de dispositivos não achatada ou ufdt) da árvore de dispositivos achatada (FDT), o que pode melhorar a fusão de dois arquivos .dtb
de O(N2) para O(N), em que N é o número de nós na árvore.
Teste de desempenho
Nos testes internos do Google, o uso de libufdt
em 2405
.dtb
e 283 nós de DT .dtbo
resulta em tamanhos de arquivo de
70.618 e 8.566 bytes após a compilação. Em comparação com uma implementação de DTO transferida do FreeBSD (tempo de execução de 124 ms), o tempo de execução de DTO do libufdt
é de 10 ms.
Testes de desempenho para dispositivos Pixel comparados a libufdt
e libfdt
. O efeito do número de nós de base é semelhante, mas inclui as seguintes diferenças:
- 500 operações de sobreposição (adição ou substituição) têm uma diferença de tempo de 6 a 8 vezes
- 1.000 operações de sobreposição (adição ou substituição) têm uma diferença de tempo de 8 a 10 vezes.
Exemplo com a contagem de anexação definida como X:
Figura 1. A contagem de inclusão é X.
Exemplo com a contagem de substituições definida como X:
Figura 2. A contagem de substituições é X.
O libufdt
é desenvolvido com algumas APIs libfdt
e estruturas de dados. Ao usar libufdt
, inclua e vincule
libfdt
. No entanto, no seu código, você pode usar a API libfdt
para operar DTB ou DTBO.
API DTO libufdt
A principal API 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
é o DT principal, e overlay_fdt
é o buffer que contém o conteúdo de um arquivo .dtbo
. O valor de retorno é um novo buffer que contém o
DT mesclado (ou null
em caso de erro). O DT mesclado é formatado
em FDT, que pode ser transmitido ao kernel ao iniciá-lo.
O novo buffer do valor de retorno é criado por dto_malloc()
,
que você precisa implementar ao portar libufdt
para o carregador de inicialização.
Para implementações de referência, consulte
sysdeps/libufdt_sysdeps_*.c
.
Restrições de nó raiz
Não é possível sobrepor um novo nó ou propriedade ao nó raiz da DT principal, porque as operações de sobreposição dependem de rótulos. Como a árvore de decisão principal precisa definir um rótulo e a árvore de decisão de sobreposição atribui os nós a serem sobrepostos com rótulos, não é possível dar um rótulo ao nó raiz (e, portanto, não é possível sobrepor o nó raiz).
Os fornecedores de SoC precisam definir a capacidade de sobreposição do DT principal. Os ODMs/OEMs só podem
adicionar ou substituir nós com rótulos definidos pelo fornecedor de SoC. Como
solução alternativa, você pode definir um nó odm
no nó
raiz 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 ao SoC na DT de base em um nó 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 para o uso de sobreposições compactadas na imagem DTBO ao usar a versão 1 do cabeçalho da tabela DT. Ao usar o cabeçalho DTBO v1, os quatro bits menos significativos do campo de flags em dt_table_entry indicam o formato de compactação da 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 */ };
No momento, 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 para testar sobreposições
compactadas ao teste VtsFirmwareDtboVerification
para ajudar você
a verificar a correção do app de sobreposição.
Exemplo de implementação de DTO
As instruções a seguir mostram um exemplo de implementação de DTO
com libufdt
(código de exemplo abaixo).
Exemplo de instruções de DTO
- Inclua bibliotecas. Para usar
libufdt
, inclualibfdt
para estruturas de dados e APIs:#include <libfdt.h> #include <ufdt_overlay.h>
- Carregue a DT principal e a DT de sobreposição. Carregue
.dtb
e.dtbo
do armazenamento para a memória. As etapas exatas dependem do seu design. 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);
- Sobreponha os DTs:
- Use
ufdt_install_blob()
para receber o cabeçalho FDT da DT principal:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Chame
ufdt_apply_overlay()
para DTO e receba um DT mesclado no formato FDT:merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size);
- Use
merged_fdt
para receber o tamanho dedtc_totalsize()
:merged_fdt_size = dtc_totalsize(merged_fdt);
- Transmita o DT mesclado para iniciar o kernel:
my_kernel_entry(0, machine_type, merged_fdt);
- Use
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); }