Immagine kernel generica

L' Android Common Kernel (ACK) sono la base per tutti i kernel di prodotti Android. I kernel del fornitore e del dispositivo sono a valle degli ACK. I fornitori aggiungono il supporto per SoC e dispositivi periferici modificando il codice sorgente del kernel e aggiungendo driver di dispositivo. Queste modifiche possono essere estesa, al punto che fino al 50% del codice in esecuzione su un dispositivo è fuori-di-struttura di codice (non da monte Linux o da AOSP kernel comuni).

Pertanto, un kernel del dispositivo è composto da:

  • A monte: il kernel Linux da kernel.org
  • AOSP: patch aggiuntive specifiche per Android dai kernel comuni AOSP
  • Fornitore: patch per l'ottimizzazione e l'abilitazione di SoC e periferiche dai fornitori
  • OEM/dispositivo: driver di dispositivo aggiuntivi e personalizzazioni

Quasi ogni dispositivo ha un kernel personalizzato. Questa è la frammentazione del kernel.

La gerarchia del kernel Android porta alla frammentazione

Figura 1. Android gerarchia kernel porta alla frammentazione

I costi della frammentazione

La frammentazione del kernel ha diversi effetti negativi sulla comunità Android.

Gli aggiornamenti di sicurezza richiedono molta manodopera

Le patch di protezione citati nel Security Bulletin Android (ASB) deve essere backport in ciascuno dei kernel del dispositivo. Tuttavia, a causa della frammentazione del kernel, è proibitivo propagare le correzioni di sicurezza ai dispositivi Android sul campo.

Difficile unire gli aggiornamenti supportati a lungo termine

Il lungo termine (LTS) supportati release includono fix di sicurezza ed altre correzioni di bug critici. Rimanere aggiornati con le versioni LTS si è dimostrato il modo più efficace per fornire correzioni di sicurezza. Sui dispositivi Pixel, è stato scoperto che il 90% dei problemi di sicurezza del kernel segnalati nell'ASB era già stato risolto per i dispositivi che rimangono aggiornati.

Tuttavia, con tutte le modifiche personalizzate nei kernel dei dispositivi, è difficile unire semplicemente le correzioni LTS nei kernel dei dispositivi.

Inibisce gli aggiornamenti del rilascio della piattaforma Android

La frammentazione rende difficile l'aggiunta di nuove funzionalità Android ai dispositivi sul campo, che richiedono modifiche al kernel. Il codice di Android Framework deve presumere che siano supportate fino a cinque versioni del kernel e che non siano state apportate modifiche al kernel per la nuova versione della piattaforma (Android 10 supporta i kernel 3.18, 4.4, 4.9, 4.14 e 4.19, che in alcuni casi non sono stati migliorato con nuove funzionalità da Android 8 nel 2017).

Difficile contribuire con le modifiche del kernel a Linux a monte

Con tutte le modifiche apportate al kernel, la maggior parte dei dispositivi di punta viene fornita con una versione del kernel che ha già almeno 18 mesi. Ad esempio, il kernel 4.14 è stato rilasciato da kernel.org nel novembre del 2017 e dei primi telefoni Android che utilizzano 4.14 kernel spediti nella primavera del 2019.

Questo lungo ritardo tra il rilascio del kernel upstream e i prodotti rende difficile per la comunità Android inserire le funzionalità e i driver necessari nei kernel upstream, quindi è difficile risolvere il problema della frammentazione.

Risolvere la frammentazione: immagine generica del kernel

Gli indirizzi progetto Kernel Generico Image (Gki) kernel frammentazione unificando kernel nucleo e spostando SoC e supporto bordo fuori dal kernel nucleo in moduli caricabili. GKI kernel presenta una stalla Kernel modulo di interfaccia (KMI) per moduli, dunque moduli e kernel possono essere aggiornati in modo indipendente.

Il GKI:

  • È costruito da fonti ACK.
  • È un singolo kernel binario più associati moduli caricabili per architettura, per LTS (attualmente solo arm64 per android11-5.4 e android12-5.4 ).
  • Viene testato con tutte le versioni della piattaforma Android che sono supportati per l'ACK associato. Non è prevista la deprecazione delle funzionalità per tutta la durata di una versione del kernel GKI
  • Espone una stalla KMI ai conducenti all'interno di una determinata LTS.
  • Non contiene SoC- o codice di discussione specifico.

Ecco come appare un dispositivo Android con GKI implementato.

Architettura GKI

Architettura Figura 2. GKI

GKI è una modifica complessa che verrà implementata in più fasi a partire dai kernel v5.4 nella versione della piattaforma Android 11.

GKI 1.0 — Requisiti di compatibilità GKI

Per alcuni dispositivi avviati con la versione della piattaforma Android 11, la compatibilità Treble richiede il test GKI per i dispositivi che eseguono kernel v5.4.

Partizioni per test di compatibilità GKI

Figura 3. Le partizioni per test di compatibilità GKI

GKI mezzi di compatibilità che il dispositivo passa VTS e CTS-on-GSI test + GKI con il sistema generico Image (GSI) e il kernel GKI installato facendo lampeggiare l'immagine di avvio GKI nel boot immagine del sistema GSI divisorio e nel system partizione. I dispositivi possono spedire con un kernel prodotto diverso e possono utilizzare moduli caricabili che GKI non fornisce. Tuttavia, sia il prodotto e noccioli GKI devono caricare moduli dalle stesse vendor_boot e vendor partizioni. Pertanto, tutti i kernel del prodotto devono avere la stessa interfaccia del modulo del kernel binario (KMI). I fornitori possono estendere il KMI per i kernel del prodotto purché rimanga compatibile con il KMI GKI. Non è necessario che i moduli del fornitore siano scaricabili in GKI 1.0.

GKI 1.0 obiettivi

  • Non introdurre regressioni in VTS o CTS quando il kernel del prodotto viene sostituito dal kernel GKI.
  • Riduci il carico di manutenzione del kernel per OEM e fornitori per rimanere aggiornati con i kernel comuni AOSP.
  • Includere le modifiche principali di Android nei kernel indipendentemente dal fatto che un dispositivo venga aggiornato a una nuova versione della piattaforma Android o lanciato di recente.
  • Non rompere mai lo spazio utente di Android.
  • Separare i componenti specifici dell'hardware dal kernel principale come moduli caricabili.

GKI 2.0 - Prodotti GKI

Alcuni dispositivi che le versioni del kernel lancio con Android 12 (2021) rilascio piattaforma utilizzando v5.10 o superiore sono tenuti a nave con il kernel GKI. Le immagini di avvio firmate saranno rese disponibili e aggiornate regolarmente con LTS e correzioni di bug critici. Poiché la stabilità binaria verrà mantenuta per il KMI, queste immagini di avvio possono essere installate senza modifiche alle immagini del fornitore.

Obiettivi GKI 2.0

  • Non introdurre prestazioni significative o regressioni di potenza con GKI.
  • Consenti agli OEM di fornire correzioni di sicurezza del kernel e correzioni di bug (LTS) senza il coinvolgimento del fornitore.
  • Riduci i costi di aggiornamento della versione principale del kernel per i dispositivi (ad esempio, dalla v5.10 al kernel LTS 2021).
  • Mantieni un solo file binario del kernel GKI per architettura aggiornando le versioni del kernel con un processo chiaro per l'aggiornamento.

GKI design

Rami del kernel KMI

Noccioli GKI sono costruiti dai rami del kernel ACK KMI, che sono descritte in dettaglio in Android Comune Kernel . La KMI è univocamente identificato dalla versione del kernel e il rilascio piattaforma Android, in modo che i rami sono denominati <androidRelease>-<kernel version> . Ad esempio, il KMI ramo 5.4 del kernel per Android 11 è chiamato android11-5.4. E 'previsto che per Android 12 (non impegnati e mostrato solo per dimostrare come il nuovo modello ramificazione progredirà in futuro) ci saranno due ulteriori KMI kernel, android12-5.4 e un secondo kernel basato sul kernel LTS 5.10, rilasciato nel mese di dicembre 2020.

Gerarchia del kernel KMI

La Figura 4 mostra la gerarchia dei rami per i kernel KMI 5.10. android12-5.10 è il kernel KMI corrispondente ad Android 12 e android13-5.10 corrisponde ad Android T (AOSP sperimentale) (non impegnati e mostrato solo per dimostrare come il nuovo modello ramificazione progredirà in futuro).

Gerarchia del kernel KMI per 5.10

Figura 4. KMI kernel gerarchia per 5.10

Come mostrato in figura 4, il KMI rami ciclo attraverso tre fasi: sviluppo (dev), stabilizzazione (stab), e congelati. Queste fasi sono descritte in dettaglio in Android Comune Kernel .

Dopo che il kernel KMI è stato bloccato, non vengono accettate modifiche che compromettano la KMI, a meno che non venga identificato un serio problema di sicurezza che non può essere mitigato senza influire sulla KMI stabile. Il ramo rimane congelato per tutta la sua vita.

Le correzioni di bug e le funzionalità dei partner possono essere accettate in un ramo bloccato purché il KMI esistente non sia danneggiato. Il KMI può essere esteso con nuovi simboli esportati purché le interfacce che compongono il KMI corrente non siano interessate. Quando vengono aggiunte nuove interfacce al KMI, diventano immediatamente stabili e non possono essere interrotte da modifiche future.

Ad esempio, una modifica che aggiunge un campo a una struttura utilizzata da un'interfaccia KMI non è consentita perché modifica la definizione dell'interfaccia:

struct foo {
  int original_field1;
  int original_field2;
  int new_field; // Not allowed
};

int do_foo(struct foo &myarg)
{
  do_something(myarg);
}
EXPORT_SYMBOL_GPL(do_foo);

Tuttavia, l'aggiunta di una nuova funzione va bene:

struct foo_ext {
  struct foo orig_foo;
  int new_field;
};

int do_foo2(struct foo_ext &myarg)
{
  do_something_else(myarg);
}
EXPORT_SYMBOL_GPL(do_foo2);

Stabilità del KMI

Per realizzare gli obiettivi GKI, è fondamentale mantenere un KMI stabile per i conducenti. Il kernel GKI è compilato e distribuito in forma binaria, ma i moduli caricabili dal fornitore sono compilati in un albero separato. Il kernel ei moduli GKI risultanti devono funzionare come se fossero stati creati insieme. Per i test di compatibilità GKI, l'immagine di avvio contenente il kernel viene sostituita con un'immagine di avvio contenente il kernel GKI e i moduli caricabili nell'immagine del fornitore devono funzionare correttamente con entrambi i kernel.

Il KMI non include tutti i simboli nel kernel e nemmeno tutti gli oltre 30.000 simboli esportati. Invece, i simboli che possono essere usati dai moduli sono elencati esplicitamente in un insieme di file di elenchi di simboli mantenuti pubblicamente nella radice dell'albero del kernel. L'unione di tutti i simboli in tutti i file dell'elenco dei simboli definisce l'insieme dei simboli KMI mantenuti come stabili. Un file di elenco dei simboli esempio è abi_gki_aarch64_db845c , che dichiara i simboli necessari per la 845C DragonBoard .

Solo i simboli elencati in un elenco di simboli e le relative strutture e definizioni sono considerati parte del KMI. Puoi pubblicare modifiche ai tuoi elenchi di simboli se i simboli di cui hai bisogno non sono presenti. Dopo che le nuove interfacce si trovano in un elenco di simboli, e quindi fanno parte della descrizione KMI, vengono mantenute come stabili e non devono essere rimosse dall'elenco di simboli o modificate dopo che il ramo è stato bloccato.

Ogni albero del kernel KMI ha il proprio set di elenchi di simboli. Non viene fatto alcun tentativo per fornire stabilità ABI tra diversi rami del kernel KMI. Ad esempio, il KMI per android11-5.4 è completamente indipendente dal KMI per android12-5.4 .

In generale, la comunità Linux ha visto di buon occhio sulla nozione di in-kernel ABI stabilità per il kernel mainline. Di fronte a diverse toolchain, configurazioni e un kernel Linux mainline in continua evoluzione, non è possibile mantenere un KMI stabile nella mainline. Tuttavia, è possibile nell'ambiente GKI altamente vincolato. I vincoli sono:

  • L'KMI è stabile solo all'interno dello stesso kernel LTS (per esempio, android11-5.4 ).
    • Stabilità No KMI viene mantenuta per android-mainline .
  • Solo la specifica Clang toolchain fornito in AOSP e definita per il ramo corrispondente è utilizzato per costruire kernel ei moduli.
  • Solo i simboli noti per essere utilizzati dai moduli come specificato in un elenco di simboli vengono monitorati per la stabilità e considerati simboli KMI.
    • Il corollario è che i moduli devono utilizzare solo simboli KMI. Ciò viene imposto dal mancato caricamento del modulo se sono richiesti simboli non KMI.
  • Dopo che il ramo KMI è stato bloccato, nessuna modifica può interrompere il KMI, tra cui:
    • Modifiche alla configurazione
    • Modifiche al codice del kernel
    • Modifiche alla toolchain (inclusi gli aggiornamenti)

Monitoraggio KMI

Strumenti di monitoraggio ABI monitorare la stabilità KMI durante i test presubmit. Le modifiche che interrompono il KMI falliscono il test pre-invio e devono essere rielaborate per essere compatibili. Questi strumenti sono a disposizione dei partner e del pubblico per l'integrazione nei loro processi di costruzione. Il team del kernel Android utilizza questi strumenti per trovare interruzioni KMI durante lo sviluppo e durante l'unione delle versioni LTS. Se viene rilevata un'interruzione del KMI durante un'unione LTS, il KMI viene preservato rimuovendo le patch dannose o eseguendo il refactoring delle patch per renderle compatibili.

Il KMI è considerato rotto se un simbolo KMI esistente viene modificato in modo incompatibile. Esempi:

  • Nuovo argomento aggiunto a una funzione KMI
  • Nuovo campo aggiunto a una struttura utilizzata da una funzione KMI
  • Nuovo valore enum aggiunto che modifica i valori degli enum utilizzati da una funzione KMI
  • Modifica della configurazione che modifica la presenza di membri dati che interessano il KMI

L'aggiunta di nuovi simboli non interrompe necessariamente il KMI, ma i nuovi simboli utilizzati devono essere aggiunti a un elenco di simboli e alla rappresentazione ABI. In caso contrario, le future modifiche a questa parte del KMI non verranno riconosciute.

Ci si aspetta che i partner utilizzino gli strumenti di monitoraggio ABI per confrontare il KMI tra il kernel del loro prodotto e il kernel GKI e assicurarsi che siano compatibili.

L'ultima android11-5.4 binario GKI kernel possono essere scaricati da ci.android.com ( kernel_aarch64 costruisce).

Compilatore singolo

Una modifica del compilatore può alterare il layout della struttura dei dati del kernel interno che influisce sull'ABI. Poiché è fondamentale mantenere stabile la KMI, la toolchain utilizzata per creare il kernel GKI deve essere completamente compatibile con la toolchain utilizzata per creare i moduli del fornitore. Il kernel GKI è costruito con la toolchain LLVM inclusa in AOSP.

A partire da Android 10, tutti i kernel Android devono essere compilati con una toolchain LLVM. Con GKI, la toolchain LLVM utilizzata per creare i kernel del prodotto e i moduli del fornitore deve generare lo stesso ABI della toolchain LLVM da AOSP e i partner devono assicurarsi che il KMI sia compatibile con il kernel GKI.

La documentazione di compilazione del kernel Android descrive come il kernel GKI di riferimento è costruito. In particolare, la procedura documentata garantisce che vengano utilizzate la toolchain e la configurazione corrette per compilare il kernel. I partner a valle sono incoraggiati a utilizzare gli stessi strumenti per produrre il kernel finale per evitare incompatibilità causate dalla toolchain o da altre dipendenze in fase di compilazione.

Configurazioni del kernel

Il kernel GKI è costruito utilizzando arch/arm64/configs/gki_defconfig . Poiché queste configurazioni influiscono sul KMI, le configurazioni di GKI sono gestite con molta attenzione. Per i kernel KMI congelati, le configurazioni possono essere aggiunte o rimosse solo se non c'è impatto sul KMI.

Il kernel GKI deve essere configurato per essere eseguito su un insieme diversificato di dispositivi. Pertanto deve avere il supporto integrato per tutti i sottosistemi e le opzioni richieste per tutti questi dispositivi, esclusi i moduli caricabili utilizzati per abilitare l'hardware. I partner dovrebbero richiedere i cambiamenti di configurazione necessari per i kernel dev.

Modifiche all'avvio

Per facilitare una netta separazione tra i componenti GKI e fornitore, l' boot partizione contiene solo componenti generici, compreso il kernel e ramdisk con moduli GKI. Viene definita una nuova versione dell'intestazione di avvio (v3) per indicare la conformità con l'architettura GKI. La versione GKI delle immagini di avvio viene fornita da Google e sostituisce la versione dell'immagine di avvio del fornitore durante il test per la compatibilità con GKI.

Il ramdisk per primo stadio init , recovery e fastbootd è initramfs un'immagine costituita da due cpio che sono concatenati dal bootloader. Il primo archivio CPIO viene dalla nuova vendor_boot partizione. Il secondo deriva dalla boot partizione.

Una sintesi delle modifiche è fornita qui. Vedere fornitore partizione di avvio per i dettagli.

Partizione di avvio

L' boot della partizione include un'intestazione, kernel, e archiviare un CPIO della parte generica del ramdisk di avvio.

Con un'intestazione avvio v3 boot partizione, queste sezioni del precedente boot partizione non sono più presenti:

  • Bootloader di seconda fase: se un dispositivo ha un bootloader di seconda fase, deve essere memorizzato nella propria partizione.
  • DTB: Il DTB viene memorizzato nella partizione di avvio del fornitore .

L' boot partizione contiene un archivio CPIO con i componenti GKI:

  • Moduli del kernel GKI situati in /lib/modules/
  • first_stage_init e le librerie da cui dipende
  • fastbootd e recovery (usato in A / B e A virtuale / dispositivi B)

L'immagine di avvio GKI è fornito da Google e deve essere utilizzato per i test di compatibilità GKI .

L'ultima arm64 android11-5.4 boot.img è scaricabile dal ci.android.com nei aosp_arm64 manufatti di generazione del aosp-master ramo.

L'ultima arm64 android11-5.4 immagine del kernel ( Image.gz ) può essere scaricato dal ci.android.com nei kernel_aarch64 manufatti di generazione del aosp_kernel-common-android11-5.4 ramo.

Partizione di avvio del fornitore

Il vendor_boot partizione viene introdotta con GKI. È A/B con Virtual A/B e consiste in un'intestazione, il ramdisk del fornitore e il blob dell'albero del dispositivo. Il ramdisk del fornitore è un archivio CPIO che contiene i moduli del fornitore necessari per l'avvio del dispositivo. Ciò include moduli per abilitare funzionalità SoC critiche, nonché driver di archiviazione e visualizzazione necessari per avviare il dispositivo e visualizzare schermate iniziali.

L'archivio CPIO contiene:

  • Prima fase di init vendor kernel moduli situato in /lib/modules/
  • modprobe config file che si trovano in /lib/modules
  • modules.load fascicolo indichi i moduli di carico durante la prima fase init

Requisiti del bootloader

Il bootloader deve caricare l'immagine ramdisk CPIO generico (dal boot partizione ) nella memoria immagine CPIO fornitore ramdisk (dal immediatamente seguente vendor_boot partizione ). Dopo la decompressione, il risultato è il ramdisk generico sovrapposto alla struttura del file del ramdisk del fornitore.

Test di compatibilità GKI

Per la versione della piattaforma Android 11, i dispositivi avviati con un kernel v5.4 devono eseguire i test VTS e CTS-on-GSI utilizzando l'immagine di avvio GKI fornita da Google.

Contribuire al kernel GKI

Il kernel GKI è costruito dal Comune AOSP Kernel iniziando con android11-5.4 . Tutte le patch inviate devono essere conformi alle queste linee guida di contribuzione , che documentano due strategie:

  1. Miglior: Fai tutte le modifiche a monte Linux. Se appropriato, eseguire il backport nelle versioni stabili. Queste patch vengono unite automaticamente nei kernel comuni AOSP corrispondenti. Se la patch è già in upstream Linux, pubblica un backport della patch conforme ai requisiti della patch di seguito.
  2. Meno buona: Sviluppate le vostre patch di albero (da un punto di vista monte Linux). A meno che le patch sono la fissazione di un bug di Android specifico, sono molto improbabile che venga accettato a meno che sono stati coordinati con kernel-team@android.com .

Per i partner che implementano GKI, potrebbero esserci validi motivi per cui sono necessarie patch out-of-tree (specialmente se ci sono pianificazioni del silicio che devono essere soddisfatte). Per questi casi, presentare una Buganizer problema.

Inviare le modifiche GKI attraverso Gerrit al android-mainline ramo e poi backport ad altri rami di rilascio in base alle esigenze.

Il codice sorgente associato ai moduli caricabili non ha bisogno di essere fornito all'albero dei sorgenti del kernel GKI, tuttavia si consiglia vivamente di inviare tutti i driver a Linux a monte.

Richiedere backport dalla linea principale

In genere, è possibile eseguire il backport delle patch principali necessarie ai partner GKI nei kernel GKI. Carica la patch per Gerrit seguendo le linee guida di contribuzione .

Se la patch è stata pubblicata a monte, ma non è stata ancora accettata, si consiglia di attendere fino a quando non viene accettata. Se la pianificazione non consente l'attesa dell'accettazione della patch a monte, presenta un problema con Buganizer.

Aggiunta e rimozione di configurazioni GKI

Per richiedere l'aggiunta o la rimozione di file di configurazione in arch/arm64/configs/gki_defconfig , presentare una questione Buganizer indicando chiaramente la richiesta e la giustificazione. Se non c'è un progetto Buganizer accessibile, pubblica la patch con la modifica della configurazione su Gerrit e assicurati che il messaggio di commit indichi chiaramente il motivo per cui è necessaria la configurazione.

Le modifiche alla configurazione nei kernel KMI bloccati non devono influire su KMI.

Modifica del codice del kernel principale

Le modifiche al codice del kernel principale nei kernel comuni AOSP sono scoraggiate. Prima invia le patch a Linux a monte e poi esegui il backport. Se c'è una buona ragione per una modifica del kernel di base, presenta un problema di Buganizer indicando chiaramente la richiesta e la giustificazione. Nessuna funzionalità specifica per Android è accettata senza un problema di Buganizer. Invia una mail a kernel-team@android.com se non è possibile creare un problema di Buganizer.

Esportazione di simboli tramite EXPORT_SYMBOL_GPL()

Non inviare patch a monte che contengono solo esportazioni di simboli. Per essere considerati per monte Linux, aggiunte di EXPORT_SYMBOL_GPL() richiedono un driver modulare in-albero che utilizza il simbolo, in modo da includere il nuovo driver o di modifiche a un driver esistente nella stessa patchset come l'esportazione.

Quando si inviano patch a monte, il messaggio di commit deve contenere una chiara giustificazione del motivo per cui la patch è necessaria e vantaggiosa per la comunità. Consentire alle esportazioni di beneficiare di driver o funzionalità fuori dall'albero non è un argomento convincente per i manutentori a monte.

Se per qualche motivo, la patch non può essere inviata a monte, segnala un problema con Buganizer e spiega perché la patch non può essere inviata a monte. In genere, è probabile che i simboli che rappresentano l'interfaccia supportata per un sottosistema del kernel vengano accettati come esportazioni. Tuttavia, è improbabile che le funzioni di supporto interno casuali vengano accettate e ti verrà chiesto di eseguire il refactoring del codice per evitare di utilizzarle.

Aggiunta di un simbolo a un elenco di simboli KMI

Gli elenchi dei simboli KMI devono essere aggiornati con i simboli del kernel GKI utilizzati dai moduli del fornitore. Poiché solo i simboli KMI vengono mantenuti stabili, GKI non consente il caricamento dei moduli se si basano su un simbolo non KMI.

extract_symbols sceneggiatura estrae i relativi simboli da un albero di compilazione del kernel e può essere utilizzato per aggiornare un elenco dei simboli KMI. Fare riferimento alla documentazione di simbolo lista per ulteriori dettagli.