码迷,mamicode.com
首页 > 其他好文 > 详细

内核启动早期的打印

时间:2018-07-16 19:33:58      阅读:726      评论:0      收藏:0      [点我收藏+]

标签:打印   ++   定位   printf   分享   snprintf   current   index   emc   

在移植4.16内核到tiny4412的过程中遇到一个问题,官方的uboot2012引导内核成功卡在staring the kernel没有了下文,看来内核还没有到注册uart驱动就已经挂掉了,尝试打开 early printk ,让内核自解压之后能够及时的打印信息。

说句题外话,内核启动卡在staring the kernel,在2440的时代,未使用设备树,这种情况往往是机器ID设置错误,或者是串口波特率等方面的原因导致,在设备树的时代,机器ID已经被废除,最有可能的问题可能就是出在设备树的身上。

early printk的使用

  • 配置内核
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
  • 设置环境变量
    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);

在内核启动进入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);
}

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)
.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

addruart_current 和 addruart 都是宏定义
各个平台实现自己的 addruart 宏:4412的位于:arch/arm/include/debug/exynos.S

   .macro addruart, rp, rv, tmp
        mrc p15, 0, \tmp, c0, c0, 0
        and \tmp, \tmp, #0xf0
        teq \tmp, #0xf0 @@ A15
        beq 100f
        mrc p15, 0, \tmp, c0, c0, 5
        and \tmp, \tmp, #0xf00
        teq \tmp, #0x100 @@ A15 + A7 but boot to A7
100: ldreq \rp, =EXYNOS5_PA_UART
        movne \rp, #EXYNOS4_PA_UART @@ EXYNOS4
        ldr \rv, =S3C_VA_UART
#if CONFIG_DEBUG_S3C_UART != 0
        add \rp, \rp, #(0x10000 * CONFIG_DEBUG_S3C_UART)
        add \rv, \rv, #(0x10000 * CONFIG_DEBUG_S3C_UART)
#endif
    .endm

这里返回的是 uart0 的基地址,虚拟地址和物理地址,EXYNOS4_PA_UART=0x13800000
技术分享图片
有了基地址还不够,能需要UTXHn的偏移地址才行
arch/arm/include/debug/samsung.S

.macro senduart,rd,rx
     strb \rd, [\rx, # S3C2410_UTXH]
.endm

include/linux/serial_s3c.h

#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)

注意:汇编阶段调用函数,一定记得保护现场,否则会破坏原有寄存器,引入不必要的麻烦

自解压阶段的打印

内核自解压阶段也可以调用早期的打印函数,需要针对自己的平台稍加修改(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

参考:https://www.cnblogs.com/pengdonglin137/p/5146791.html
参考:http://www.wowotech.net/x_project/kernel_earlycon_porting.html





内核启动早期的打印

标签:打印   ++   定位   printf   分享   snprintf   current   index   emc   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!