After linux boots, you can specify a console-connected device by specifying it with an option such as console = ttyS0,115200
.
But what if I want to see the log before it starts? How do you display it? Let's sort out that.
The survey base is around here.
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm
Although it is a useful debugging routine when MM problem or printk does not work, It is for debugging only, and it is supposed to be removed in the product kernel.
You need to be careful when handling it!
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!
*/
It seems that you should specify ʻearly printk` as the boot option of linux.
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/Documentation/admin-guide/kernel-parameters.txt#n1135
What happens when you specify this?
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/kernel/early_printk.c
The address of the character to be displayed is passed to the printascii () function.
Click here for the definition of printascii. In the case of ARM assembler, the argument is in r0. In this case, r0 is a pointer to a string.
-\ 0 → Processing is completed. -Convert to \ n → \ r \ n and output characters --Other than that → Character output
I think.
arch/arm/kernel/debug.S
#ifndef CONFIG_DEBUG_SEMIHOSTING
ENTRY(printascii)
addruart_current r3, r1, r2 // 2.Explained in 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.Explained in 2
senduart r1, r3 // 4.Explained in 3
busyuart r2, r3 // 4.Explained in 4
mov r1, #'\n'
3: waituart r2, r3
senduart r1, r3
busyuart r2, r3
b 1b
ENDPROC(printascii)
<Omitted>
#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)
The behavior changes depending on whether DEBUG_SEMIHOSTING is enabled or not.
If it is invalid, it is trying to output characters by making full use of adduart_current / waituart / senduart. This will be a product-specific implementation (see below).
What if it is valid? 0x04 is set in the r0 register, and the character to be displayed is set in the r1 register, and 0x123456 is SVCed in ARM mode. This is a function that can be used regardless of the model, and can be viewed (probably) via a debugger such as JTAG.
I will explain adduart_current, which is a process common to all models.
arch/arm/kernel/debug.S
#ifdef CONFIG_MMU
.macro addruart_current, rx, tmp1, tmp2
addruart \tmp1, \tmp2, \rx // 4.Explained in 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 */
accessible address of rx [out] tmp1 / tmp2 temp1 [tmp]: Temporary use temp2 [tmp]: Temporary use
If the MMU is enabled, switch PHYS / VIRT according to the state of the MMU, If the MMU is disabled, use PHYS.
In other words, the appropriate address for controlling the UART device is assigned to the first register (r3).
Now let's see where each function called in printascii is defined.
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.
These options are for real kernel hackers who want to get their hands dirty
Select Y to define printascii, printch and printhex in the kernel. This will be useful if you want to debug the code that runs before the console is initialized.
Note that selecting this option limits the kernel to a single UART definition, as shown below. Do not enable this option on portable kernels as it will * not work * if you try to boot the kernel image on another platform.
So, it's really solid, so it's a non-portable fix.
If DEBUG_ALPINE_UART0 etc. is defined in the above settings, DEBUG_UART_8250 etc. will be specified and replaced with the definition of which assembler code to include.
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
As a result of the above, CONFIG_DEBUG_LL_INCLUDE becomes the string "debug / *. S "
, which is #included.
arch/arm/kernel/debug.S
#if !defined(CONFIG_DEBUG_SEMIHOSTING)
#include CONFIG_DEBUG_LL_INCLUDE
#endif
Click here for definitions such as CONFOG_DEBUG_UART_PHTS that will be used in 4.1 soon.
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
:
:
The actual assembler code is implemented under include / debug respectively. Here, using the bcm code, which seems to have the shortest source code, Easily check the definition of the provided function. 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]: Data passed to waituart / busyuart
This function is also indirectly called from the mmu control. Maybe this won't be used if the iomap table is defined ...
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]: Temporary use rx [in]: UART device port
Read the memory in OFFSET of UART_IR_REG on the UART device port and loop until it matches # (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]: String to export rx [in]: UART device port
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]: Temporary use rx [in]: UART device port
Read the memory in OFFSET of UART_IR_REG on the UART device port and loop until it matches # (1 << UART_IR_TXTRESH).
--Added earlyprintk to boot parameter at boot time --Enable DEBUG_LL in KERNEL CONFIG --Added debugging settings to arch / arm / Kconfig.debug. --Added assembler corresponding to arch / arm / Kconfig.debug --Added the corresponding assembler under arch / arm / include --waituart: Waiting for export --senduart: Actually export as FIFO --busyuart: Waiting for export completion
that's all.
Recommended Posts