Na tej stronie omówiono optymalizacje, które można wprowadzić w implementacji DTO, opisano ograniczenia dotyczące nakładania węzła głównego i szczegółowo opisano sposób konfigurowania skompresowanych nakładek w obrazie DTBO. Zawiera także przykładowe instrukcje i kod implementacji.
Wiersz poleceń jądra
Oryginalna linia poleceń jądra w drzewie urządzeń znajduje się w chosen/bootargs
. Program ładujący 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 ID i nakładki DT, więc musisz umieścić linię poleceń jądra głównego DT w chosen/bootargs
i linię poleceń jądra nakładki DT w chosen/bootargs_ext
. Program ładujący może następnie połączyć te lokalizacje i przekazać wynik do jądra.
główne.dts | nakładka.dts |
---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; | /dts-v1/; /plugin/; &chosen { bootargs_ext = "..."; }; |
libufdt
Chociaż najnowsza libfdt
obsługuje DTO, czy zaleca się używanie libufdt
do implementacji DTO (źródło AOSP w platform/system/libufdt
). libufdt
tworzy prawdziwą strukturę drzewa (niespłaszczone drzewo urządzeń, ufdt ) ze spłaszczonego drzewa urządzeń (FDT), dzięki czemu może usprawnić łączenie dwóch plików .dtb
od O(N 2 ) do O(N), gdzie N to liczba węzłów w drzewie.
Test wydajności
W wewnętrznych testach Google użycie biblioteki libufdt
w węzłach DT 2405 .dtb
i 283 .dtbo
DT po kompilacji dało rozmiary plików 70 618 i 8566 bajtów. W porównaniu z implementacją DTO przeniesioną z FreeBSD (czas działania 124 ms), czas wykonania libufdt
DTO wynosi 10 ms.
Testy wydajności urządzeń Pixel porównały libufdt
i libfdt
. Efekt liczby węzłów podstawowych jest podobny, ale zawiera następujące różnice:
- 500 operacji nakładania (dodawania lub zastępowania) ma różnicę czasu od 6 do 8 razy
- 1000 operacji nakładania (dodawania lub zastępowania) ma różnicę czasu od 8 do 10 razy
Przykład z dodaną liczbą ustawioną na X:
Przykład z nadrzędną liczbą ustawioną na X:
libufdt
jest rozwijany z niektórymi interfejsami API i strukturami danych libfdt
. Używając libufdt
, musisz dołączyć i połączyć libfdt
(jednak w swoim kodzie możesz użyć API libfdt
do obsługi DTB lub DTBO).
API DTO libufdt
Główne API DTO w libufdt
jest następujące:
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
jest głównym ID, a overlay_fdt
jest buforem zawierającym zawartość pliku .dtbo
. Wartość zwracana to nowy bufor zawierający scalony identyfikator (lub null
w przypadku błędu). Połączony ID jest sformatowany w formacie FDT, który można przekazać do jądra podczas uruchamiania jądra.
Nowy bufor ze zwracanej wartości jest tworzony przez dto_malloc()
, którą powinieneś zaimplementować podczas przenoszenia libufdt
do bootloadera. Implementacje referencyjne można znaleźć w sysdeps/libufdt_sysdeps_*.c
.
Ograniczenia węzła głównego
Nie można nałożyć nowego węzła lub właściwości na węzeł główny głównego ID, ponieważ operacje nakładania opierają się na etykietach. Ponieważ główny ID musi definiować etykietę, a nałożony ID przypisuje węzły, na które mają zostać nałożone etykiety, nie można nadać etykiety węzłowi głównemu (i dlatego nie można nałożyć węzła głównego).
Dostawcy SoC muszą zdefiniować możliwość nakładania się głównego ID; Producenci ODM/OEM mogą dołączać lub zastępować węzły wyłącznie z etykietami zdefiniowanymi przez dostawcę SoC. W ramach obejścia można zdefiniować węzeł odm
pod węzłem głównym w podstawowym ID, umożliwiając wszystkim węzłom ODM w nakładkowym ID dodawanie nowych węzłów. Alternatywnie możesz umieścić wszystkie węzły związane z SoC w podstawowym ID w węźle soc
w węźle głównym, jak opisano poniżej:
główne.dts | nakładka.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
W systemie Android 9 dodano obsługę skompresowanych nakładek w obrazie DTBO podczas korzystania z wersji 1 nagłówka tabeli drzewa urządzeń. Gdy używany jest nagłówek DTBO v1, cztery najmniej znaczące bity pola flag 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' 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 */ };
Obecnie obsługiwane są kompresje zlib
i gzip
.
enum dt_compression_info { NO_COMPRESSION, ZLIB_COMPRESSION, GZIP_COMPRESSION };
W systemie Android 9 dodano obsługę testowania skompresowanych nakładek do testu VtsFirmwareDtboVerification
, który pomaga zweryfikować poprawność aplikacji nakładek.
Przykładowa implementacja DTO
Poniższe instrukcje przeprowadzą Cię przez przykładową implementację DTO z libufdt
(przykładowy kod poniżej).
Przykładowe instrukcje DTO
- Uwzględnij 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 ID i nałożony ID. Załaduj
.dtb
i.dtbo
z pamięci do pamięci (dokładne kroki zależą od projektu). W tym momencie powinieneś mieć 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łóż ID:
- Użyj
ufdt_install_blob()
, aby uzyskać nagłówek FDT dla głównego ID:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Wywołaj funkcję
ufdt_apply_overlay()
do DTO, aby uzyskać połączony identyfikator ID 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ż połączony identyfikator, aby uruchomić jądro:
my_kernel_entry(0, machine_type, merged_fdt);
- Użyj
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); }