打开Linux内核启动早期的log



打开Linux内核启动早期的log

有时会遇到当在u-boot中执行完bootm后,打印出start kernel后串口就没有再输出任何信息了。此时就需要打开内核早期的log

makemenuconfig

  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

对于earlyprintk,还需要在bootargs中添加参数earlyprintk才能生效,有了上面这几个配置,会有下面几个宏生效:

CONFIG_DEBUG_LL=y

CONFIG_DEBUG_S3C_UART0=y

CONFIG_DEBUG_LL_INCLUDE="debug/exynos.S"

CONFIG_DEBUG_UNCOMPRESS=y
CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
CONFIG_EARLY_PRINTK=y

关于earlyprintk的解析在文件arch/arm/kernel/early_printk.c中:

   1: extern void printch(int);

   2:

   3: static voidearly_write(const char *s, unsigned n)

   4: {

   5:     while (n-- >; 0) {

   6:         if (*s== '\n')

   7:             printch('\r');

   8:         printch(*s);

   9:         s++;

  10:     }

  11: }

  12: 

  13: static void early_console_write(struct console *con, const char *s, unsigned n)

  14: {

  15:     early_write(s, n);

  16: }

  17: 

  18: static struct console early_console_dev = {

  19:     .name =        "earlycon",

  20:     .write =    early_console_write,

  21:     .flags =    CON_PRINTBUFFER | CON_BOOT,

  22:     .index =    -1,

  23: };

  24:

  25: static int __initsetup_early_printk(char *buf)

  26: {

  27:     early_console = &;early_console_dev;

  28:     register_console(&;early_console_dev);

  29:     return 0;

  30: }

  31: 

  32: early_param("earlyprintk", setup_early_printk);

其中printch都是通过汇编语言实现的。

arch/arm/Kconfig.debug中可以看到:

configDEBUG_LL
    bool "Kernel low-level debuggingfunctions (read help!)"
    depends on DEBUG_KERNEL
    help
      Say Y here to include definitions of printascii,printch, printhex
      in the kernel.  This is helpful if you aredebugging code that
      executes beforethe console is initialized
.

 

configDEBUG_S3C_UART0
    depends on PLAT_SAMSUNG
    select DEBUG_EXYNOS_UART if ARCH_EXYNOS
    select DEBUG_S3C24XX_UART if ARCH_S3C24XX
    select DEBUG_S5PV210_UART if ARCH_S5PV210
    bool "Use Samsung S3C UART 0 for low-level debug"
    help
      Say Y here if you want the debug printroutines to direct
      their output to UART 0. The portmust have been initialised
      by the boot-loaderbefore use.

 

configDEBUG_LL_INCLUDE
    string
    ……
    default "debug/exynos.S" ifDEBUG_EXYNOS_UART

 

configEARLY_PRINTK
    bool "Early printk"
    depends on DEBUG_LL
    help
      Say Y here if you want to have an earlyconsole using the
      kernel low-level debuggingfunctions. Addearlyprintk to your
      kernel parametersto enable this console.

从上面的信息我们可以知道:

·            在串口终端尚未注册时,内核定义了printasciiprintch以及printhex用于调试;

·            early console使用的也是上面定义的函数,需要在传递给内核的参数中添加earlyprintk参数

·            Linux内核早期的print函数的输出串口要跟u-boot下使用的一致,即内核不再负责初始化了,让u-boot来做,所以二者一定要一致,否则那些print函数以及earlyprintk都没法输出信息;

·            可以参考arch/arm/kernel/debug.Sprintasciiprintch以及printhex都是在这里定义的;

·            kernel进入C函数(start_kernel)后可以调用early_print来打印信息,它是在arch/arm/kernel/setup.c中定义的:

   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: }

可以看到,early_print也会调用printasciiprintk,意思是用early_print打印的信息可能会重复出现在终端上(printk会缓冲一部分,bootconsole注册后,会将printk缓冲区中的内容输出)。

上面所说的打印函数只能在内核自解压后的函数中才能使用,那么内核自解压过程中的信息是不是也可以打印呢?可以,内核自解压相关的文件在arch/arm/boot/compressed/下面,我们所熟知的:

Uncompressing Linux... done, booting the kernel.

就是这个目录下的代码打印出来的,具体代码如下:

arch/arm/boot/compressed/misc.c

   1: void

   2: decompress_kernel(unsigned long output_start, unsigned longfree_mem_ptr_p,

   3:         unsigned longfree_mem_ptr_end_p,

   4:         int arch_id)

   5: {

   6:     ......

   7:     putstr("Uncompressing Linux...");

   8:     ret = do_decompress(input_data,input_data_end - input_data,

   9:                 output_data, error);

  10:     ......

  11:     putstr(" done, booting the kernel.\n");

  12: }

 其中,putstr的定义如下:

   1: static void putstr(const char *ptr)

   2: {

   3:     char c;

   4:

   5:     while ((c = *ptr++) != '\0') {

   6:         if (c =='\n')

   7:             putc('\r');

   8:         putc(c);

   9:     }

  10: 

  11:     flush();

  12: }

 putc是汇编实现的,arch/arm/boot/compressed/debug.S

   1: #include CONFIG_DEBUG_LL_INCLUDE

   2: 

   3: ENTRY(putc)

   4:     addruart r1, r2, r3

   5:     waituart r3, r1

   6:     senduart r0, r1

   7:     busyuart r3, r1

   8:     mov     pc, lr

   9: ENDPROC(putc)

  10: 

  11: 

其中addruart的实现因soc的不同而不同,对于exynos4412,它的实现是(arch/arm/include/debug/exynos.S):

   1: .macro addruart, rp, rv, tmp

   2:     mrc    p15, 0, \tmp, c0, c0, 0

   3:     and    \tmp, \tmp, #0xf0

   4:     teq    \tmp, #0xf0        @@ A15

   5:     ldreq    \rp, =EXYNOS5_PA_UART

   6:     movne    \rp, #EXYNOS4_PA_UART    @@ EXYNOS4

   7:     ldr    \rv, =S3C_VA_UART

   8: CONFIG_DEBUG_S3C_UART != 0

   9:     add    \rp, \rp, #(0x10000 * CONFIG_DEBUG_S3C_UART)

  10:     add    \rv, \rv, #(0x10000 * CONFIG_DEBUG_S3C_UART)

  11: if

  12: .endm

 这个函数的目的就是获得控制调试uart的寄存器的物理基地址(rp)和虚拟基地址(rv),这里也没有初始化uart的代码,所以必须跟u-boot使用的串口一致。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值