内核启动早期的打印

Posted bringup

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内核启动早期的打印相关的知识,希望对你有一定的参考价值。


    在移植4.16内核到tiny4412的过程中遇到一个问题,官方的uboot2012引导内核成功卡在staring the kernel没有了下文,看来内核还没有到注册uart驱动就已经挂掉了,尝试打开 early printk ,让内核自解压之后能够及时的打印信息。
    说句题外话,内核启动卡在staring the kernel,在2440的时代,未使用设备树,这种情况往往是机器ID设置错误,或者是串口波特率等方面的原因导致,在设备树的时代,机器ID已经被废除,最有可能的问题可能就是出在设备树的身上。
    
early printk的使用:
    1、配置内核

        make menuconfig

          Kernel hacking  --->

              [*] Kernel low-level debugging functions (read help!)

                 Kernel low-level debugging port (Use Samsung S3C UART 0 for low-level debug)

              [*] Early printk

    2、设置环境变量

         Add earlyprintk to your kernel parameters to enable this console

         在 bootargs 中添加 earlyprintk


首先来看 bootargs earlyprintk的解析:

arch/arm/kernel/early_printk.c

extern void printascii(const char *);

static void early_write(const char *s, unsigned n)
{
        char buf[128];
        while (n) {
                unsigned l = min(n, sizeof(buf)-1);
                memcpy(buf, s, l);
                buf[l] = 0;
                s += l;
                n -= l;
                printascii(buf);
        }
}

static void early_console_write(struct console *con, const char *s, unsigned n)
{
        early_write(s, n);
}

static struct console early_console_dev = {
        .name =         "earlycon",
        .write =        early_console_write,
        .flags =        CON_PRINTBUFFER | CON_BOOT,
        .index =        -1,
};

static int __init setup_early_printk(char *buf)
{
        early_console = &early_console_dev;
        register_console(&early_console_dev);
        return 0;
}

early_param("earlyprintk", setup_early_printk);
35
 
1
extern void printascii(const char *);
2
3
static void early_write(const char *s, unsigned n)
4
{
5
        char buf[128];
6
        while (n) {
7
                unsigned l = min(n, sizeof(buf)-1);
8
                memcpy(buf, s, l);
9
                buf[l] = 0;
10
                s += l;
11
                n -= l;
12
                printascii(buf);
13
        }
14
}
15
16
static void early_console_write(struct console *con, const char *s, unsigned n)
17
{
18
        early_write(s, n);
19
}
20
21
static struct console early_console_dev = {
22
        .name =         "earlycon",
23
        .write =        early_console_write,
24
        .flags =        CON_PRINTBUFFER | CON_BOOT,
25
        .index =        -1,
26
};
27
28
static int __init setup_early_printk(char *buf)
29
{
30
        early_console = &early_console_dev;
31
        register_console(&early_console_dev);
32
        return 0;
33
}
34
35
early_param("earlyprintk", setup_early_printk);

在内核启动进入C语言阶段,start_kernel->parse_early_param 就会第一时间解析early_param("earlyprintk", setup_early_printk);

然后调用 register_console(&early_console_dev);

可以看到 early_console 的 write 函数最终靠 printascii 来实现,同样,直接调用 earlyprintk 也会调用 printascii 

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);
}
15
 
1
void __init early_print(const char *str, ...)
2
{
3
    extern void printascii(const char *);
4
    char buf[256];
5
    va_list ap;
6
7
    va_start(ap, str);
8
    vsnprintf(buf, sizeof(buf), str, ap);
9
    va_end(ap);
10
11
#ifdef CONFIG_DEBUG_LL
12
    printascii(buf);
13
#endif
14
    printk("%s", buf);
15
}
printascii:arch/arm/kernel/debug.S
ENTRY(printascii)
        addruart_current r3, r1, r2
1:      teq r0, #0
        ldrneb  r1, [r0], #1
        teqne   r1, #0
        reteq   lr
2:      teq     r1, #‘
‘
        bne 3f
        mov r1, #‘
‘
        waituart r2, r3
        senduart r1, r3
        busyuart r2, r3
        mov r1, #‘
‘
3:      waituart r2, r3
        senduart r1, r3
        busyuart r2, r3
        b   1b
ENDPROC(printascii)
18
 
1
ENTRY(printascii)
2
        addruart_current r3, r1, r2
3
1:      teq r0, #0
4
        ldrneb  r1, [r0], #1
5
        teqne   r1, #0
6
        reteq   lr
7
2:      teq     r1, #‘
‘
8
        bne 3f
9
        mov r1, #‘
‘
10
        waituart r2, r3
11
        senduart r1, r3
12
        busyuart r2, r3
13
        mov r1, #‘
‘
14
3:      waituart r2, r3
15
        senduart r1, r3
16
        busyuart r2, r3
17
        b   1b
18
ENDPROC(printascii)
         .macro  addruart_current, rx, tmp1, tmp2
         addruart    	mp1, 	mp2, 
x
         mrc     p15, 0, 
x, c1, c0
         tst     
x, #1
         moveq       
x, 	mp1
         movne       
x, 	mp2
         .endm
7
 
1
         .macro  addruart_current, rx, tmp1, tmp2
2
         addruart    	mp1, 	mp2, 
x
3
         mrc     p15, 0, 
x, c1, c0
4
         tst     
x, #1
5
         moveq       
x, 	mp1
6
         movne       
x, 	mp2
7
         .endm
addruart_current 和 addruart 都是宏定义
各个平台实现自己的 addruart 宏:4412的位于:arch/arm/include/debug/exynos.S
 23     .macro addruart, rp, rv, tmp
 24         mrc p15, 0, 	mp, c0, c0, 0
 25         and 	mp, 	mp, #0xf0
 26         teq 	mp, #0xf0     @@ A15
 27         beq 100f
 28         mrc p15, 0, 	mp, c0, c0, 5
 29         and 	mp, 	mp, #0xf00
 30         teq 	mp, #0x100        @@ A15 + A7 but boot to A7
 31 100:        ldreq   
p, =EXYNOS5_PA_UART
 32         movne   
p, #EXYNOS4_PA_UART   @@ EXYNOS4
 33         ldr 
v, =S3C_VA_UART
 34 #if CONFIG_DEBUG_S3C_UART != 0
 35         add 
p, 
p, #(0x10000 * CONFIG_DEBUG_S3C_UART)
 36         add 
v, 
v, #(0x10000 * CONFIG_DEBUG_S3C_UART)
 37 #endif
 38     .endm
17
 
1
 23     .macro addruart, rp, rv, tmp
2
 24         mrc p15, 0, 	mp, c0, c0, 0
3
 25         and 	mp, 	mp, #0xf0
4
 26         teq 	mp, #0xf0     @@ A15
5
 27         beq 100f
6
 28         mrc p15, 0, 	mp, c0, c0, 5
7
 29         and 	mp, 	mp, #0xf00
8
 30         teq 	mp, #0x100        @@ A15 + A7 but boot to A7
9
 31 100:        ldreq   
p, =EXYNOS5_PA_UART
10
 32         movne   
p, #EXYNOS4_PA_UART   @@ EXYNOS4
11
 33         ldr 
v, =S3C_VA_UART
12
 34 #if CONFIG_DEBUG_S3C_UART != 0
13
 35         add 
p, 
p, #(0x10000 * CONFIG_DEBUG_S3C_UART)
14
 36         add 
v, 
v, #(0x10000 * CONFIG_DEBUG_S3C_UART)
15
 37 #endif
16
 38     .endm
17
技术分享图片
这里返回的是 uart0 的基地址,虚拟地址和物理地址,EXYNOS4_PA_UART=0x13800000
有了基地址还不够,能需要UTXHn的偏移地址才行
arch/arm/include/debug/samsung.S
 51     .macro  senduart,rd,rx
 52         strb    
d, [
x, # S3C2410_UTXH]
 53     .endm
3
 
1
 51     .macro  senduart,rd,rx
2
 52         strb    
d, [
x, # S3C2410_UTXH]
3
 53     .endm
include/linux/serial_s3c.h:33:#define S3C2410_UTXH        (0x20)
因此,可以得知,内核earlyprintk不会去初始化串口,直接使用Uboot初始化ok的,这是个好消息~
C语言阶段,可以调用early_printk,那么在自解压之后,C语言之前的汇编阶段,如何打印呢?
参考:
__error_p:
#ifdef CONFIG_DEBUG_LL
    adr r0, str_p1
    bl  printascii
    mov r0, r9
    bl  printhex8
    adr r0, str_p2
    bl  printascii
    b   __error
str_p1: .asciz  "
Error: unrecognized/unsupported processor variant (0x"
str_p2: .asciz  ").
"
    .align
#endif
ENDPROC(__error_p)
14
 
1
__error_p:
2
#ifdef CONFIG_DEBUG_LL
3
    adr r0, str_p1
4
    bl  printascii
5
    mov r0, r9
6
    bl  printhex8
7
    adr r0, str_p2
8
    bl  printascii
9
    b   __error
10
str_p1: .asciz  "
Error: unrecognized/unsupported processor variant (0x"
11
str_p2: .asciz  ").
"
12
    .align
13
#endif
14
ENDPROC(__error_p)
注意:汇编阶段调用函数,一定记得保护现场,否则会破坏原有寄存器,引入不必要的麻烦

自解压阶段的打印:
内核自解压阶段也可以调用早期的打印函数,需要针对自己的平台稍加修改(addruart 宏定义),这个阶段可能出现的问题,内核自解压把设备树覆盖了,导致卡在 staring the kernel 我这里就是如此~~,修改Uboot设备树的重定位地址即可。
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -24,6 +24,7 @@
  * 100% relocatable.  Any attempt to do so will result in a crash.
  * Please select one of the following when turning on debugging.
  */
+#define DEBUG
 #ifdef DEBUG

 #if defined(CONFIG_DEBUG_ICEDCC)
@@ -67,7 +68,7 @@
                .endm
 #else
                .macro  loadsp, rb, tmp
-               addruart 
b, 	mp
+               addruart 
b, 	mp, 	mp
                .endm
 #endif
 #endif
@@ -554,6 +555,12 @@ not_relocated:     mov     r0, #0
  *   r7  = architecture ID
  *   r8  = atags pointer
  */
+        stmfd    sp!, {r0-r3, r10-r12, lr}
+        mov    r0, r8
+        bl    memdump
+        kputc    #‘
‘
+        ldmfd    sp!, {r0-r3, r10-r12, lr}
+
                mov     r0, r4
                mov     r1, sp                  @ malloc space above stack
                add     r2, sp, #0x10000        @ 64k max
@@ -563,6 +570,12 @@ not_relocated:     mov     r0, #0
                bl      cache_off
                mov     r1, r7                  @ restore architecture number
                mov     r2, r8                  @ restore atags pointer
+
+        stmfd    sp!, {r0-r3, r10-r12, lr}
+        mov    r0, r8
+        bl    memdump
+        kputc    #‘
‘
+        ldmfd    sp!, {r0-r3, r10-r12, lr}

 #ifdef CONFIG_ARM_VIRT_EXT
45
 
1
--- a/arch/arm/boot/compressed/head.S
2
+++ b/arch/arm/boot/compressed/head.S
3
@@ -24,6 +24,7 @@
4
  * 100% relocatable.  Any attempt to do so will result in a crash.
5
  * Please select one of the following when turning on debugging.
6
  */
7
+#define DEBUG
8
 #ifdef DEBUG
9
10
 #if defined(CONFIG_DEBUG_ICEDCC)
11
@@ -67,7 +68,7 @@
12
                .endm
13
 #else
14
                .macro  loadsp, rb, tmp
15
-               addruart 
b, 	mp
16
+               addruart 
b, 	mp, 	mp
17
                .endm
18
 #endif
19
 #endif
20
@@ -554,6 +555,12 @@ not_relocated:     mov     r0, #0
21
  *   r7  = architecture ID
22
  *   r8  = atags pointer
23
  */
24
+        stmfd    sp!, {r0-r3, r10-r12, lr}
25
+        mov    r0, r8
26
+        bl    memdump
27
+        kputc    #‘
‘
28
+        ldmfd    sp!, {r0-r3, r10-r12, lr}
29
+
30
                mov     r0, r4
31
                mov     r1, sp                  @ malloc space above stack
32
                add     r2, sp, #0x10000        @ 64k max
33
@@ -563,6 +570,12 @@ not_relocated:     mov     r0, #0
34
                bl      cache_off
35
                mov     r1, r7                  @ restore architecture number
36
                mov     r2, r8                  @ restore atags pointer
37
+
38
+        stmfd    sp!, {r0-r3, r10-r12, lr}
39
+        mov    r0, r8
40
+        bl    memdump
41
+        kputc    #‘
‘
42
+        ldmfd    sp!, {r0-r3, r10-r12, lr}
43
44
 #ifdef CONFIG_ARM_VIRT_EXT
45





















以上是关于内核启动早期的打印的主要内容,如果未能解决你的问题,请参考以下文章

内核启动早期的打印

内核启动早期的打印

内核启动早期的打印

LINUX PID 1和SYSTEMD PID 0 是内核的一部分,主要用于内进换页,内核初始化的最后一步就是启动 init 进程。这个进程是系统的第一个进程,PID 为 1,又叫超级进程(代码片段

Initramfs 原理和实践

Linux系统启动过程的打印信息从何而来?