[console] early printk实现流程

来源:互联网 发布:python 获取列表索引 编辑:程序博客网 时间:2024/05/16 02:38

http://blog.csdn.net/ooonebook/article/details/52654120

本文以ARM为例

一、功能说明

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_printk的实现方式
early_printk与earlycon相比较为落后,其差异可以参考《earlycon实现流程》文章,建议使用earlycon的实现方式来做early console功能

二、需要打开的宏,如何使能

1、需要打开的宏

CONFIG_DEBUG_LL
  • 1

ENTRY(printch)定义在arch/arm/debug.S中,需要用这个宏来打开。

CONFIG_EARLY_PRINTK
  • 1

setup_early_printk的定义。解析cmdline中的early_printk参数并安装boot console。 
early_printk输出函数的定义。

2、如何使能

在cmdline中添加“earlyprintk”字符串,如下:

"console=ttySAC0,115200n8 root=/dev/mmcblk0p1 rw rootwait ignore_loglevel earlyprintk"
  • 1

三、如何使用

1、printascii、printk、early_print、early_printk的区别。

2、在setup.c中加对应例子并打印

比如:

printascii("early_printk success")early_printk("early_printk success");printk("printk success")early_print("early_print success")
  • 1
  • 2
  • 3
  • 4

(1)printascii(不建议使用) 
(2)early_printk 
(3)printk(通过early_console(bcon)来进行输出的) 
(4)early_print(不建议使用) 
内部包含了printascii和printk 
加一条early_print测试一下会不会打印两次

四、early console代码流程

由于early_printk和printk都是基于early console的基础上实现。所以先说明early console的流程。

1、解析cmdline中的“early_printk参数”

arch/arm/kernel/early_printk.c

early_param("earlyprintk", setup_early_printk);
  • 1

early_param和__setup相似,在cmdline中解析到”earlyprintk”字符串时,调用setup_early_printk。 
__setup与early_param不同的是,early_param 宏注册的内核选项必须要在其他内核选项之前被处理。在函数start_kernel中,parse_early_param处理early_param定义的参数,parse_args处理__setup定义的参数,这里不详细说明,只需要知道当从cmdline中查找到对应字符串时,对应的setup函数会被调用。

2、setup_early_printk

arch/arm/kernel/early_printk.c

static int __init setup_early_printk(char *buf){    early_console = &early_console_dev;    register_console(&early_console_dev);    return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

设置了early_console的实现。 
调用register_console向kernel注册一个console(具体细节在console篇中说明)。

3、early_console_dev实现

arch/arm/kernel/early_printk.c

static struct console early_console_dev = {    .name =        "earlycon",    .write =    early_console_write,    .flags =    CON_PRINTBUFFER | CON_BOOT,    .index =    -1,};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中write方法用于实现console输出的入口,也early console的核心。 
CON_PRINTBUFFER标识,表示注册这个console的时候,需要把printk的buf中的log通过这个console进行输出。 
CON_BOOT标识,表示这是一个boot console(bcon)。当启动过程了注册其他非boot console的时候,需要先卸载掉这个console。

4、early_console_write也就是要实现printk和early_printk的核心

arch/arm/kernel/early_printk.c

static void early_console_write(struct console *con, const char *s, unsigned n){    early_write(s, n);}static void early_write(const char *s, unsigned n){    while (n-- > 0) {        if (*s == '\n')            printch('\r');        printch(*s);        s++;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

调用到printch函数中。

5、printch实现

arch/arm/kernel/debug.S

ENTRY(printch)        addruart_current r3, r1, r2        mov    r1, r0        mov    r0, #0        b    1bENDPROC(printch)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

addruart_current宏定义如下:

        .macro    addruart_current, rx, tmp1, tmp2        addruart    \tmp1, \tmp2, \rx        mrc        p15, 0, \rx, c1, c0        tst        \rx, #1        moveq        \rx, \tmp1        movne        \rx, \tmp2        .endm
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

调用到addruart,也就是真正做写数据动作的位置,这也是平台上要实现的东西,也是说移植的核心就是实现这个函数。 
以s5pv210为例 
arch/arm/include/debug/s5pv210.S

.macro addruart, rp, rv, tmp        ldr    \rp, =S5PV210_PA_UART        ldr    \rv, =S3C_VA_UART#if CONFIG_DEBUG_S3C_UART != 0        add    \rp, \rp, #(0x400 * CONFIG_DEBUG_S3C_UART)        add    \rv, \rv, #(0x400 * CONFIG_DEBUG_S3C_UART)#endif    .endm
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意在这里,只是基于bootloader中对于uart已经初始化完成并且可以正常使用的基础上,直接往uart的tx寄存器中写入数据,从而实现串口输出的目的。

至此,有两种方法通过early_console来输出log。 
(1)直接调用early_console->write。 
early_console->write(early_console, buf, n); 
early_printk函数就是通过这种方法实现。 
(2)通过标准printk接口调用到console的write函数。 
printk函数就是通过这种方法实现。 
下面会详细说明。

五、early_printk软件流程

kernel/printk/printk.c

#ifdef CONFIG_EARLY_PRINTKstruct console *early_console;asmlinkage __visible void early_printk(const char *fmt, ...){    va_list ap;    char buf[512];    int n;    if (!early_console)        return;    va_start(ap, fmt);    n = vscnprintf(buf, sizeof(buf), fmt, ap);    va_end(ap);    early_console->write(early_console, buf, n);}#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

直接通过early_console->write来进行console的输出。也就是上述四的内容。

六、printk软件流程(当early_console作为console)

当四中的register_console(&early_console_dev);完成之后,console子系统中的console_drivers就存在了early_console_dev这个console(具体参考“console”的文章)。 
经过printk的标准调用之后

printk->vprintk->console_unlock->call_console_drivers
  • 1

在call_console_drivers调用如下:

     for_each_console(con) {        con->write(con, text, len);    }#define for_each_console(con) \        for (con = console_drivers; con != NULL; con = con->next)
  • 1
  • 2
  • 3
  • 4
  • 5

early_console_dev作为当前console_drivers一个con,其write函数也会被调用。 
early_console_dev->write(con, text, len); 
也就是上述第四节的内容,可以实现输出的目的。

七、Q&A

1、为什么就算early printk功能没有打开,只要串口console被注册,启动早期的log也会被打印出来? 
printk buffer的存在。 
串口console中包含CON_PRINTBUFFER标识,可以打印出printk buffer中的log。具体可以看register_console的实现。

八、backup

1、printascii软件流程(废弃)

arch/arm/kernel/debug.S 
ENTRY(printascii) 
addruart_current r3, r1, r2 
ENDPROC(printascii) 
同样也是通过addruart_current来实现。 
具体参考《四、5实现》

2、 early_print软件流程(同样不推荐使用)

arch/arm/kernel/setup.c

void __init early_print(const char *str, ...){    extern void printascii(const char *);    char buf[256];    va_list ap;    va_start(ap, str);    vsnprintf(buf, sizeof(buf), str, ap);    va_end(ap);#ifdef CONFIG_DEBUG_LL    printascii(buf);#endif    printk("%s", buf);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

通过printascii和printk来实现打印。


原创粉丝点击