Optymalizacja organizacji zajmujących się handlem narkotykami

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), libufdtczas wykonywania DTO wynosi 10 ms.

Testy wydajności urządzeń Pixel w porównaniu z libufdtlibfdt. 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ń

  1. Dołącz biblioteki. Aby użyć libufdt, dołącz libfdt dla struktur danych i interfejsów API:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
  2. Załaduj główny DT i nakładany DT. Przeładuj .dtb.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);
  3. Nakładanie DT:
    1. 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;
    2. 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);
    3. Aby uzyskać rozmiar elementu dtc_totalsize(), użyj operatora merged_fdt:
      merged_fdt_size = dtc_totalsize(merged_fdt);
    4. Przekaż scalone drzewo urządzeń, aby uruchomić jądro:
      my_kernel_entry(0, machine_type, merged_fdt);

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);
}