本文转发自:http://blog.csdn.net/ooonebook/article/details/52654191
谈早期的打印问题。
early printk和earlycon是两种早期printk打印方法,具体参考:
本文以samsung s5pv210(ARM)为例
一、功能说明&使用方法
1、功能说明
printk的log输出是由console实现(会在其他文章中说明)。由于在kernel刚启动的过程中,还没有为串口等设备等注册console(在device probe阶段实现),此时无法通过正常的console来输出log。
为此,linux提供了early console机制,用于实现为设备注册console之前的早期log的输出,对应console也称为boot console,简称bcon。这个console在kernel启动的早期阶段就会被注册,主要通过输出设备(比如串口设备)的简单的write方法直接进行数据打印。而这个write方法也就是平台实现。
注意,这时候作为输出的串口设备是基于bootloader中已经初始化完成的。
early console机制有两种实现方式,早期的early_printk实现和后面的earlycon实现。在这里主要说明early_con的实现方式
2、earlycon和early_printk的差异
earlycon比early_printk更新的一种early console机制。
在early_printk中,平台主要实现early_console->write的最终流程addruart函数,各个平台通过函数定义的方式来进行实现,兼容性较差。
并且uart寄存器地址都是由自己定义,和正常console的定义完全独立开来,可移植性较差。
而在earlycon中,其通过__earlycon_table维护所有的earlycon_id,通过dts中的正常console的compatible获取到所需要使用的earlycon_id,兼容性较好。
并且dts获取正常console使用的uart寄存器地址来作为earlycon write实现中的uart寄存器基址,可移植性较好。
关于early_printk请参考《[console] early_printk实现流程》一文
3、earlycon使用方法简介
(1)打开对应宏
对应平台需要打开对应earlycon_id实现的宏,以tiny210(s5pv210平台)为例
CONFIG_SERIAL_SAMSUNG_CONSOLE
(2)在cmdline中添加earlycon。
以tiny210为例,使用的是bootargs来传递cmdline,所以在bootargs中添加“earlycon”
(3)在dts中为chosen节点添加“linux,stdout-path”或者“stdout-path”属性,这个属性指定要用作标准输入输出的dts节点路径。
以tiny210为例
(4)调用printk进行打印。
二、earlycon定义
(1)每个earlycon都对应一个earlycon_id,所有的earlycon_id都被维护__earlycon_table中
定义方法如下:
OF_EARLYCON_DECLARE展开如下:
include/linux/serial_core.h
s5pv210对应如下:
这些earlycon_id都放在__earlycon_table中。
(2)earlycon_id定义如下:
include/linux/serial_core.h
char name:earlycon的名字,最终会作为相应console的名称。
char compatible:用于匹配uart对应的dts node。
setup:用来为earlycon设置write函数
三、earlycon安装
0、结构体说明
(1)earlycon_device
include/linux/serial_core.h
struct console *con:用来往console子系统中注册的console。
struct uart_port port:对应的串口的uart_port,需要在解析earlycon的过程中设置
char options[16]:earlycon的参数,选项
unsigned int baud:波特率
1、首先解析cmdline中的earlycon参数
drivers/of/fdt.c
调用early_init_dt_scan_chosen_serial来搜索dts,找到相应的节点并进一步安装。
2、early_init_dt_scan_chosen_serial实现
drivers/of/fdt.c
首先从chosen节点中获取stdout-path或者linux,stdout-path属性,这两个属性指明了标准输入输出串口的dtsi节点路径,以tiny210为例:
通过匹配compatible来查找标准输入输出串口的dtsi对应的earlycon_id。
例如:tiny210的标准输入输出串口节点如下(使用的是uart0)
s5pv210的earlycon_id的compatible也是”samsung,s5pv210-uart”,所以会匹配到s5pv210的earlycon_id,然后调用of_setup_earlycon进行下一步setup。
3、of_setup_earlycon实现如下
drivers/tty/serial/earlycon.c
(1)从dtsi中获取到匹配节点,根据节点的内容来初始化early_console_dev.port
(2)earlycon_init继续对early_console_dev.port参数进行初始化。
(3)通过match->setup来设置write输出函数。
(4)最后,调用register_console向console子系统注册early_console_dev.con。(在“console篇进行说明”)
到此,printk就可以通过early_console_dev.con->write来进行输出log了,后面会继续说明。
early_console_dev定义如下:
drivers/tty/serial/earlycon.c
CON_PRINTBUFFER标识,表示注册这个console的时候,需要把printk的buf中的log通过这个console进行输出。
CON_BOOT标识,表示这是一个boot console(bcon)。当启动过程了注册其他非boot console的时候,需要先卸载掉这个console。具体参考“console”系列文章。
4、match->setup
目的是定义early_console_dev.con的write函数,这个也是平台要实现的核心部分。
也就是在前面说过的定义earlycon_id过程中指定的setup函数,对于s5pv210来说,这个函数是
具体见后续“四、earlycon平台对应的实现位置”的说明.
到这里,earlycon就已经初始化完成了。
三、printk打印流程
当register_console(early_console_dev.con)完成之后,console子系统中的console_drivers就存在了early_console_dev.con这个console(具体参考“console”的文章)。
经过printk的标准调用之后
在call_console_drivers调用如下:
early_console_dev.con作为当前console_drivers一个con,其write函数也会被调用。
early_console_dev.con->write(con, text, len);
最终调用对应平台的console的write函数中。
四、earlycon平台对应的实现位置
s5pv210的early con实现流程:
1、dtis上需要定义compatiable和reg
s5pv210的对应dtsi节点:
arch/arm/boot/dts/s5pv210.dtsi
对于earlycon而言,只在乎compatible属性和reg属性,其他需要初始化的都要在uboot中完成。
compatible属性在earlycon的实现中用于和earlycon_id匹配。
reg属性则是整个uart寄存器的基地址和长度。(因为是和通用uart驱动共用的)。在要实现的write函数中,发送寄存器则是在这个基地址上进行偏移的。
2、代码中实现部分
drivers/tty/serial/samsung.c
需要打开如下宏:
代码中主要目的是实现对应的earlycon_id,而实现earlycon_id的主要工作又是实现其setup函数。实现setup函数的主要核心是实现uart的write函数。
(1)定义一个earlycon_id
注意:”samsung,s5pv210-uart”需要和dtsi里面console节点匹配的。
(2)实现setup函数。(主要目的是实现earlycon_device.con的write方法)
设置s5pv210的私有数据之后,调用samsung的通用的early_console_setup函数
samsung_early_write实现如下:
uart_console_write会将调用samsung_early_putc将字符串中的字符挨个处理。
最终可以观察到uart成功输出log,并且有如下log:
相关阅读: