Phân vùng DTB và DTBO

Nếu blob cây thiết bị (DTB) hoặc blob cây thiết bị cho lớp phủ (DTBO) nằm trong một phân vùng duy nhất, chẳng hạn như phân vùng dtbdtbo, hãy sử dụng cấu trúc bảng và định dạng tiêu đề sau:

Hình 1. Ví dụ về bố cục phân vùng DTB và DTBO.

Cấu trúc dữ liệu

dt_table_header chỉ dành cho phân vùng dtb/dtbo; bạn KHÔNG THỂ thêm định dạng này vào sau image.gz. Nếu có một DTB hoặc DTBO, bạn vẫn phải sử dụng định dạng này (và dt_entry_count trong dt_table_header là 1).

#define DT_TABLE_MAGIC 0xd7b7ab1e

struct dt_table_header {
  uint32_t magic;             // DT_TABLE_MAGIC
  uint32_t total_size;        // includes dt_table_header + all dt_table_entry
                              // and all dtb/dtbo
  uint32_t header_size;       // sizeof(dt_table_header)

  uint32_t dt_entry_size;     // sizeof(dt_table_entry)
  uint32_t dt_entry_count;    // number of dt_table_entry
  uint32_t dt_entries_offset; // offset to the first dt_table_entry
                              // from head of dt_table_header

  uint32_t page_size;         // flash page size we assume
  uint32_t version;       // DTBO image version, the current version is 0.
                          // The version is incremented when the
                          // dt_table_header struct is updated.
};

struct dt_table_entry {
  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 custom[4];         // optional, must be zero if unused
};

Để đọc tất cả dt_table_entry, hãy sử dụng dt_entry_size, dt_entry_countdt_entries_offset. Ví dụ:

my_read(entries_buf,
        header_addr + header->dt_entries_offset,
        header->dt_entry_size * header->dt_entry_count);

id, rev, custom trong dt_table_entry là các mã nhận dạng phần cứng không bắt buộc của cây thiết bị mà trình tải khởi động có thể sử dụng để xác định hiệu quả DTB hoặc DTBO cần tải. Nếu trình tải khởi động yêu cầu thêm thông tin, hãy đặt thông tin đó vào DTB hoặc DTBO để trình tải khởi động có thể đọc thông tin đó bằng cách phân tích cú pháp DTB hoặc DTBO (xem mã mẫu bên dưới).

Mã mẫu

Mã mẫu sau đây kiểm tra thông tin nhận dạng phần cứng trong trình tải khởi động.

  • Hàm check_dtbo() kiểm tra mã nhận dạng phần cứng. Trước tiên, hàm này sẽ kiểm tra dữ liệu trong cấu trúc dt_table_entry (id, rev, v.v.). Nếu dữ liệu này không đủ, thì ứng dụng sẽ tải dữ liệu dtb vào bộ nhớ và kiểm tra giá trị trong dtb.
  • Các giá trị của thuộc tính my_hw_informationsoc_id được phân tích cú pháp trong nút gốc (ví dụ trong my_dtbo_1.dts).
    [my_dtbo_1.dts]
    /dts-v1/;
    /plugin/;
    
    / {
      /* As DTS design, these properties only for loader, won't overlay */
      compatible = "board_manufacturer,board_model";
    
      /* These properties are examples */
      board_id = <0x00010000>;
      board_rev = <0x00010001>;
      another_hw_information = "some_data";
      soc_id = <0x68000000>;
      ...
    };
    
    &device@0 {
      value = <0x1>;
      status = "okay";
    };
    
    
    [my_bootloader.c]
    int check_dtbo(const dt_table_entry *entry, uint32_t header_addr) {
      ...
      if (entry->id != ... || entry->rev != ...) {
        ...
      }
      ...
      void * fdt_buf = my_load_dtb(header_addr + entry->dt_offset, entry->dt_size);
      int root_node_off = fdt_path_offset(fdt_buf, "/");
      ...
      const char *my_hw_information =
        (const char *)fdt_getprop(fdt_buf, root_node_off, "my_hw_information", NULL);
      if (my_hw_information != NULL && strcmp(my_hw_information, ...) != 0) {
        ...
      }
      const fdt32_t *soc_id = fdt_getprop(fdt_buf, root_node_off, "soc_id", NULL);
      if (soc_id != NULL && *soc_id != ...) {
        ...
      }
      ...
    }

mkdtimg

mkdtimg là một công cụ để tạo hình ảnh dtb/dtbo (mã nguồn tại system/libufdt trong AOSP). mkdtimg hỗ trợ một số lệnh, bao gồm create, cfg_createdump.

create

Sử dụng lệnh create để tạo hình ảnh dtb/dtbo:

mkdtimg create <image_filename> (<global-option>...) \
    <ftb1_filename> (<entry1_option>...) \
    <ftb2_filename> (<entry2_option>...) \
    ...

ftbX_filename tạo một dt_table_entry trong hình ảnh. entryX_option là các giá trị cần gán cho dt_table_entry. Các giá trị này có thể là bất kỳ giá trị nào sau đây:

--id=<number|path>
--rev=<number|path>
--custom0=<number|path>
--custom1=<number|path>
--custom2=<number|path>
--custom3=<number|path>

Giá trị số có thể là chữ số 32 bit (chẳng hạn như 68000) hoặc số thập lục phân (chẳng hạn như 0x6800). Ngoài ra, bạn có thể chỉ định một đường dẫn bằng định dạng:

<full_node_path>:<property_name>

Ví dụ: /board/:id. mkdtimg đọc giá trị từ đường dẫn trong tệp DTB hoặc DTBO và gán giá trị (32 bit) cho một thuộc tính tương đối trong dt_table_entry. Ngoài ra, bạn có thể đặt global_option làm tuỳ chọn mặc định cho tất cả các mục nhập. Giá trị mặc định của page_size trong dt_table_header là 2048; hãy sử dụng global_option --page_size=<number> để chỉ định một giá trị khác.

Ví dụ:

[board1.dts]
/dts-v1/;
/plugin/;

/ {
  compatible = "board_manufacturer,board_model";
  board_id = <0x00010000>;
  board_rev = <0x00010001>;
  another_hw_information = "some_data";
  ...
};

&device@0 {
  value = <0x1>;
  status = "okay";
};


mkdtimg create dtbo.img --id=/:board_id --custom0=0xabc \
  board1.dtbo \
  board2.dtbo --id=0x6800 \
  board3.dtbo --id=0x6801 --custom0=0x123
  • dt_table_entry đầu tiên (board1.dtbo) id0x00010000custom[0]0x00000abc.
  • id thứ hai là 0x00006800custom[0]0x00000abc.
  • id thứ ba là 0x00006801custom[0]0x00000123.
  • Tất cả các giá trị khác đều sử dụng giá trị mặc định (0).

cfg_create

Lệnh cfg_create tạo một hình ảnh có tệp cấu hình ở định dạng sau:

# global options
  <global_option>
  ...
# entries
<ftb1_filename>     # comment
  <entry1_option>   # comment
  ...
<ftb2_filename>
  <entry2_option>
  ...
...

Tuỳ chọn global_optionentryX_option phải bắt đầu bằng một hoặc nhiều ký tự dấu cách (các tuỳ chọn này giống với tuỳ chọn create, không có tiền tố --). Các dòng trống hoặc dòng bắt đầu bằng # sẽ bị bỏ qua.

Ví dụ:

[dtboimg.cfg]
# global options
  id=/:board_id
  rev=/:board_rev
  custom0=0xabc

board1.dtbo

board2.dtbo
  id=0x6800       # override the value of id in global options

board2.dtbo
  id=0x6801       # override the value of id in global options
  custom0=0x123   # override the value of custom0 in global options


mkdtimg cfg_create dtbo.img dtboimg.cfg

mkdtimg không xử lý việc căn chỉnh cho các tệp .dtb/.dtbo mà thay vào đó sẽ thêm các tệp đó vào hình ảnh. Khi sử dụng dtc để biên dịch .dts thành .dtb/.dtbo, bạn phải thêm tuỳ chọn -a. Ví dụ: việc thêm tuỳ chọn -a 4 sẽ thêm khoảng đệm để kích thước của .dtb/.dtbo khớp với 4 byte.

Một số mục trong bảng DT có thể dùng chung .dtb/.dtbo. Nếu bạn sử dụng cùng một tên tệp cho nhiều mục, thì tên tệp đó sẽ chỉ lưu trữ một nội dung trong hình ảnh có cùng dt_offsetdt_size. Điều này rất hữu ích khi sử dụng nhiều phần cứng có DT giống hệt nhau.

tệp kết xuất

Đối với hình ảnh dtb/dtbo, hãy sử dụng lệnh dump để in thông tin trong hình ảnh. Ví dụ:

mkdtimg dump dtbo.img
dt_table_header:
               magic = d7b7ab1e
          total_size = 1300
         header_size = 32
       dt_entry_size = 32
      dt_entry_count = 3
   dt_entries_offset = 32
           page_size = 2048
             version = 0
dt_table_entry[0]:
             dt_size = 380
           dt_offset = 128
                  id = 00010000
                 rev = 00010001
           custom[0] = 00000abc
           custom[1] = 00000000
           custom[2] = 00000000
           custom[3] = 00000000
           (FDT)size = 380
     (FDT)compatible = board_manufacturer,board_model
...