Na tej stronie znajdziesz informacje o optymalizacjach, które możesz wprowadzić w implementacji nakładki drzewa urządzenia (DTO). Dowiesz się też, jakie ograniczenia dotyczą nakładania na węzeł główny, oraz jak skonfigurować skompresowane nakładki w pliku DTBO. Znajdziesz w nim też instrukcje implementacji i przykładowy kod.
Wiersz poleceń jądra
Pierwotna linia poleceń jądra w drzewie urządzenia (DT) znajduje się w węźle chosen/bootargs
. Program rozruchowy musi złączać tę lokalizację z innymi źródłami linii poleceń jądra:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
DTO nie może konkatenować wartości z głównego DT i nakładkowego DT, więc
musisz umieścić wiersz poleceń jądra głównego DT w chosen/bootargs
, a wiersz poleceń jądra nakładkowego DT w chosen/bootargs_ext
. Bootloader może następnie złączyć te lokalizacje i przekazać wynik do jądra.
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.
Testy wydajności
W ramach testów wewnętrznych Google użycie funkcji libufdt
na 2405
.dtb
i 283 .dtbo
węzłach DT skutkowało rozmiarami plików wynoszącymi po kompilacji 70 618 i 8 566 bajtów. W porównaniu z implementacją DTO przeniesioną z FreeBSD (czas wykonywania 124 ms), libufdt
czas wykonywania DTO wynosi 10 ms.
Testy wydajności urządzeń Pixel w porównaniu z libufdt
i libfdt
. Liczba węzłów podstawowych ma podobny efekt, ale różni się w kilku aspektach:
- 500 operacji nakładania (dodawania lub zastępowania) zajmuje 6–8 razy więcej czasu
- 1000 operacji nakładania (dodawania lub zastępowania) zajmuje 8–10 razy więcej czasu.
Przykład z liczbą dodawania ustawioną na X:
Rysunek 1. Liczba dołączenia to X.
Przykład z liczbą zastąpienia ustawioną na X:
Rysunek 2. Liczba zastąpiona to X.
libufdt
jest opracowywany z wykorzystaniem niektórych interfejsów API libfdt
i struktur danych. Jeśli używasz interfejsu libufdt
, musisz go uwzględnić i do niego odwoływać (w kodzie możesz jednak używać interfejsu libfdt
do obsługi DTB lub DTBO).libfdt
Interfejs libufdt DTO API
Główny interfejs API do DTO w libufdt
:
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łówny DT, a overlay_fdt
to bufor zawierający zawartość pliku .dtbo
. Zwracany jest nowy bufor zawierający scalone dane typu DT (lub null
w przypadku błędu). Połączony DT jest sformatowany w formacie FDT, który można przekazać do jądra podczas uruchamiania jądra.
Nowy bufor z wartością zwracaną jest tworzony przez funkcję dto_malloc()
, którą należy zaimplementować podczas przenoszenia funkcji libufdt
do bootloadera.
Przykładowe implementacje znajdziesz w dokumentacji sysdeps/libufdt_sysdeps_*.c
.
Ograniczenia 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 DT, ponieważ operacje nakładania opierają się na etykietach. Ponieważ główny DT musi zdefiniować etykietę, a DT nakładki przypisuje etykiety do węzłów, nie możesz nadać etykiety węzłowi głównemu (a zatem nie możesz nałożyć etykiety na węzeł główny).
Dostawcy układów SoC muszą zdefiniować możliwości nakładania głównego DT; ODM/OEM mogą tylko dodawać lub zastępować węzły etykietami zdefiniowanymi przez dostawcę układu SoC. Aby obejść ten problem, możesz zdefiniować węzeł odm
pod węzłem głównym w podstawowym DT, umożliwiając w ten sposób wszystkim węzłom ODM w nakładce DT dodawanie nowych węzłów.
Możesz też umieścić wszystkie węzły związane z SoC w podstawowej wersji DT w węźle soc
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 obsługuje kompresowane nakładki w obrazie DTBO, gdy używasz wersji 1 nagłówka tabeli DT. W przypadku 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 umożliwia testowanie skompresowanych nakładek w ramach testu VtsFirmwareDtboVerification
, co ułatwia weryfikację prawidłowego działania 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żyć
libufdt
, dołączlibfdt
dla struktur danych i interfejsów API:#include <libfdt.h> #include <ufdt_overlay.h>
- Załaduj główny DT i nakładany DT. Przeładuj
.dtb
i.dtbo
z pamięci na pamięć (dokładne czynności zależą od projektu). W tym momencie rozmiar bufora powinien wynosić.dtb
/.dtbo
:main_size = my_load_main_dtb(main_buf, main_buf_size)
overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
- Nakładanie DT:
- Aby pobrać nagłówek FDT dla głównego drzewa urządzeń, użyj narzędzia
ufdt_install_blob()
:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Aby pobrać scalone drzewo urządzeń w formacie FDT, wywołaj funkcję
ufdt_apply_overlay()
w nakładce drzewa urządzeń:merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size);
- Aby uzyskać rozmiar elementu
dtc_totalsize()
, użyj operatoramerged_fdt
:merged_fdt_size = dtc_totalsize(merged_fdt);
- Przekaż scalone drzewo urządzeń, aby uruchomić jądro:
my_kernel_entry(0, machine_type, merged_fdt);
- Aby pobrać nagłówek FDT dla głównego drzewa urządzeń, użyj narzędzia
Przykładowy kod DTO
#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); }