Na tej stronie omawiamy optymalizacje, które możesz wprowadzić w implementacji nakładek drzewa urządzeń, opisujemy ograniczenia dotyczące nakładania na węzeł główny oraz wyjaśniamy, jak skonfigurować skompresowane nakładki w obrazie DTBO. Znajdziesz tu też przykładowe instrukcje implementacji i kod.
Wiersz poleceń jądra
Oryginalny wiersz poleceń jądra w drzewie urządzeń (DT) znajduje się w węźle
chosen/bootargs. Program rozruchowy musi połączyć tę lokalizację z innymi źródłami wiersza poleceń jądra systemu:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
Nakładki drzewa urządzeń nie mogą łączyć wartości z głównego drzewa urządzeń i nakładki drzewa urządzeń, dlatego
wiersz poleceń jądra głównego drzewa urządzeń musisz umieścić w
chosen/bootargs i wiersz poleceń jądra nakładki drzewa urządzeń w
chosen/bootargs_ext. Program rozruchowy może następnie połączyć te lokalizacje i przekazać wynik do jądra systemu (operacyjnego).
| main.dts | overlay.dts |
|---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; |
/dts-v1/;
/plugin/;
&chosen {
bootargs_ext = "...";
}; |
libufdt
Choć najnowsze
libfdt
obsługuje nakładki drzewa urządzeń, do ich wdrożenia zalecamy użycie zasady libufdt
(Źródło AOSP:
platform/system/libufdt).
libufdt tworzy prawdziwą strukturę drzewa (niespłaszczone drzewo urządzeń,
lub ufdt) z płaskiego drzewa urządzeń (FDT), aby poprawić
połączenie dwóch plików .dtb z zakresu O(N2) do O(N), gdzie N określa liczbę węzłów w drzewie.
Testowanie wydajności
W testach wewnętrznych Google użycie libufdt na 2405
.dtb i 283 .dtbo węzłach drzewa urządzeń powoduje, że rozmiary plików po kompilacji wynoszą
70 618 i 8566 bajtów. W porównaniu z
implementacją nakładek drzewa urządzeń
przeniesioną z FreeBSD (czas działania 124 ms) czas działania nakładek drzewa urządzeń libufdt
wynosi 10 ms.
W testach wydajności na urządzeniach Pixel porównano libufdt i
libfdt. Liczba węzłów bazowych ma podobny wpływ, ale obejmuje
te różnice:
- 500 operacji nakładania (dołączania lub zastępowania) ma 6–8-krotną różnicę czasu .
- 1000 operacji nakładania (dołączania lub zastępowania) ma 8–10-krotną różnicę czasu .
Przykład z liczbą dołączania ustawioną na X:

Rysunek 1. Liczba dołączania wynosi X.
Przykład z liczbą zastępowania ustawioną na X:

Rysunek 2. Liczba zastępowania wynosi X.
libufdt jest opracowywana przy użyciu niektórych interfejsów API i struktur danych libfdt. Gdy używasz libufdt, musisz dołączyć i połączyć
libfdt (jednak w kodzie możesz używać interfejsu API libfdt
do obsługi DTB lub DTBO).
Interfejs API nakładek drzewa urządzeń libufdt
Główny interfejs API nakładek drzewa urządzeń w libufdt jest taki:
struct fdt_header *ufdt_apply_overlay(
struct fdt_header *main_fdt_header,
size_t main_fdt_size,
void *overlay_fdt,
size_t overlay_size);
Parametr main_fdt_header to główne drzewo urządzeń, a
overlay_fdt to bufor zawierający zawartość pliku
.dtbo. Wartość zwracana to nowy bufor zawierający scalone drzewo urządzeń (lub null w przypadku błędu). Scalone drzewo urządzeń jest sformatowane
w FDT, które możesz przekazać do jądra podczas jego uruchamiania.
Nowy bufor z wartości zwracanej jest tworzony przez dto_malloc(),
który należy zaimplementować podczas portowania libufdt do programu rozruchowego.
Implementacje referencyjne znajdziesz w
sysdeps/libufdt_sysdeps_*.c.
Ograniczenia dotyczące węzła głównego
Nie możesz nałożyć nowego węzła ani właściwości na węzeł główny głównego drzewa urządzeń ponieważ operacje nakładania opierają się na etykietach. Główne drzewo urządzeń musi definiować etykietę , a nakładka drzewa urządzeń przypisuje etykiety do węzłów, które mają zostać nałożone, dlatego nie możesz przypisać etykiety do węzła głównego (a tym samym nie możesz nałożyć węzła głównego ).
Dostawcy układów SOC muszą zdefiniować możliwość nakładania na główne drzewo urządzeń. Producenci ODM/OEM mogą tylko
dołączać lub zastępować węzły z etykietami zdefiniowanymi przez dostawcę układów SOC. Aby
obejść ten problem, możesz zdefiniować węzeł odm pod
węzłem głównym w podstawowym drzewie urządzeń, co umożliwi wszystkim węzłom ODM w nakładce drzewa urządzeń dodawanie nowych węzłów.
Możesz też umieścić wszystkie węzły związane z układem SOC w podstawowym drzewie urządzeń w
soc węźle pod węzłem głównym, jak opisano poniżej:
| 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 { ... }; ... }; |
Używanie skompresowanych nakładek
Android 9 dodaje obsługę używania skompresowanych nakładek w obrazie DTBO, gdy używasz wersji 1 nagłówka tabeli DT. Gdy używasz nagłówka DTBO w wersji 1, 4 najmniej znaczące bity pola flagi w dt_table_entry wskazują format kompresji wpisu 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 */ };
Obecnie obsługiwane są kompresje zlib i gzip.
enum dt_compression_info { NO_COMPRESSION, ZLIB_COMPRESSION, GZIP_COMPRESSION };
Android 9 dodaje obsługę testowania skompresowanych
nakładek do testu VtsFirmwareDtboVerification, aby pomóc Ci
sprawdzić poprawność aplikacji nakładki.
Przykładowa implementacja nakładek drzewa urządzeń
Poniżej znajdziesz instrukcje przykładowej implementacji nakładek drzewa urządzeń
za pomocą libufdt (przykładowy kod poniżej).
Przykładowe instrukcje dotyczące nakładek drzewa urządzeń
- Dołącz biblioteki. Aby używać
libufdt, dołączlibfdtdla struktur danych i interfejsów API:#include <libfdt.h> #include <ufdt_overlay.h>
- Wczytaj główne drzewo urządzeń i nakładkę drzewa urządzeń. Wczytaj
.dtbi.dtboz pamięci do pamięci (dokładne kroki zależą od Twojego projektu). W tym momencie, powinny być dostępne bufor i rozmiar.dtb/.dtbo:main_size = my_load_main_dtb(main_buf, main_buf_size)
overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
- Nałóż drzewa urządzeń:
- Użyj
ufdt_install_blob(), aby pobrać nagłówek FDT dla głównego drzewa urządzeń:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Wywołaj
ufdt_apply_overlay()do nakładek drzewa urządzeń, aby uzyskać scalone drzewo urządzeń w formacie FDT format:merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size); - Użyj
merged_fdt, aby uzyskać rozmiardtc_totalsize():merged_fdt_size = dtc_totalsize(merged_fdt);
- Przekaż scalone drzewo urządzeń, aby uruchomić jądro:
my_kernel_entry(0, machine_type, merged_fdt);
- Użyj
Przykładowy kod nakładek drzewa urządzeń
#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); }