Après le démarrage de Linux, vous pouvez spécifier un périphérique connecté à la console en le spécifiant avec une option telle que console = ttyS0,115200
.
Mais que faire si je veux voir le journal avant qu'il ne démarre? Comment l'affichez-vous? Faisons le tri.
La base de sondage est ici.
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm
Bien qu'il s'agisse d'une routine de débogage efficace lorsque le problème MM ou printk ne fonctionne pas, Il est uniquement destiné au débogage et est censé être supprimé dans le noyau du produit.
Vous devez être prudent lors de sa manipulation!
arch/arm/kernel/debug.S
/*
* Some debugging routines (useful if you've got MM problems and
* printk isn't working). For DEBUGGING ONLY!!! Do not leave
* references to these in a production kernel!
*/
Il semble que vous deviez spécifier ʻearly printk` comme option de démarrage de linux.
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/Documentation/admin-guide/kernel-parameters.txt#n1135
Que se passe-t-il lorsque vous spécifiez cela?
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/kernel/early_printk.c
L'adresse du caractère à afficher est transmise à la fonction printascii ().
Cliquez ici pour la définition de printascii. Dans le cas de l'assembleur ARM, l'argument est dans r0. Dans ce cas, r0 est un pointeur vers une chaîne.
Je pense.
arch/arm/kernel/debug.S
#ifndef CONFIG_DEBUG_SEMIHOSTING
ENTRY(printascii)
addruart_current r3, r1, r2 // 2.Expliqué en 1
1: teq r0, #0
ldrbne r1, [r0], #1
teqne r1, #0
reteq lr
2: teq r1, #'\n'
bne 3f
mov r1, #'\r'
waituart r2, r3 // 4.Expliqué en 2
senduart r1, r3 // 4.Expliqué en 3
busyuart r2, r3 // 4.Expliqué en 4
mov r1, #'\n'
3: waituart r2, r3
senduart r1, r3
busyuart r2, r3
b 1b
ENDPROC(printascii)
<Omis>
#else
ENTRY(printascii)
mov r1, r0
mov r0, #0x04 @ SYS_WRITE0
ARM( svc #0x123456 )
#ifdef CONFIG_CPU_V7M
THUMB( bkpt #0xab )
#else
THUMB( svc #0xab )
#endif
ret lr
ENDPROC(printascii)
Le comportement change selon que DEBUG_SEMIHOSTING est activé ou non.
S'il n'est pas valide, il essaie de générer des caractères en utilisant pleinement adduart_current / waituart / senduart. Ce sera une implémentation spécifique au produit (voir ci-dessous).
Et s'il est valide? 0x04 est défini dans le registre r0, et le caractère à afficher est défini dans le registre r1 et 0x123456 est SVC en mode ARM. C'est une fonction qui peut être utilisée quel que soit le modèle, et peut être visualisée (probablement) via un débogueur tel que JTAG.
J'expliquerai adduart_current, qui est un processus commun à tous les modèles.
arch/arm/kernel/debug.S
#ifdef CONFIG_MMU
.macro addruart_current, rx, tmp1, tmp2
addruart \tmp1, \tmp2, \rx // 4.Expliqué en 1
mrc p15, 0, \rx, c1, c0
tst \rx, #1
moveq \rx, \tmp1
movne \rx, \tmp2
.endm
#else /* !CONFIG_MMU */
.macro addruart_current, rx, tmp1, tmp2
addruart \rx, \tmp1, \tmp2
.endm
#endif /* CONFIG_MMU */
rx [out] adresse accessible de tmp1 / tmp2 temp1 [tmp]: utilisé pour temporaire temp2 [tmp]: utilisé pour temporaire
Si MMU est activé, changez PHYS / VIRT en fonction de l'état de MMU, Si MMU est désactivé, utilisez PHYS.
En d'autres termes, l'adresse appropriée pour contrôler le dispositif UART est attribuée au premier registre (r3).
Voyons maintenant où est définie chaque fonction appelée dans printascii.
arch/arm/Kconfig.debug
# These options are only for real kernel hackers who want to get their hands dirty.
config DEBUG_LL
bool "Kernel low-level debugging functions (read help!)"
depends on DEBUG_KERNEL
help
Say Y here to include definitions of printascii, printch, printhex
in the kernel. This is helpful if you are debugging code that
executes before the console is initialized.
Note that selecting this option will limit the kernel to a single
UART definition, as specified below. Attempting to boot the kernel
image on a different platform *will not work*, so this option should
not be enabled for kernels that are intended to be portable.
choice
prompt "Kernel low-level debugging port"
depends on DEBUG_LL
config DEBUG_ALPINE_UART0
bool "Kernel low-level debugging messages via Alpine UART0"
depends on ARCH_ALPINE
select DEBUG_UART_8250
help
Say Y here if you want kernel low-level debugging support
on Alpine based platforms.
Ces options sont destinées aux vrais hackers du noyau qui veulent se salir les mains
Sélectionnez Y pour définir printascii, printch et printhex dans le noyau. Cela sera utile si vous souhaitez déboguer le code qui s'exécute avant l'initialisation de la console.
Notez que la sélection de cette option limite le noyau à une seule définition UART, comme illustré ci-dessous. N'activez pas cette option sur les noyaux portables car elle ne fonctionnera * pas * si vous essayez de démarrer l'image du noyau sur une autre plate-forme.
Donc, c'est vraiment solide ici, donc c'est un correctif qui le rend moins portable.
Si DEBUG_ALPINE_UART0 etc. est défini dans les paramètres ci-dessus, DEBUG_UART_8250 etc. est spécifié et remplacé par la définition du code assembleur à inclure.
arch/arm/Kconfig.debug
config DEBUG_LL_INCLUDE
string
default "debug/sa1100.S" if DEBUG_SA1100
default "debug/palmchip.S" if DEBUG_UART_8250_PALMCHIP
default "debug/8250.S" if DEBUG_LL_UART_8250 || DEBUG_UART_8250
default "debug/at91.S" if DEBUG_AT91_UART
default "debug/asm9260.S" if DEBUG_ASM9260_UART
default "debug/clps711x.S" if DEBUG_CLPS711X_UART1 || DEBUG_CLPS711X_UART2
default "debug/dc21285.S" if DEBUG_DC21285_PORT
default "debug/meson.S" if DEBUG_MESON_UARTAO
default "debug/pl01x.S" if DEBUG_LL_UART_PL01X || DEBUG_UART_PL01X
default "debug/exynos.S" if DEBUG_EXYNOS_UART
default "debug/efm32.S" if DEBUG_LL_UART_EFM32
default "debug/icedcc.S" if DEBUG_ICEDCC
À la suite de ce qui précède, CONFIG_DEBUG_LL_INCLUDE devient la chaîne de caractères " debug / *. S "
, qui est #included.
arch/arm/kernel/debug.S
#if !defined(CONFIG_DEBUG_SEMIHOSTING)
#include CONFIG_DEBUG_LL_INCLUDE
#endif
Cliquez ici pour des définitions telles que CONFOG_DEBUG_UART_PHTS qui seront bientôt utilisées dans la version 4.1.
arch/arm/Kconfig.debug
config DEBUG_UART_PHYS
hex "Physical base address of debug UART"
default 0x01c20000 if DEBUG_DAVINCI_DMx_UART0
default 0x01c28000 if DEBUG_SUNXI_UART0
default 0x01c28400 if DEBUG_SUNXI_UART1
default 0x01d0c000 if DEBUG_DAVINCI_DA8XX_UART1
default 0x01d0d000 if DEBUG_DAVINCI_DA8XX_UART2
default 0x01f02800 if DEBUG_SUNXI_R_UART
default 0x02530c00 if DEBUG_KEYSTONE_UART0
default 0x02531000 if DEBUG_KEYSTONE_UART1
default 0x03010fe0 if ARCH_RPC
:
config DEBUG_UART_VIRT
hex "Virtual base address of debug UART"
default 0xc881f000 if DEBUG_RV1108_UART2
default 0xc8821000 if DEBUG_RV1108_UART1
default 0xc8912000 if DEBUG_RV1108_UART0
default 0xe0010fe0 if ARCH_RPC
default 0xf0000be0 if ARCH_EBSA110
default 0xf0010000 if DEBUG_ASM9260_UART
default 0xf0100000 if DEBUG_DIGICOLOR_UA0
default 0xf01fb000 if DEBUG_NOMADIK_UART
default 0xf0201000 if DEBUG_BCM2835 || DEBUG_BCM2836
default 0xf1000300 if DEBUG_BCM_5301X
default 0xf1000400 if DEBUG_BCM_HR2
default 0xf1002000 if DEBUG_MT8127_UART0
default 0xf1006000 if DEBUG_MT6589_UART0
default 0xf1009000 if DEBUG_MT8135_UART3
default 0xf1023000 if DEBUG_BCM_IPROC_UART3
default 0xf11f1000 if DEBUG_VERSATILE
default 0xf1600000 if DEBUG_INTEGRATOR
:
:
Le code assembleur réel est implémenté respectivement sous include / debug. Ici, en utilisant le code bcm, qui semble avoir le code source le plus court, Vérifiez facilement la définition de la fonction fournie. 4.1 adduart, rp,rv,tmp
/arch/arm/include/debug/bcm63xx.S
.macro addruart, rp, rv, tmp
ldr \rp, =CONFIG_DEBUG_UART_PHYS
ldr \rv, =CONFIG_DEBUG_UART_VIRT
.endm
rp [out] : CONFIG_DEBUG_UART_PHYS rv [out] : CONFIG_DEBUG_UART_VIRT tmp [out]: données transmises à waituart / busyuart
Cette fonction est également appelée indirectement depuis le contrôle mmu. Peut-être que cela ne sera pas utilisé si la table iomap est définie ...
arch/arm/mm/mmu.c
/*
* Ask the machine support to map in the statically mapped devices.
*/
if (mdesc->map_io)
mdesc->map_io();
else
debug_ll_io_init();
:
:
#ifdef CONFIG_DEBUG_LL
void __init debug_ll_io_init(void)
{
struct map_desc map;
debug_ll_addr(&map.pfn, &map.virtual);
if (!map.pfn || !map.virtual)
return;
map.pfn = __phys_to_pfn(map.pfn);
map.virtual &= PAGE_MASK;
map.length = PAGE_SIZE;
map.type = MT_DEVICE;
iotable_init(&map, 1);
}
#endif
arch/arm/kernel/debug.S
#ifdef CONFIG_MMU
ENTRY(debug_ll_addr)
addruart r2, r3, ip
str r2, [r0]
str r3, [r1]
ret lr
ENDPROC(debug_ll_addr)
#endif
4.2 waituart, rd,rx
/arch/arm/include/debug/bcm63xx.S
.macro waituart, rd, rx
1001: ldr \rd, [\rx, #UART_IR_REG]
tst \rd, #(1 << UART_IR_TXEMPTY)
beq 1001b
.endm
rd [tmp]: utilisé pour rx [in]: port de périphérique UART
Lisez la mémoire dans OFFSET de UART_IR_REG du port de périphérique UART et bouclez jusqu'à ce qu'elle corresponde à # (1 << UART_IR_TXEMPTY).
4.3 senduart, rd,rx
/arch/arm/include/debug/bcm63xx.S
.macro senduart, rd, rx
/* word access do not work */
strb \rd, [\rx, #UART_FIFO_REG]
.endm
rd [in]: Exporter la chaîne de caractères rx [in]: port de périphérique UART
4.4 busyuart, rd,rx
/arch/arm/include/debug/bcm63xx.S
.macro busyuart, rd, rx
1002: ldr \rd, [\rx, #UART_IR_REG]
tst \rd, #(1 << UART_IR_TXTRESH)
beq 1002b
.endm
rd [tmp]: utilisé pour rx [in]: port de périphérique UART
Lisez la mémoire dans OFFSET de UART_IR_REG du port de périphérique UART et bouclez jusqu'à ce qu'elle corresponde à # (1 << UART_IR_TXTRESH).
c'est tout.
Recommended Posts