Optymalizacja organizacji zajmujących się handlem narkotykami

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 organizacje zajmujące się handlem narkotykami, 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ładki (dołączenia lub zastąpienia) mają od 8 do 10 razy 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 organizacji DTO

Poniżej znajdziesz instrukcje przykładowej implementacji organizacji DTO za pomocą libufdt (przykładowy kod poniżej).

Przykładowe instrukcje dotyczące organizacji zajmujących się handlem narkotykami

  1. 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>
    
  2. 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);
    
  3. Nakładanie plików przenoszenia danych:
    1. 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;
      
    2. 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);
      
    3. Użyj funkcji merged_fdt, aby uzyskać rozmiar dtc_totalsize():
      merged_fdt_size = dtc_totalsize(merged_fdt);
      
    4. Aby uruchomić jądro, przekaż scalony plik przenoszenia danych:
      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);
}