Trang này thảo luận về các phương pháp tối ưu hoá mà bạn có thể thực hiện đối với việc 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ớp nút gốc và trình bày chi tiết cách định cấu hình các lớp phủ được nén trong hình ảnh DTBO. Trang này cũng cung cấp hướng dẫn và mã triển khai mẫu.
Dòng lệnh kernel
Dòng lệnh kernel 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 kernel:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
DTO không thể nối các giá trị từ DT chính và lớp phủ DT, vì vậy
bạn phải đặt dòng lệnh kernel của DT chính trong
chosen/bootargs và dòng lệnh kernel của lớp phủ DT trong
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 kernel.
| 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 xây dựng 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), vì vậy, nó có thể cải thiện việc
hợp nhất 2 tệp .dtb từ O(N2) thành O(N), trong đó N là số
nút trong cây.
Kiểm thử hiệu suất
Trong quá trình kiểm thử nội bộ của Google, việc sử dụng libufdt trên 2405
.dtb và 283 .dtbo nút DT sẽ tạo ra các tệp có kích thước
70.618 và 8.566 byte sau khi biên dịch. So với việc triển khai DTO được chuyển từ FreeBSD (thời gian chạy 124 ms), libufdt thời gian chạy DTO là 10 ms.
Quá trình kiểm thử 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 phủ lớp (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 phủ lớp (nối hoặc ghi đè) có sự khác biệt về thời gian từ 8 đến 10 lần
Ví dụ về việc nối số lượng được đặt thành X:

Hình 1. Số lượng nối là X.
Ví dụ về việc ghi đè số lượng được đặt thành X:

Hình 2. Số lượng ghi đè là X.
libufdt được phát triển bằng một số API và cấu trúc dữ liệu libfdt. Khi sử dụng libufdt, bạn phải thêm và liên kết
libfdt (tuy nhiên, trong mã của mình, 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à bộ đệm chứa nội dung của tệp
.dtbo Giá trị trả về là một bộ đệ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ể chuyển DT này đến kernel khi khởi động kernel.
Bộ đệm mới từ giá trị trả về được tạo bởi dto_malloc(),
bạn nên triển khai bộ đệm này khi chuyển libufdt vào trình tải khởi động.
Để biết các cách triển khai tham chiếu, hãy tham khảo
sysdeps/libufdt_sysdeps_*.c.
Quy định hạn chế đối với nút gốc
Bạn không thể phủ lớp 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ủ lớp dựa vào nhãn. Vì DT chính phải xác định một nhãn và lớp phủ DT chỉ định các nút được phủ lớp bằng nhãn, nên bạn không thể đặt nhãn cho nút gốc (và do đó không thể phủ lớp nút gốc ).
Nhà cung cấp SoC phải xác định khả năng phủ lớp của DT chính; ODM/OEM chỉ có thể
nối hoặc ghi đè các nút có nhãn do nhà cung cấp SoC xác định. Để khắc phục vấn đề này, bạn có thể xác định nút odm trong nút gốc trong DT cơ sở, cho phép tất cả các nút ODM trong lớp phủ DT 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ả bên dưới:
| 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 các lớp phủ được nén
Android 9 bổ sung hỗ trợ sử dụng các lớp phủ được nén trong hình ảnh DTBO khi sử dụng tiêu đề bảng DT phiên bản 1. Khi sử dụng tiêu đề DTBO phiên bản 1, 4 bit í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, hệ thống hỗ trợ các phương thức nén zlib và gzip.
enum dt_compression_info { NO_COMPRESSION, ZLIB_COMPRESSION, GZIP_COMPRESSION };
Android 9 bổ sung hỗ trợ kiểm thử các lớp phủ được nén
cho bài kiểm thử VtsFirmwareDtboVerification để giúp bạn
xác minh tính chính xác của ứng dụng phủ lớp.
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ẫu bằng libufdt (mã mẫu bên dưới).
Hướng dẫn DTO mẫu
- Thêm thư viện. Để sử dụng
libufdt, hãy thêmlibfdtcho các cấu trúc dữ liệu và API:#include <libfdt.h> #include <ufdt_overlay.h>
- Tải DT chính và lớp phủ DT. Tải
.dtbvà.dtbotừ 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);
- Phủ lớp các 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 để lấy 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);
- Chuyển DT đã hợp nhất để khởi động kernel:
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); }