Trang này thảo luận về các biện pháp tối ưu hoá mà bạn có thể thực hiện để triển khai lớp phủ cây thiết bị (DTO), mô tả các quy định hạn chế đối với việc phủ lên nút gốc và thông tin chi tiết về cách định cấu hình lớp phủ nén trong hình ảnh DTBO. Tài liệu này cũng cung cấp hướng dẫn và mã triển khai mẫu.
Dòng lệnh hạt nhân
Dòng lệnh hạt nhân ban đầu trong cây thiết bị (DT) nằm trong nút chosen/bootargs
. Trình tải khởi động phải nối vị trí này với các nguồn khác của dòng lệnh hạt nhân:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
DTO không thể nối các giá trị từ DT chính và DT lớp phủ, vì vậy, bạn phải đặt dòng lệnh hạt nhân của DT chính vào chosen/bootargs
và dòng lệnh hạt nhân của DT lớp phủ vào chosen/bootargs_ext
. Sau đó, trình tải khởi động có thể nối các vị trí này và chuyển kết quả đến hạt nhân.
main.dts | overlay.dts |
---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; |
/dts-v1/; /plugin/; &chosen { bootargs_ext = "..."; }; |
libufdt
Mặc dù libfdt
mới nhất hỗ trợ DTO, nhưng bạn nên sử dụng libufdt
để triển khai DTO (nguồn AOSP tại platform/system/libufdt
). libufdt
tạo một cấu trúc cây thực (cây thiết bị không được làm phẳng hoặc ufdt) từ cây thiết bị được làm phẳng (FDT), nhờ đó có thể cải thiện việc hợp nhất hai tệp .dtb
từ O(N2) thành O(N), trong đó N là số lượng nút trong cây.
Kiểm thử hiệu suất
Trong thử nghiệm nội bộ của Google, việc sử dụng libufdt
trên nút DT 2405
.dtb
và 283 .dtbo
sẽ dẫn đến kích thước tệp
là 70.618 và 8.566 byte sau khi biên dịch. So với quy trình triển khai DTO được chuyển từ FreeBSD (thời gian chạy 124 mili giây), thời gian chạy DTO của libufdt
là 10 mili giây.
Thử nghiệm hiệu suất cho các thiết bị Pixel so sánh libufdt
và libfdt
. Số lượng nút cơ sở có hiệu ứng tương tự, nhưng bao gồm các điểm khác biệt sau:
- 500 thao tác lớp phủ (nối hoặc ghi đè) có sự khác biệt về thời gian từ 6 đến 8 lần
- 1000 thao tác lớp phủ (nối tiếp hoặc ghi đè) có sự khác biệt về thời gian từ 8 đến 10 lần
Ví dụ về việc đặt số lượng nối thêm thành X:
Hình 1. Số lượng phần đính kèm là X.
Ví dụ về trường hợp số lượng ghi đè được đặt thành X:
Hình 2. Số lần ghi đè là X.
libufdt
được phát triển bằng một số API libfdt
và cấu trúc dữ liệu. Khi sử dụng libufdt
, bạn phải đưa vào và liên kết libfdt
(tuy nhiên, trong mã, bạn có thể sử dụng API libfdt
để vận hành DTB hoặc DTBO).
API DTO libufdt
API chính cho DTO trong libufdt
như sau:
struct fdt_header *ufdt_apply_overlay( struct fdt_header *main_fdt_header, size_t main_fdt_size, void *overlay_fdt, size_t overlay_size);
Tham số main_fdt_header
là DT chính và overlay_fdt
là vùng đệm chứa nội dung của tệp .dtbo
. Giá trị trả về là một vùng đệm mới chứa DT đã hợp nhất (hoặc null
trong trường hợp xảy ra lỗi). DT đã hợp nhất được định dạng trong FDT, bạn có thể truyền DT này đến hạt nhân khi khởi động hạt nhân.
Vùng đệm mới từ giá trị trả về được tạo bởi dto_malloc()
, bạn nên triển khai vùng đệm này khi chuyển libufdt
vào trình tải khởi động.
Để triển khai tham chiếu, hãy tham khảo sysdeps/libufdt_sysdeps_*.c
.
Quy định hạn chế về nút gốc
Bạn không thể phủ một nút hoặc thuộc tính mới vào nút gốc của DT chính vì các thao tác phủ dựa vào nhãn. Vì DT chính phải xác định một nhãn và lớp phủ DT gán các nút cần phủ nhãn, nên bạn không thể cung cấp nhãn cho nút gốc (và do đó không thể phủ lên nút gốc).
Nhà cung cấp SoC phải xác định khả năng phủ của DT chính; ODM/OEM chỉ có thể thêm hoặc ghi đè các nút có nhãn do nhà cung cấp SoC xác định. Để khắc phục, bạn có thể xác định một nút odm
bên dưới nút gốc trong DT cơ sở, bật tất cả các nút ODM trong DT lớp phủ để thêm các nút mới.
Ngoài ra, bạn có thể đặt tất cả các nút liên quan đến SoC trong DT cơ sở vào nút soc
trong nút gốc như mô tả dưới đây:
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 { ... }; ... }; |
Sử dụng lớp phủ nén
Android 9 hỗ trợ thêm việc sử dụng lớp phủ nén trong hình ảnh DTBO khi sử dụng phiên bản 1 của tiêu đề bảng DT. Khi sử dụng tiêu đề DTBO phiên bản 1, 4 bit có giá trị ít quan trọng nhất của trường cờ trong dt_table_entry cho biết định dạng nén của mục 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 */ };
Hiện tại, chúng tôi hỗ trợ định dạng nén zlib
và gzip
.
enum dt_compression_info { NO_COMPRESSION, ZLIB_COMPRESSION, GZIP_COMPRESSION };
Android 9 hỗ trợ thêm tính năng kiểm thử lớp phủ được nén vào kiểm thử VtsFirmwareDtboVerification
để giúp bạn xác minh tính chính xác của ứng dụng lớp phủ.
Triển khai DTO mẫu
Các hướng dẫn sau đây sẽ hướng dẫn bạn cách triển khai DTO một cách mẫu bằng libufdt
(mã mẫu bên dưới).
Hướng dẫn mẫu về DTO
- Bao gồm các thư viện. Để sử dụng
libufdt
, hãy thêmlibfdt
cho các cấu trúc dữ liệu và API:#include <libfdt.h> #include <ufdt_overlay.h>
- Tải DT chính và DT lớp phủ. Tải
.dtb
và.dtbo
từ bộ nhớ vào bộ nhớ (các bước chính xác phụ thuộc vào thiết kế của bạn). Tại thời điểm này, bạn sẽ có bộ đệm và kích thước của.dtb
/.dtbo
:main_size = my_load_main_dtb(main_buf, main_buf_size)
overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
- Lớp phủ DT:
- Sử dụng
ufdt_install_blob()
để lấy tiêu đề FDT cho DT chính:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Gọi
ufdt_apply_overlay()
đến DTO để nhận DT đã hợp nhất ở định dạng FDT:merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size);
- Sử dụng
merged_fdt
để lấy kích thước củadtc_totalsize()
:merged_fdt_size = dtc_totalsize(merged_fdt);
- Truyền DT đã hợp nhất để khởi động hạt nhân:
my_kernel_entry(0, machine_type, merged_fdt);
- Sử dụng
Mã DTO mẫu
#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); }