Lors de la mise au point du noyau Linux sur une cible, il est possible que les messages sur la console (issus de l'utilisation de printk()) n'apparaissent pas car le pilote associé nécessite un meilleur niveau de fonctionnement du noyau. Bien entendu, la sonde JTAG est la meilleure solution mais elle n'est pas toujours disponible.
L'utilisation de early printk permet d'améliorer les choses en permettant l'enregistrement de la console en amont, mais la aussi les résultats dépendent du niveau fonctionnel du noyau en cours de mise au point.
La version ARM du noyau Linux dispose de fonctions très bas niveau permettant de piloter directement le contrôleur série (UART) par des routines en assembleur de quelques lignes, ce qui suffit largement pour la mise au point. La fonction printascii() est définie dans le fichier arch/arm/kernel/debug.S. La compilation de ces fonctions nécessite d'activer l'option Kernel hacking>Kernel low-level debugging functions lors de la configuration du noyau par make menuconfig.
Cette option correspond à CONFIG_DEBUG_LL dans le fichier de configuration .config.
Une fois l'option activée, il est nécessaire de modifier légèrement le code de la fonction printk() afin d'ajouter l'appel à printascii(). La fonction est définie dans kernel/printk.c et il faut ajouter l'appel à printascii() au code de la fonction vprintk() elle-même appelée par printk() .
extern void printascii(const char *); asmlinkage int vprintk(const char *fmt, va_list args) { ... /* Emit the output into the temporary buffer */ printed_len += vscnprintf(printk_buf + printed_len, sizeof(printk_buf) - printed_len, fmt, args); //Low level debug printascii(printk_buf); p = printk_buf; /* Do we have a loglevel in the string? */ if (p[0] == '<') { ... }
Dans l'exemple suivant, un noyau compilé pour la carte ARM Integrator/CP ne fonctionne pas au démarrage et n'affiche aucun message d'erreur malgré l'activation de l'option CONFIG_EARLY_PRINTK dans le menu Kernel hacking>Kernel low-level debugging functions .
$ qemu-system-arm -M integratorcp -m 64 -kernel linux-2.6.33/arch/arm/boot/zImage -initrd rootfs_qemu.gz -append "console=ttyAMA0" -nographic Uncompressing Linux... done, booting the kernel. QEMU: Terminated
Si l'on effectue les modifications décrites précédemment, soit la modification de la fonction printk(), on obtient des traces beaucoup plus intéressantes. Nous remarquons que les messages sont préfixés par le niveau d'affichage (exemple: <5>) puisque nous affichons directement le message en l'envoyant à l'UART. A la fin des messages, nous constatons que l'erreur est liée à l'initialisation de la console VGA.
Uncompressing Linux... done, booting the kernel. <5>Linux version 2.6.33 (pierre@dellpf) (gcc version 4.2.2) #4 Tue Feb 15 15:48:37 CET 2011 CPU: ARM926EJ-S [41069265] revision 5 (ARMv5TEJ), cr=00093177 CPU: VIVT data cache, VIVT instruction cache Machine: ARM-IntegratorCP Memory policy: ECC disabled, Data cache writeback <7>On node 0 totalpages: 16384 <7>free_area_init_node: node 0, pgdat c0317c1c, node_mem_map c0349000 <7> Normal zone: 128 pages used for memmap <7> Normal zone: 0 pages reserved <7> Normal zone: 16256 pages, LIFO batch:3 Built 1 zonelists in Zone order, mobility grouping on. Total pages: 16256 <5>Kernel command line: console=ttyAMA0 <6>PID hash table entries: 256 (order: -2, 1024 bytes) <6>Dentry cache hash table entries: 8192 (order: 3, 32768 bytes) <6>Inode-cache hash table entries: 4096 (order: 2, 16384 bytes) <6>Memory: 64MB = 64MB total <5>Memory: 59888KB available (2832K code, 286K data, 108K init, 0K highmem) <6>SLUB: Genslabs=11, HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1 <6>Hierarchical RCU implementation. <6>NR_IRQS:47 <6>I-pipe 1.18-00: pipeline enabled. <1>Unable to handle kernel paging request at virtual address ee0003da <1>pgd = c0004000 <1>[ee0003da] *pgd=00000000 <0>Internal error: Oops: 35 [#1] <0>last sysfs file: Modules linked in: CPU: 0 Not tainted (2.6.33 #4) PC is at vgacon_startup+0x1c4/0x440 LR is at 0x3df pc : [] lr : [<000003df>] psr: 80000153 sp : c0301f88 ip : c0305e64 fp : 00000000 r10: 0001f578 r9 : 41069265 r8 : 0001f5ac r7 : c0304430 r6 : c0021010 r5 : 00000001 r4 : 00000000 r3 : 00000000 r2 : 00000000 r1 : 00000001 r0 : ee000300 Flags: Nzcv IRQs on FIQs off Mode SVC_32 ISA ARM Segment kernel Control: 00093177 Table: 00004000 DAC: 00000017 <0>Process swapper (pid: 0, stack limit = 0xc0300270) <0>Stack: (0xc0301f88 to 0xc0302000) <0>1f80: 00000028 c001fc68 c001fc6c c0021010 c0304430 c0018838 <0>1fa0: c0021010 c0304430 0001f5ac 41069265 0001f578 c001fc68 c001fc6c c0021010 <0>1fc0: c0304430 c0017edc 60000153 c0308d18 c0318270 c0008abc c00084ec 00000000 <0>1fe0: 00000000 c0021010 00093175 c0318518 c0021414 00008034 00000000 00000000 [] (vgacon_startup+0x1c4/0x440) from [] (con_init+0x24/0x288) ...
Si l'on désactive la console VGA (CONFIG_VGA_CONSOLE), le noyau démarre alors correctement. Bien entendu, il est nécessaire de désactiver cette modification lorsque le problème est corrigé car cela génère un grand nombre de traces devenues inutiles.
Tu peux voir l'utilisation de earlyprintk dans /Documentation/kernel-parameters.txt. Pour moi earlyprintk=ttyS0 sur la ligne de commande du kernel suffit.