Tối ưu hóa DTO

Trang này thảo luận về các tối ưu hóa mà bạn có thể thực hiện đối với việc triển khai DTO của mình, mô tả các hạn chế đối với việc xếp chồng nút gốc và chi tiết cách định cấu hình lớp phủ nén trong hình ảnh DTBO. Nó cũng cung cấp hướng dẫn triển khai mẫu và mã.

Dòng lệnh hạt nhân

Dòng lệnh kernel ban đầu trong cây thiết bị nằm ở nút chosen/bootargs . Bộ nạp khởi động phải nối vị trí này với các nguồn dòng lệnh kernel khác:

/dts-v1/;

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

DTO không thể ghép 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 kernel của DT chính trong chosen/bootargs và dòng lệnh kernel của DT lớp phủ trong chosen/bootargs_ext . Bootloader sau đó có thể nối các vị trí này và chuyển kết quả vào kernel.

main.dts lớp phủ.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 có 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 phẳng hoặc ufdt ) từ cây thiết bị phẳng (FDT), do đó, nó có thể cải thiện việc hợp nhất hai tệp .dtb từ O(N 2 ) đến O(N), trong đó N là số nút trong cây.

Kiểm tra năng suất

Trong thử nghiệm nội bộ của Google, việc sử dụng libufdt trên 2405 .dtb và 283 nút .dtbo DT 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 triển khai DTO được chuyển từ FreeBSD (thời gian chạy 124 ms), thời gian chạy libufdt DTO là 10 ms.

Kiểm tra hiệu suất cho các thiết bị Pixel so sánh libufdtlibfdt . Số lượng nút cơ sở có hiệu lực tương tự, nhưng bao gồm những khác biệt sau:

  • 500 thao tác lớp phủ (chắp thêm hoặc ghi đè) có chênh lệch thời gian từ 6 đến 8 lần
  • 1000 thao tác lớp phủ (chắp thêm hoặc ghi đè) có chênh lệch thời gian từ 8 đến 10 lần

Ví dụ với số lượng bổ sung được đặt thành X:

Hình 1. Số lượng bổ sung là X

Ví dụ với số lượng ghi đè được đặt thành X:

Hình 2. Số lượng ghi đè là X

libufdt được phát triển với một số cấu trúc dữ liệu và API libfdt . Khi sử dụng libufdt , bạn phải bao gồm và liên kết libfdt (tuy nhiên, trong mã của bạn, 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à bộ đệm mới chứa DT đã hợp nhất (hoặc null trong trường hợp có lỗi). DT đã hợp nhất được định dạng ở dạng FDT, mà bạn có thể chuyển sang 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 bộ nạp khởi động. Để biết cách triển khai tham khảo, hãy tham khảo sysdeps/libufdt_sysdeps_*.c .

Hạn chế 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 hoạt động lớp phủ phụ thuộc vào nhãn. Vì DT chính phải xác định nhãn và DT lớp phủ chỉ định các nút được phủ nhãn nên bạn không thể đặt nhãn cho nút gốc (và do đó không thể phủ lên nút gốc).

Các nhà cung cấp SoC phải xác định khả năng che 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 nút odm bên dưới 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 một nút soc bên dưới nút gốc như được mô tả bên dưới:

main.dts lớp phủ.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 bổ sung hỗ trợ 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 cây thiết bị. Khi sử dụng tiêu đề DTBO v1, bốn bit có trọng số thấp nhất của trường cờ trong dt_table_entry cho biết định dạng nén của mục nhập 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 */
};

Hiện tại, nén zlibgzip được hỗ trợ.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 bổ sung tính năng hỗ trợ kiểm tra lớp phủ nén vào bài kiểm tra 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ẫu bằng libufdt (mã mẫu bên dưới).

Hướng dẫn DTO mẫu

  1. Bao gồm các thư viện. Để sử dụng libufdt , hãy bao gồm libfdt cho cấu trúc dữ liệu và API:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
    
  2. Tải DT chính và DT lớp phủ. Tải .dtb.dtbo từ bộ lưu trữ vào bộ nhớ (các bước chính xác tùy thuộc vào thiết kế của bạn). Tại thời điểm này, bạn nên có bộ đệm và kích thước .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. Xếp chồng các DT:
    1. 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;
      
    2. Gọi ufdt_apply_overlay() tới 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);
      
    3. Sử dụng merged_fdt để lấy kích thước của dtc_totalsize() :
      merged_fdt_size = dtc_totalsize(merged_fdt);
      
    4. Truyền DT đã hợp nhất để khởi động kernel:
      my_kernel_entry(0, machine_type, merged_fdt);
      

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);
}