Na tej stronie omawiamy optymalizacje, które możesz wprowadzić w implementacji nakładki drzewa urządzeń (DTO), opisujemy ograniczenia dotyczące nakładania węzła głównego i wyjaśniamy, jak skonfigurować skompresowane nakładki w obrazie DTBO. Zawiera 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:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
DTO nie może łączyć wartości z głównego DT i nakładki DT, więc wiersz poleceń jądra głównego DT musisz umieścić w chosen/bootargs
, a wiersz poleceń jądra nakładki DT w chosen/bootargs_ext
. Program rozruchowy może następnie połą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 testach wewnętrznych Google użycie libufdt
na 2405
.dtb
i 283 .dtbo
węzłach DT powoduje, że po kompilacji rozmiary plików wynoszą 70 618 i 8566 bajtów. W porównaniu z implementacją nakładek drzewa urządzeń przeniesioną z FreeBSD (czas wykonania 124 ms) czas wykonania nakładek drzewa urządzeń wynosi 10 ms.libufdt
Testy wydajności urządzeń Pixel w porównaniu z libufdt
i libfdt
. Efekt liczby węzłów bazowych jest podobny, ale ma następujące różnice:
- 500 operacji nakładania (dołączania lub zastępowania) ma 6–8-krotnie większą różnicę czasu
- 1000 operacji nakładania (dołączania lub zastępowania) ma 8–10-krotnie większą różnicę czasu
Przykład z ustawionym na X dołączaniem liczby:
Rysunek 1. Liczba dołączeń to X.
Przykład z zastąpieniem liczby ustawionym na X:
Rysunek 2. Liczba zastąpień to X.
libufdt
jest opracowywana przy użyciu interfejsów API i struktur danych libfdt
. Gdy używasz libufdt
, musisz dołączyć i połączyć libfdt
(w kodzie możesz jednak używać interfejsu API libfdt
do obsługi DTB lub DTBO).
libufdt DTO API
Główny interfejs API do DTO w libufdt
wygląda tak:
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
. Wartością zwracaną jest nowy bufor zawierający scalony DT (lub null
w przypadku błędu). Połączone drzewo urządzeń jest sformatowane w FDT, które możesz przekazać do jądra podczas jego uruchamiania.
Nowy bufor z wartością zwracaną jest tworzony przez dto_malloc()
, co należy zaimplementować podczas przenoszenia libufdt
do programu rozruchowego.
Przykłady implementacji znajdziesz w sekcji 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 danych, ponieważ operacje nakładania zależą od etykiet. Główny DT musi definiować etykietę, a nakładka DT przypisuje węzły do nakładania etykietami, więc nie możesz podać etykiety dla 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 głównego DT; ODM/OEM mogą tylko dołączać lub zastępować węzły etykietami zdefiniowanymi przez dostawcę układu SoC. Aby obejść ten problem, możesz zdefiniować węzeł odm
w węźle głównym w podstawowym pliku DT, co umożliwi 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 układem SoC w podstawowym pliku 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 dodaje obsługę skompresowanych nakładek w obrazie DTBO podczas korzystania z wersji 1 nagłówka tabeli DT. W przypadku używania nagłówka DTBO w wersji 1 cztery najmniej znaczące bity pola flagi w strukturze 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 w weryfikacji poprawności 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ń
- Uwzględnij biblioteki. Aby użyć
libufdt
, dodajlibfdt
w przypadku struktur danych i interfejsów API:#include <libfdt.h> #include <ufdt_overlay.h>
- Wczytaj główny DT i nakładkowy DT. Wczytaj
.dtb
i.dtbo
z pamięci do pamięci (dokładne czynności zależą od projektu). W tym momencie bufor i rozmiar powinny mieć wartość.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łóż DT:
- Aby uzyskać nagłówek FDT dla głównego drzewa urządzeń, użyj polecenia
ufdt_install_blob()
:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Wywołaj narzędzie
ufdt_apply_overlay()
, aby uzyskać scalone drzewo urządzeń w formacie FDT: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);
- Aby uzyskać nagłówek FDT dla głównego drzewa urządzeń, użyj polecenia
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); }