Zgodnie z wymaganiami dotyczącymi modułów jądra wprowadzonymi w Androidzie 8.0 wszystkie jądra systemów na chipie (SoC) muszą obsługiwać ładowalne moduły jądra.
Opcje konfiguracji jądra
Aby obsługiwać ładowalne moduły jądra, android-base.config we wszystkich popularnych jądrach zawiera te opcje konfiguracji jądra (lub ich odpowiedniki w wersji jądra):
CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y
Te opcje muszą być włączone we wszystkich jądrach urządzeń. Moduły jądra powinny też obsługiwać wyładowywanie i ponowne wczytywanie, o ile to możliwe.
Podpisywanie modułów
Podpisywanie modułów nie jest obsługiwane w przypadku modułów dostawców GKI. Na urządzeniach, które muszą obsługiwać uruchamianie w trybie zweryfikowanym, Android wymaga, aby moduły jądra znajdowały się na partycjach z włączoną funkcją dm-verity. Dzięki temu nie trzeba podpisywać poszczególnych modułów w celu potwierdzenia ich autentyczności.
Android 13 wprowadził koncepcję modułów GKI. Moduł GKI korzysta z infrastruktury podpisywania w czasie kompilacji jądra, aby rozróżniać GKI od innych modułów w czasie wykonywania.
Moduły bez podpisu mogą się wczytywać, o ile używają tylko symboli znajdujących się na liście dozwolonych lub dostarczonych przez inne moduły bez podpisu.
Aby ułatwić podpisywanie modułów GKI podczas kompilacji GKI za pomocą pary kluczy kompilacji jądra, w konfiguracji jądra GKI włączono CONFIG_MODULE_SIG_ALL=y
.
Aby uniknąć podpisywania modułów innych niż GKI podczas kompilowania jądra urządzenia, musisz dodać
# CONFIG_MODULE_SIG_ALL is not set
jako część fragmentów konfiguracji jądra.
Lokalizacje plików
Chociaż Android 7.x i starsze nie wymagają korzystania z modułów jądra (i obsługują insmod
i rmmod
), w Androidzie 8.x i nowszych zalecamy używanie modułów jądra w ekosystemie. Tabela poniżej przedstawia potencjalne wymagania dotyczące obsługi urządzeń peryferyjnych w ramach trzech trybów uruchamiania Androida.
Tryb uruchamiania | Miejsce na dane | Wyświetlacz | Klawiatura | Bateria | PMIC | Ekran dotykowy | NFC, Wi-Fi, Bluetooth |
Czujniki | Aparat |
---|---|---|---|---|---|---|---|---|---|
Odzyskiwanie | |||||||||
Ładowarka | |||||||||
Android |
Oprócz dostępności w trybach rozruchu Androida moduły jądra mogą być też kategoryzowane według tego, kto jest ich właścicielem (dostawca SoC lub ODM). Jeśli używane są moduły jądra, wymagania dotyczące ich umieszczenia w systemie plików są następujące:
- Wszystkie jądra powinny mieć wbudowane wsparcie dla uruchamiania i montowania partycji.
- Moduły jądra muszą być ładowane z partycji tylko do odczytu.
- W przypadku urządzeń wymagających uruchamiania w trybie zweryfikowanym moduły jądra powinny być ładowane z partycji zweryfikowanych.
- Moduły jądra nie powinny znajdować się w katalogu
/system
. - Moduły GKI wymagane na urządzeniu powinny być ładowane z
/system/lib/modules
, który jest linkiem symbolicznym do/system_dlkm/lib/modules
. - Moduły jądra od dostawcy układu SoC, które są wymagane do pełnego systemu Android lub trybów ładowarki, powinny znajdować się w
/vendor/lib/modules
. - Jeśli partycja ODM istnieje, moduły jądra z ODM, które są wymagane w trybie pełnego Androida lub trybie ładowania, powinny znajdować się w
/odm/lib/modules
. W przeciwnym razie te moduły powinny znajdować się w folderze/vendor/lib/modules
. - Moduły jądra od dostawcy SoC i ODM, które są wymagane w trybie odzyskiwania, powinny znajdować się w pliku
ramfs
w katalogu/lib/modules
. - Moduły jądra wymagane zarówno w trybie odzyskiwania, jak i w pełnym systemie Android lub w trybie ładowania powinny znajdować się zarówno w partycji
rootfs
, jak i w partycji/vendor
lub/odm
(jak opisano powyżej). - Moduły jądra używane w trybie odzyskiwania nie powinny zależeć od modułów znajdujących się tylko w
/vendor
lub/odm
, ponieważ te partycje nie są montowane w tym trybie. - Moduł jądra dostawcy SoC nie powinien zależeć od modułów jądra ODM.
W Androidzie 7.x i starszych partycje /vendor
i /odm
nie są montowane wcześniej. W Androidzie 8.x i nowszych, aby umożliwić wczytywanie modułów z tych partycji, wprowadzono postanowienia dotyczące wczesnego montowania partycji na urządzeniach z wsparciem i bez obsługi testów A/B. Dzięki temu partycje są montowane zarówno w trybie Androida, jak i w trybie ładowania.
Obsługa systemu kompilacji Androida
W BoardConfig.mk
kompilacja Androida definiuje zmienną BOARD_VENDOR_KERNEL_MODULES
, która zawiera pełną listę modułów jądra przeznaczonych dla obrazu dostawcy. Modułów wymienionych w tej zmiennej kopia jest tworzona w obrazie dostawcy w miejscu /lib/modules/
, a po zamontowaniu w Androidzie są one widoczne w miejscu /vendor/lib/modules
(zgodnie z wymaganiami wymienionymi powyżej).
Przykładowa konfiguracja modułów dostawcy jądra:
vendor_lkm_dir := device/$(vendor)/lkm-4.x BOARD_VENDOR_KERNEL_MODULES := \ $(vendor_lkm_dir)/vendor_module_a.ko \ $(vendor_lkm_dir)/vendor_module_b.ko \ $(vendor_lkm_dir)/vendor_module_c.ko
W tym przykładzie repozytorium z gotowym modułem jądra dostawcy jest mapowane na kompilację Androida w wymienionej powyżej lokalizacji.
Obraz odzyskiwania może zawierać podzbiór modułów dostawcy. Kompilacja Androida definiuje zmienną BOARD_RECOVERY_KERNEL_MODULES
dla tych modułów. Przykład:
vendor_lkm_dir := device/$(vendor)/lkm-4.x BOARD_RECOVERY_KERNEL_MODULES := \ $(vendor_lkm_dir)/vendor_module_a.ko \ $(vendor_lkm_dir)/vendor_module_b.ko
Kompilacja Androida uruchamia narzędzie depmod
, aby wygenerować wymagane pliki modules.dep
w katalogach /vendor/lib/modules
i /lib/modules
(recovery ramfs
).
Ładowanie modułów i uwzględnianie wersji
Załaduj wszystkie moduły jądra w jednym przejściu z poziomu init.rc*
, wywołując modprobe -a
. Dzięki temu unikniesz nadmiernego obciążania procesora podczas wielokrotnego inicjowania środowiska wykonawczego C dla binarnego pliku modprobe
. Zdarzenie early-init
można zmodyfikować, aby wywołać zdarzenie modprobe
:
on early-init exec u:r:vendor_modprobe:s0 -- /vendor/bin/modprobe -a -d \ /vendor/lib/modules module_a module_b module_c ...
Zazwyczaj moduł jądra musi być skompilowany z jądrem, z którym ma być używany (w przeciwnym razie jądro odmówi załadowania modułu).
CONFIG_MODVERSIONS
oferuje obejście problemu przez wykrywanie awarii w interfejsie binarnym aplikacji (ABI). Ta funkcja oblicza wartość CRC dla prototypu każdego wyeksportowanego symbolu w jądrze i przechowuje wartości jako część jądra. W przypadku symboli używanych przez moduł jądra wartości są również przechowywane w module jądra. Podczas ładowania modułu wartości symboli używanych przez moduł są porównywane z wartościami w jądrze. Jeśli wartości są zgodne, moduł jest wczytywany. W przeciwnym razie wczytywanie kończy się niepowodzeniem.
Aby umożliwić aktualizowanie obrazu jądra oddzielnie od obrazu dostawcy, włącz CONFIG_MODVERSIONS
. Dzięki temu można wprowadzać drobne aktualizacje jądra (np. poprawki błędów z wersji LTS) przy zachowaniu zgodności z dotychczasowymi modułami jądra w obrazie dostawcy. Jednak CONFIG_MODVERSIONS
nie naprawia samodzielnie naruszenia ABI. Jeśli prototyp wyeksportowanego symbolu w jądrze ulegnie zmianie (z powodu modyfikacji źródła lub zmiany konfiguracji jądra), spowoduje to utratę zgodności z modułami jądra, które używają tego symbolu. W takich przypadkach należy ponownie skompilować moduł jądra.
Na przykład struktura task_struct
w rdzeniu (zdefiniowana w include/linux/sched.h
) zawiera wiele pól uwzględnianych warunkowo w zależności od konfiguracji rdzenia. Pole sched_info
jest obecne tylko wtedy, gdy włączona jest zasada CONFIG_SCHED_INFO
(co ma miejsce, gdy włączone są zasady CONFIG_SCHEDSTATS
lub CONFIG_TASK_DELAY_ACCT
). Jeśli te opcje konfiguracji zmienią stan, układ struktury task_struct
ulegnie zmianie, a wszystkie wyeksportowane interfejsy jądra, które używają task_struct
, zostaną zmienione (na przykład set_cpus_allowed_ptr
w kernel/sched/core.c
). Spowoduje to utratę zgodności z wcześniej skompilowanymi modułami jądra, które używają tych interfejsów. Konieczne będzie ich ponowne skompilowanie z nową konfiguracją jądra.
Więcej informacji o CONFIG_MODVERSIONS
znajdziesz w dokumentacji w drzewie jądra na stronie Documentation/kbuild/modules.rst
.