Guida Programmare nel kernel Linux [parte 1]

kernelspace

Utente Silver
17 Giugno 2021
222
13
107
95
Ultima modifica:
[editato 01/8/2021, rev 2: alcuni fix di sintassi, aggiunto trace di debug]
[editato 02/8/2021, rev 3: rimosso link matrix, pare violi regolamento, lo terro' nella firma]

Saluti a tutti,

ho deciso di scrivere una piccola guida introduttiva forse utile, per questo simpatico forum.

La guida riguarda il kernel "mainline" noto anche come vanilla, si trova in www.kernel.org, ed e' quello ufficiale (fonte ufficiale chiamata anche "upsteram") gestito da Linus Torvalds, da cui tutti gli altri kernel derivano.

Come saprete, il kernel Linux e' 99% C, quindi stiamo parlando di programmazione C e la guida dovrebbe essere on-topic.

Alcune note preliminari:

Perchè dovrebbe interesssarmi la programmazione nel kernel Linux ?

Intanto perche' anche solo studiarlo un po' e' un ottima scelta a fini didattici, montagne di codice C in genere scritto bene dove e' stato implementato di tutto e di piu' E' codice opensource quindi riutilizzabile, che sia una linked list, un driver o l'intero kernel. Saper metterci mano consente anche di capire la ragione di misteriosi errori/warnings che si incontrano al boot, che spesso nei forum nessuno sa spiegare, o effettuare alcune personalizzazioni. Contribuire al kernel poi, che e' la cosa piu' difficile, consente di acquisire un certo "punteggio" per il proprio cv.

Requisiti: buona conoscenza C e git.

PARTE 1: introduzione e metodologia di lavoro.

1) Puoi prendere visione dei sorgenti del kernel, senza scaricarli, cliccando sorgenti .

2) Dal sito ufficiale kernel.org troverete vari repository, quelli fondamentali sono:

linux (Kernel "mainline", testa dello sviluppo, codice testato)
linux-next (features aggiunte dai vari maintainers, in fase di test)
linux-stable (versioni stable e LTS, con backport di fix importanti)

Scaricando il codice dal repository di interesse, e' possibile accedere alla precisa versione su cui si vuole operare (git branch / tag).

3) Per iniziare a mettere mano al codice, pouoi scaricare il codice su pc. Ci sono due vie,
  • rapida, scaricare solo il pacchetto compresso della specifica versione, tipo linux-5.3-rc8.tar.gz
  • completa: clonare l'intero repository con tutta la storia, download lungo, ma la storia e' fondamentale per lo sviluppo.
Bash:
# clone completo del kernel mainline
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

4) Se si intende aggiungere delle funzionalita' o fixare qualche bug, con la finalita' di contribuire inviando la patch upstream, si deve scaricare e lavorare sul kernel mainline, branch "master".

5) Se intendi modificare, compilare e utilizzare il kernel per la tua stessa macchina, la posizione indicata dove scaricarlo sara':

[email protected] /usr/src $ lstotal 28
drwxr-xr-x 7 angelo angelo 4096 May 15 11:52 .
drwxr-xr-x 12 root root 4096 Jul 28 12:34 ..
drwxr-xr-x 5 angelo angelo 4096 Mar 22 15:18 kernel-mft-4.16.1
lrwxrwxrwx 1 root root 9 Jul 3 2020 linux -> linux.git
drwxr-xr-x 24 angelo angelo 4096 Dec 12 2019 linux-5.3-rc8
drwxr-xr-x 25 angelo angelo 4096 Mar 31 2020 linux-5.6-rc3
drwxr-xr-x 29 angelo angelo 4096 Apr 28 11:01 linux.git


Se invece intendi cross-compilarlo, ad esempio per una schedina arm, puoi scaricarlo dove piu' ti piace.

6) Una volta scaricato, l'albero dei sorgenti si presenta cosi:

1627721327767.png


Non entro in questa prima parte nel dettaglio del contenuto di ogni directory. Fondamentali sono comunque
  • arch, architetture supportate, codice basso livello specifico di ogni architettura, con spesso codici assembly per le prime inizializzazioni (head.S),
  • drivers, quasi tutti i driver, eccetto quelli audio in sound,
  • include, gli header files,
  • mm, memory management,
  • fs, file systems,
  • net, protocolli di rete.
7) Configurazione

Per configurare il kernel si parte da una configurazione esistente, altrimenti sarebbe un operazione infinita.
Per pre-configurare con una configurazione esistente si utilizzano alcune facilitazioni:

make oldconfigcerca un file .config in uso e parte da li, chiedendo le informazioni mancanti rispetto alla versione che si sta compilando
make olddefconfigcerca un file .config in uso e parte da li, non domanda nulla, applica i default dove mancano informazioni
make xxxxx_defconfigOgni architettura ha delle configurazioni predefinite, si trovano in arch/xxx/configs

Quella scelta tra le operazioni sopra genera il file .config.
Il centro del processo di configurazione e' il file .config, la compilazione (make) utilizza sempre questo file.

Dopo aver preconfigurato il kernel come sopra, e generato .config, si puo modificarlo con un semplice

make menuconfig

.config sara' cosi aggiornato. Dall'attuale .config si puo creare un file "defconfig" ovvero una configurazione predefinita con

Codice:
make savedefconfig
cp defconfig arch/arm/configs/mio_defconfig

E riutilizzarla con
Codice:
make mio_defconfig
make

Nota: ogni modulo/driver puo essere compilato built-in [*] o loadable [M]. In un PC in genere si tengono abilitati tantissimi driver come [M] perche vengano riconosciuti in maniera dinamica dispositivi pluggati in bus pci / usb etc. Nelle schedine embedded in genere si abilitano solo i driver per i dispositivi presenti sulla schedina (platform driver/device), quindi pochissimi, la scelta se includerli built-in o caricabili e' dettata dalla strategia scelta (fast boot etc).

8) Modifiche

Aggiungiamo per test un semplice trace di debug,

nano arch/x86/kernel/setup.c

C:
786         * quirk is invoked before subsequent calls to __flush_tlb_all()
787         * so proper operation is guaranteed.
788         */
789        __flush_tlb_all();
790#else
791        printk(KERN_INFO "Command line: %s\n", boot_command_line);
792        boot_cpu_data.x86_phys_bits = MAX_PHYSMEM_BITS;
793
794        printk(KERN_INFO "%s() : boot_cpu_data.x86_phys_bits %d\n",
795                       __func__, boot_cpu_data.x86_phys_bits);
796
797#endif
798
799        /*
800         * If we have OLPC OFW, we might end up relocating the fixmap due to

Dopo il boot, dal "dmesg", all'inizio subito dopo la command line dovrebbe apparire il vs. messaggio.

9) Compilazione

Che ci si trovi a compilare "native", cioe' con gcc installato sul tuo stesso pc dove eseguirai il kernel, o cross-compilare con toolchain specifica, i passi importanti per la compilazione sono:

Bash:
# cross compilazione, queste 4 linee non servono se si compila da e per lo stesso pc
export CROSS_COMPILE=/opt/toolchains/m68k/gcc-10.1.0-nolibc/m68k-linux/bin/m68k-linux-
export ARCH=m68k
entry_point=40001000
load_addr=0x${entry_point}

# compilazione
make specifico_defconfig
make -j24 KALLSYMS_EXTRA_PASS=1 LOADADDR=${load_addr} zImage

# moduli, facoltativo
make modules
make modules_install INSTALL_MOD_PATH=/....

Questo sopra e' solo il nocciolo della compilazione, lascio a voi arricchire lo script a piacimento.
Dalla zImage generata ogni distribuzione ha i propri comandi per generare l'apposito initramfs.

Questa prima guida si ferma qui, e si propone di avervi portato a generare la Vs. zImage. Spero ci siate riusciti. Per non mettere troppa carne al fuoco, aspetto Vs. commenti per eventualmente creare la parte 2.