이 페이지에서는 DTO 구현에 적용 가능한 최적화를 논의하고 루트 노드 오버레이의 제한사항을 설명하고 DTBO 이미지에서 압축된 오버레이를 구성하는 자세한 방법을 다루며 샘플 구현 안내 및 코드도 제공합니다.
커널 명령줄
기기 트리의 원래 커널 명령줄은 chosen/bootargs
노드에 있습니다. 부트로더는 이 위치를 커널 명령줄의 다른 소스에 연결해야 합니다.
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
DTO는 기본 DT 및 오버레이 DT의 값을 연결할 수 없습니다. 따라서 기본 DT의 커널 명령줄을 chosen/bootargs
에, 오버레이 DT의 커널 명령줄을 chosen/bootargs_ext
에 배치해야 합니다. 그러면 부트로더가 이러한 위치를 연결하고 결과를 커널에 전달할 수 있습니다.
main.dts | overlay.dts |
---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; |
/dts-v1/; /plugin/; &chosen { bootargs_ext = "..."; }; |
libufdt
최신 libfdt
에서 DTO를 지원하지만 libufdt
를 사용하여 DTO(platform/system/libufdt
의 AOSP 소스)를 구현하는 것이 좋습니다. libufdt
는 평면화된 기기 트리(FDT)에서 실제 트리 구조(평면화되지 않은 기기 트리 또는 ufdt)를 빌드하므로 2개의 .dtb
파일을 O(N2)에서 O(N)으로 병합하는 과정을 개선할 수 있습니다. 여기서 N은 트리의 노드 수입니다.
성능 테스트
Google의 내부 테스트에서 2405 .dtb
및 283 .dtbo
DT 노드에 libufdt
를 사용하면 컴파일 후 파일 크기가 70,618 및 8,566바이트가 됩니다. FreeBSD(124ms 런타임)에서 포팅된 DTO 구현과 비교하면 libufdt
DTO 런타임은 10ms입니다.
Pixel 기기의 성능 테스트에서는 libufdt
와 libfdt
를 비교했습니다. 기본 노드 효과의 수는 비슷하지만 다음과 같은 차이가 있습니다.
- 500 오버레이(추가 또는 재정의) 작업에 6배~8배의 시간차가 있음
- 1000 오버레이(추가 또는 재정의) 작업에 8배~10배의 시간차가 있음
X로 설정된 횟수 추가 예시:
X로 설정된 횟수 재정의의 예:
libufdt
는 libfdt
API와 데이터 구조로 개발되었습니다. libufdt
를 사용하는 경우 libfdt
를 포함하고 연결해야 합니다. 그러나 코드에는 libfdt
API를 사용하여 DTB 또는 DTBO를 작동할 수 있습니다.
libufdt DTO API
libufdt
에서 DTO의 기본 API는 다음과 같습니다.
struct fdt_header *ufdt_apply_overlay( struct fdt_header *main_fdt_header, size_t main_fdt_size, void *overlay_fdt, size_t overlay_size);
매개변수 main_fdt_header
는 기본 DT이며 overlay_fdt
는 .dtbo
파일의 콘텐츠가 포함된 버퍼입니다. 반환 값은 병합된 DT나 오류 시 null
이 포함된 새 버퍼입니다. 병합된 DT는 FDT로 형식이 지정되며 커널을 시작할 때 커널에 전달할 수 있습니다.
반환 값의 새 버퍼는 libufdt
를 부트로더에 포팅할 때 구현해야 하는 dto_malloc()
에서 만듭니다.
참조 구현은 sysdeps/libufdt_sysdeps_*.c
를 참조하세요.
루트 노드 제한사항
새 노드나 속성은 기본 DT의 루트 노드에 오버레이할 수 없으며 이는 오버레이 작업이 라벨에 의존하기 때문입니다. 기본 DT는 라벨을 정의해야 하고 오버레이 DT는 라벨로 오버레이하려는 노드를 할당해야 합니다. 따라서 루트 노드에는 라벨을 부여할 수 없고 결과적으로는 루트 노드도 오버레이할 수 없습니다.
SoC 공급업체는 기본 DT의 오버레이 기능을 정의해야 합니다. ODM/OEM은 SoC 공급업체에서 정의한 라벨로만 노드를 추가하거나 재정의할 수 있습니다. 이를 해결하려면 기본 DT의 루트 노드 아래에 odm
노드를 정의하여 오버레이 DT의 모든 ODM 노드가 새 노드를 추가할 수 있도록 하면 됩니다.
또는 아래 설명처럼 기본 DT의 모든 SoC 관련 노드를 루트 노드 아래의 soc
노드에 배치하는 방법도 있습니다.
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 { ... }; ... }; |
압축된 오버레이 사용
Android 9에는 기기 트리 테이블 헤더의 1 버전을 사용할 때 DTBO 이미지에 압축된 오버레이를 사용할 수 있도록 지원하는 기능이 추가되었습니다. DTBO 헤더 v1을 사용할 때에는 dt_table_entry에 있는 가장 작은 4개의 플래그 필드 비트가 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 */ };
현재 zlib
및 gzip
압축이 지원됩니다.
enum dt_compression_info { NO_COMPRESSION, ZLIB_COMPRESSION, GZIP_COMPRESSION };
Android 9에서는 오버레이 애플리케이션의 정확성을 확인하는 데 도움이 되도록 압축 오버레이 테스트 지원 기능을 VtsFirmwareDtboVerification
테스트에 추가합니다.
샘플 DTO 구현
다음 안내는 libufdt
(아래 샘플 코드)를 포함하는 DTO의 샘플 구현을 보여줍니다.
샘플 DTO 안내
- 라이브러리를 포함합니다.
libufdt
를 사용하려면 데이터 구조 및 API에libfdt
를 포함합니다.#include <libfdt.h> #include <ufdt_overlay.h>
- 기본 DT 및 오버레이 DT를 로드합니다. 저장소에서 메모리로
.dtb
및.dtbo
를 로드합니다(정확한 단계는 설계에 따라 다름). 이때.dtb
/.dtbo
의 버퍼와 크기가 있어야 합니다.main_size = my_load_main_dtb(main_buf, main_buf_size)
overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
- DT 오버레이:
ufdt_install_blob()
를 사용하여 기본 DT에서 FDT 헤더를 가져옵니다.main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- DTO에
ufdt_apply_overlay()
를 호출하여 FDT 형식의 병합된 DT를 가져옵니다.merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size);
merged_fdt
를 사용하여dtc_totalsize()
의 크기를 가져옵니다.merged_fdt_size = dtc_totalsize(merged_fdt);
- 병합된 DT를 전달하여 커널을 시작합니다.
my_kernel_entry(0, machine_type, merged_fdt);
샘플 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); }