Na tej stronie omawiamy optymalizacje, które można wprowadzić w implementacji nakładki drzewa urządzenia, opisuje ograniczenia zapobiegające nakładaniu się węzła głównego oraz szczegóły skonfigurować skompresowane nakładki w obrazie DTBO. Udostępnia ona również za pomocą instrukcji i kodu implementacji.
Wiersz poleceń jądra systemu
Oryginalny wiersz poleceń jądra w drzewie urządzeń (DT) znajduje się w
chosen/bootargs
węzeł. Program rozruchowy musi łączyć ten element
lokalizację za pomocą innych źródeł wiersza poleceń jądra:
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; };
DTO nie może łączyć wartości z głównego przenoszenia danych i nakładanego przenoszenia danych, więc
musisz umieścić wiersz poleceń jądra głównego pliku DT w
chosen/bootargs
i wiersza poleceń jądra w pliku przenoszenia danych nakładki w
chosen/bootargs_ext
Program rozruchowy może wtedy połączyć te elementy
lokalizacji i przekazać wynik do jądra systemu operacyjnego.
main.dts. | nakładek.dts |
---|---|
/dts-v1/; / { chosen: chosen { bootargs = "..."; }; }; |
/dts-v1/; /plugin/; &chosen { bootargs_ext = "..."; }; |
Libufdt
Choć najnowsze
libfdt
obsługuje nakładek drzewa urządzeń, czy 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 jest znakiem
i określ liczbę węzłów w drzewie.
Testy wydajności
W testach wewnętrznych Google przy użyciu interfejsu libufdt
w wersji 2405
W przypadku .dtb
i 283 .dtbo
węzłów DT pliki o rozmiarze
70 618 i 8566 bajtów po skompilowaniu. W porównaniu z
DTO
implementacja przeniesiona z FreeBSD (czas działania: 124 ms), libufdt
Czas działania DTO wynosi 10 ms.
Testy wydajności urządzeń Pixel w porównaniu z: libufdt
i
libfdt
Efekt liczby węzłów podstawowych jest podobny, ale uwzględnia
następujące różnice:
- 500 operacji nakładania (dołączenia lub zastępowania) ma 6–8 razy czas trwania różnica
- 1000 operacji nakładania (dołączenia lub zastępowania) ma 8–10-krotny czas trwania. różnica
Przykład z liczbą dołączaną ustawioną na X:
Rysunek 1. Liczba dołączanych elementów to X.
Przykład z liczbą zastąpień ustawioną na X:
Rysunek 2. Liczba zastąpień wynosi X.
Program libufdt
został opracowany z użyciem niektórych interfejsów API i danych libfdt
w różnych strukturach. Jeśli używasz libufdt
, musisz dołączyć atrybut i link
libfdt
(w kodzie możesz jednak użyć funkcji libfdt
API do obsługi DTB lub DTBO).
libufdt DTO API,
Główny interfejs API używany przez DTO w regionie 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
jest głównym ciągiem przenoszenia danych i
overlay_fdt
to bufor zawierający zawartość pliku
.dtbo
. Zwracana wartość to nowy bufor zawierający
scalone przenoszenie danych (lub null
w przypadku błędu). Scalony plik przenoszenia danych jest sformatowany
w FDT, który można przekazać do jądra przy uruchamianiu jądra.
Nowy bufor zwracanej wartości jest tworzony przez funkcję dto_malloc()
,
który należy wdrożyć podczas przenoszenia pakietu libufdt
do programu rozruchowego.
Implementacje referencyjne znajdziesz tutaj:
sysdeps/libufdt_sysdeps_*.c
Ograniczenia węzłów głównych
Nie można nakładać nowego węzła ani usługi na węzeł główny głównego przenoszenia danych ponieważ operacje nakładania opierają się na etykietach. Ponieważ główny plik przenoszenia danych musi definiować , a nakładka DT przypisuje węzły do nałożenia na etykiety. nie można nadać etykiety węzła głównemu (i dlatego nie może nakładać się na węzeł główny) ).
Dostawcy SoC muszą określić możliwość nakładania się głównego przenoszenia danych. ODM/OEM mogą mieć tylko
dołączanie lub zastępowanie węzłów etykietami zdefiniowanymi przez dostawcę układu SOC. Jako
możesz obejść ten problem, możesz zdefiniować węzeł odm
w
węzeł główny w podstawowym interfejsie przenoszenia danych, dzięki czemu wszystkie węzły ODM w nakładki DT mogą dodawać nowe węzły.
Można też umieścić wszystkie węzły związane z SOC
soc
w węźle głównym, jak opisano poniżej:
main.dts. | nakładek.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 { ... }; ... }; |
Korzystanie z skompresowanych nakładek
W Androidzie 9 można korzystać z skompresowanych nakładek w obrazie DTBO, gdy używana jest wersja 1 nagłówka tabeli DT. Jeśli używany jest nagłówek DTBO w wersji 1, cztery najmniej istotne bity pola flag w dt_table_entry wskazują format kompresji wpisu przenoszenia danych.
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 };
W Androidzie 9 dodano obsługę testowania skompresowanych
nakładki na test VtsFirmwareDtboVerification
, aby pomóc
zweryfikować 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ń
- Uwzględnij biblioteki. Aby użyć
libufdt
, uwzględnij:libfdt
w przypadku struktur danych i interfejsów API:#include <libfdt.h> #include <ufdt_overlay.h>
- Wczytaj główny plik przenoszenia danych i nałożony plik przenoszenia danych. Wczytaj
.dtb
i.dtbo
z miejsca na dane do pamięci (dokładne czynności 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);
- Nakładanie plików przenoszenia danych:
- Użyj pola
ufdt_install_blob()
, by pobrać nagłówek FDT dla głównego pliku przenoszenia danych:main_fdt_header = ufdt_install_blob(main_buf, main_size); main_fdt_size = main_size;
- Wywołaj
ufdt_apply_overlay()
do DTO, aby uzyskać scalone przenoszenie danych w FDT format:merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size, overlay_buf, overlay_size);
- Użyj funkcji
merged_fdt
, aby uzyskać rozmiardtc_totalsize()
:merged_fdt_size = dtc_totalsize(merged_fdt);
- Aby uruchomić jądro, przekaż scalony plik przenoszenia danych:
my_kernel_entry(0, machine_type, merged_fdt);
- Użyj pola
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); }