arm early_printk
在linux 内核调试中,有时需要实时打印早期的日志。
arm实现了early_printk,可以支持内核启动早期的打印。
arm的early_printk实现在这里:
arch/arm/kernel/early_printk.c
这里定义了一个struct console结构体:
early_console_dev
其write回调函数是early_console_write。
early_console_write -> early_write -> printch
printch用于打印字符。
arch/arm/kernel/debug.S
这里面用汇编实现了printch和printascii
在这个汇编文件中还有其他打印函数,不一一描述
这些函数中使用了一些其他的宏:
addruart_current
addruart
waituart
senduart
busyuart
addruart_current是对addruart的包装;
addruart定义在mach自己的debug.S中,例如在./include/debug/msm.S中:
.macro addruart,rp,rv,tmp
ldr \rp, =CONFIG_DEBUG_UART_PHYS
ldr \rv, =CONFIG_DEBUG_UART_VIRT
.endm
waituart, senduart, busyuart定义在./include/asm/hardware/debug-pl01x.S中或./include/asm/hardware/debug-8250.S
然后再mach自己的debug.S中包含其中之一,当然也可以自己定义这三个宏
在arch/arm/kernel/Makefile中编译了debug.S:
obj-$(CONFIG_DEBUG_LL) += debug.o
CONFIG_DEBUG_LL shi
关于register_console
register_sonsole()是console的重要流程,可以此为切入点学习console的实现。
这个函数实现了一些约束,也是console的约束:
- console分为两类,一类是boot console,另一类是real console
- 可以注册多个boot console
- 一旦注册了一个real console,则所有的boot console都会被注销
- 一旦注册了real console则无法再注册boot console
- 通过阅读源码,可知注册多个real console是可行的。
- 所有注册的console是通过console_drivers链表管理的。
early_printk什么时候被调用
- 当调用pirintk.c中的early_printk时。
early_printk -> early_vprintk -> early_console_write - 当调用printk()时。
printk()调用到arm的early_console_write过程比较复杂,总结如下:
printk() --> vprintk_func() --> vprintk_default() --> vprintk_emit --> console_unlock() --> call_console_drivers() --> con->write()
con->write()就是early_console_dev->write().
需要注意一下,console_unlock被广泛调用。
console_unlock()以及call_console_drviers()都是console的通用方法,并不是专门为early_printk设计的。
在解锁console时打印log buffer中的内容的做法是很容易理解的。
earlycon
并非所有的arch都有early_printk。
arm64就没有实现early_printk,而是使用更通用的earlycon。
earlycon是通用的,其驱动源码位于:drivers/tty/serial/earlycon.c
可以通过cmdline或devicetree指定earlycon的设备。
在device tree中指定earlycon的方法是:bootargs = "earlycon=xxx,yyy,zzz"