内核启动早期的打印

在移植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, #‘\n‘
        bne 3f
        mov r1, #‘\r‘
        waituart r2, r3
        senduart r1, r3
        busyuart r2, r3
        mov r1, #‘\n‘
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, #‘\n‘

8

        bne 3f

9

        mov r1, #‘\r‘

10

        waituart r2, r3

11

        senduart r1, r3

12

        busyuart r2, r3

13

        mov r1, #‘\n‘

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    \tmp1, \tmp2, \rx
         mrc     p15, 0, \rx, c1, c0
         tst     \rx, #1
         moveq       \rx, \tmp1
         movne       \rx, \tmp2
         .endm

7

1

         .macro  addruart_current, rx, tmp1, tmp2

2

         addruart    \tmp1, \tmp2, \rx

3

         mrc     p15, 0, \rx, c1, c0

4

         tst     \rx, #1

5

         moveq       \rx, \tmp1

6

         movne       \rx, \tmp2

7

         .endm

addruart_current 和 addruart 都是宏定义

各个平台实现自己的 addruart 宏:4412的位于:arch/arm/include/debug/exynos.S

 23     .macro addruart, rp, rv, tmp
 24         mrc p15, 0, \tmp, c0, c0, 0
 25         and \tmp, \tmp, #0xf0
 26         teq \tmp, #0xf0     @@ A15
 27         beq 100f
 28         mrc p15, 0, \tmp, c0, c0, 5
 29         and \tmp, \tmp, #0xf00
 30         teq \tmp, #0x100        @@ A15 + A7 but boot to A7
 31 100:        ldreq   \rp, =EXYNOS5_PA_UART
 32         movne   \rp, #EXYNOS4_PA_UART   @@ EXYNOS4
 33         ldr \rv, =S3C_VA_UART
 34 #if CONFIG_DEBUG_S3C_UART != 0
 35         add \rp, \rp, #(0x10000 * CONFIG_DEBUG_S3C_UART)
 36         add \rv, \rv, #(0x10000 * CONFIG_DEBUG_S3C_UART)
 37 #endif
 38     .endm

17

1

 23     .macro addruart, rp, rv, tmp

2

 24         mrc p15, 0, \tmp, c0, c0, 0

3

 25         and \tmp, \tmp, #0xf0

4

 26         teq \tmp, #0xf0     @@ A15

5

 27         beq 100f

6

 28         mrc p15, 0, \tmp, c0, c0, 5

7

 29         and \tmp, \tmp, #0xf00

8

 30         teq \tmp, #0x100        @@ A15 + A7 but boot to A7

9

 31 100:        ldreq   \rp, =EXYNOS5_PA_UART

10

 32         movne   \rp, #EXYNOS4_PA_UART   @@ EXYNOS4

11

 33         ldr \rv, =S3C_VA_UART

12

 34 #if CONFIG_DEBUG_S3C_UART != 0

13

 35         add \rp, \rp, #(0x10000 * CONFIG_DEBUG_S3C_UART)

14

 36         add \rv, \rv, #(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    \rd, [\rx, # S3C2410_UTXH]
 53     .endm

3

1

 51     .macro  senduart,rd,rx

2

 52         strb    \rd, [\rx, # 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  "\nError: unrecognized/unsupported processor variant (0x"
str_p2: .asciz  ").\n"
    .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  "\nError: unrecognized/unsupported processor variant (0x"

11

str_p2: .asciz  ").\n"

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 \rb, \tmp
+               addruart \rb, \tmp, \tmp
                .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    #‘\n‘
+        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    #‘\n‘
+        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 \rb, \tmp

16

+               addruart \rb, \tmp, \tmp

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    #‘\n‘

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    #‘\n‘

42

+        ldmfd    sp!, {r0-r3, r10-r12, lr}

43


44

 #ifdef CONFIG_ARM_VIRT_EXT

45


参考:https://www.cnblogs.com/pengdonglin137/p/5146791.html

参考:http://www.wowotech.net/x_project/kernel_earlycon_porting.html

来自为知笔记(Wiz)

原文地址:https://www.cnblogs.com/bringup/p/9316103.html

时间: 2024-08-30 05:31:19

内核启动早期的打印的相关文章

I.MX6Q(TQIMX6Q/TQE9)学习笔记——内核启动与文件系统挂载

经过前面的移植,u-boot已经有能力启动内核了,本文主要来看下如何通过之前移植的u-boot来启动内核.如果按照前面的文章完成了LTIB 的编译,那么,Linux的内核应该就会出现rpm/BUILD/目录下,接下来,我们就开始移植这个3.0.35版本的内核到TQIMX6Q. 内核的编译 为了简化内核编译的过程,可以在内核目录下创建编译脚本,命名为build.sh,内容如下: [cpp] view plaincopy #!/bin/sh export ARCH=arm export CROSS_

Linux移植之内核启动过程引导阶段分析

在Linux移植之make uImage编译过程分析中已经提到了uImage是一个压缩的包并且内含压缩程序,可以进行自解压.自解压完成之后内核代码从物理地址为0x30008000处开始运行.下面分析在进入C之前内核做的一些工作,以下是内核启动过程中打印出来的信息,其中Uncompressing Linux就是在自解压代码.make uImage编译的最后也给出了链接脚本arch/arm/kernel/vmlinux.lds,以及链接的顺序arch/arm/kernel/head.o 是第一个.

屏蔽内核启动打印信息

最近做的4412板子终于经过测试形成产品,在这之前需要把内核启动过程中的打印信息去掉,并且更换启动LOGO,下面讲讲自己去掉打印信息的过程. 去掉打印信息方法很多,我使用一个相对比较简单的,就是将下面选项的[*]去掉 Device Drivers  ---> Character devices  ---> [* ]   Support for console on virtual terminal 这样printk的打印信息就不会输出在显示器或LCD上. 将打印信息去掉后,莫名其妙的会出现因为

tiny4412 串口驱动分析八 --- log打印的几个阶段之内核启动阶段(printk tiny4412串口驱动的注册)

作者:彭东林 邮箱:[email protected] 开发板:tiny4412ADK+S700 4GB Flash 主机:Wind7 64位 虚拟机:Vmware+Ubuntu12_04 u-boot:U-Boot 2010.12 Linux内核版本:linux-3.0.31 Android版本:android-4.1.2 在arch/arm/mach-exynos/mach-tiny4412.c中: MACHINE_START(TINY4412, "TINY4412") .boot

tiny4412 串口驱动分析七 --- log打印的几个阶段之内核启动阶段(earlyprintk)

作者:彭东林 邮箱:[email protected] 开发板:tiny4412ADK+S700 4GB Flash 主机:Wind7 64位 虚拟机:Vmware+Ubuntu12_04 u-boot:U-Boot 2010.12 Linux内核版本:linux-3.0.31 Android版本:android-4.1.2 下面要分析的是内核Log打印的几个阶段 自解压阶段 内核启动阶段 内核启动完全以后 shell终端下 在这个阶段内核log打印可以调用printk和printascii,同

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html 前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线

Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】

原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938390.html 在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段, 下面我就顺这代码逐个函数的解释,但是这里并不会过于深入

Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938393.html 在分析start_kernel函数的时候,其中有构架相关的初始化函数setup_arch. 此函数根据构架而异,对于ARM构架的详细分析如下: void __init setup_arch(char **cmdlin

Linux内核启动分析

张超<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 我的代码可见https://www.shiyanlou.com/courses/reports/986221 在这里我们用的是linux-3.18.6版本,以下简写成linux. start_kernel在 /linux/init/main.c中定义: 这个函数是内核由引导程序引导以后,由自解压程序解压以后执行的第一个函数,可以认为是整个内核的入口函数,以后我