打开Linux内核启动早期的log

本文介绍如何在Linux内核启动早期阶段打开日志打印功能,包括配置内核选项、添加earlyprintk参数及使用特定宏进行调试。探讨了在内核自解压过程中的信息打印方法。

打开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 debuggingfunctionsAddearlyprintk to your
      kernel parametersto enable this console.

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

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

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

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

·            可以参考arch/arm/kernel/debug.S,printascii、printch以及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也会调用printascii和printk,意思是用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使用的串口一致。

<think> 首先,用户的问题是关于在Linux系统中配置内核log输出串口波特率的方法。用户引用了几个来源,这些来源提供了相关信息。 关键引用: - 引用[1]:讨论了修改uboot和kernel的串口波特率,具体提到修改波特率的方法。 - 引用[2]:涉及更改嵌入式Linux的调试串口,包括uboot和kernel- 引用[3]:展示了如何在C#中设置串口波特率,但这可能不直接相关,因为用户的问题是Linux系统。 - 引用[4]:描述了在Linux虚拟机中添加串口打印开机log,通过修改grub和printk设置。 - 引用[5]:解释了printk的log级别,但没有直接提到波特率设置。 用户的主要需求是:配置内核log输出串口波特率的方法。 回顾用户的问题:"我想查找在Linux系统中配置内核log输出串口波特率的方法"。 所以,我需要提供一个结构清晰的解决方案,基于引用和一般知识。 步骤: 1. **理解Linux内核log输出**:内核log通过串口输出通常涉及uboot阶段和内核阶段的配置。 2. **修改波特率的关键点**: - 在uboot阶段:需要修改uboot的配置或源代码来设置串口波特率。 -内核阶段:需要在内核配置中设置console参数。 3. **具体方法**: - 从引用[1]和[2]:修改uboot和kernel的串口波特率可能需要重新编译。 - 从引用[4]:通过修改grub配置文件添加console参数可以设置串口波特率。 - 常见方法是修改kernel命令行参数,指定console和波特率。 标准方法: -Linux内核中,串口console的波特率通常在kernel命令行中指定。例如,使用`console=ttyS0,9600`来指定串口设备和波特率。 - 对于嵌入式系统,可能需要在内核配置中设置或重新编译。 - 对于printk的log级别,引用[5]提到了log级别,但没有波特率;波特率是硬件通信参数。 用户提到的"内核log输出串口波特率",指的是设置串口设备用于console输出的波特率。 基于引用[4]:它提到修改grub添加`earlyprintk=ttyS0 console=ttyS0`,但没有指定波特率。波特率通常在console参数中指定,如`console=ttyS0,115200n8`。 完整命令示例:`console=ttyS0,115200n8`,其中115200是波特率,n表示无奇偶校验,8表示数据位。 步骤概括: 1. **修改GRUB配置**:编辑`/boot/grub/grub.cfg`或相关文件(如`/etc/default/grub`),在kernel命令行添加console参数。 2. **重新生成GRUB配置**:运行`sudo update-grub`应用更改。 3. **检查printk级别**(可选):如果需要调整日志级别,参考引用[5],使用`echo 7 7 7 7 > /proc/sys/kernel/printk`。 4. **对于嵌入式系统**:可能需要修改uboot环境变量和内核设备树。 从引用[1]:修改uboot和kernel的串口波特率需要重新编译镜像。 在回答中,我需要: - 使用中文。 - 结构清晰:分步解释。 - 引用来源:在末尾添加引用标识,如[^1]。 - 生成相关问题:在最后添加相关问题。 回答结构: 1. 介绍问题。 2. 详细步骤: - 修改GRUB配置。 - 设置console参数。 - 其他注意事项。 3. 代码块(如果需要):如果有命令示例。 4. 相关问题。 确保LaTeX语法正确:用户提到行内数学表达式用$...$,但由于主题是系统配置,可能不需要数学表达式,除非涉及计算。 在回答中,可能提到波特率数值,但不需要LaTeX。 生成相关问题: - 例如: 1. 如何修改uboot的串口波特率? 2. 如何在嵌入式Linux中更改调试串口? 3. 如何调整内核日志级别? 引用标记: - 在相关段落末尾添加,如[^1]。 最终回答: </think> ### 在Linux系统中配置内核log输出串口波特率的方法 在Linux系统中,配置内核log输出串口的波特率主要涉及修改内核命令行参数(kernel command-line parameters),这通常在启动加载器(如GRUB)中进行设置。核心是通过指定`console`参数来配置串口设备和波特率,例如`console=ttyS0,115200n8`,其中`115200`是波特率(常见值如9600、115200等),`n8`表示无奇偶校验和8位数据位。以下是详细的配置步骤,基于引用提供的上下文(如uboot、kernel和grub配置)进行整理。配置方法分为通用Linux系统和嵌入式Linux系统两种情况。 #### 通用Linux系统(如桌面或虚拟机环境) 对于标准Linux发行版(如Ubuntu、CentOS),配置主要通过GRUB引导加载器实现: 1. **修改GRUB配置文件** 编辑GRUB的配置文件以添加`console`参数。首先打开文件: ```bash sudo vim /etc/default/grub ``` 在`GRUB_CMDLINE_LINUX_DEFAULT`或`GRUB_CMDLINE_LINUX`行中添加`console`参数。例如,设置波特率为115200(引用[4]): ``` GRUB_CMDLINE_LINUX_DEFAULT="quiet splash earlyprintk=ttyS0 console=ttyS0,115200n8" ``` - `earlyprintk=ttyS0`:确保内核启动早期日志输出到串口(ttyS0)。 - `console=ttyS0,115200n8`:指定串口设备为ttyS0、波特率115200(n表示无奇偶校验,8表示数据位)。 - 如果使用其他波特率(如9600),修改为`console=ttyS0,9600n8`。 2. **更新GRUB配置并重启** 保存文件后,重新生成GRUB配置文件: ```bash sudo update-grub ``` 重启系统以应用更改: ```bash sudo reboot ``` 3. **验证日志输出** 系统重启后,连接串口调试工具(如minicom或screen),设置为匹配的波特率(如115200)。检查日志: ```bash sudo dmesg | grep ttyS0 # 确认串口设备加载日志 ``` 如果需要调整内核日志级别(如确保所有级别日志输出),参考引用[5],修改printk级别: ```bash echo 7 7 7 7 > /proc/sys/kernel/printk # 将所有日志级别设为DEBUG(最高优先级)[^5] ``` #### 嵌入式Linux系统(如Banana Pi等ARM设备) 在嵌入式系统中,配置通常涉及uboot和kernel的源码修改,并重新编译镜像(引用[1][^1]和[2][^2]): 1. **修改uboot波特率** - 编辑uboot源码中的串口配置文件(通常位于`drivers/serial/serial.c`或类似路径)。 - 查找`CONFIG_BAUDRATE`参数并修改波特率值,例如: ```c #define CONFIG_BAUDRATE 115200 // 改为目标波特率,如9600 ``` - 重新编译uboot并刷入设备(参考引用[1][^1])。 2. **修改内核命令行参数** -内核源码中,编辑设备树文件(`*.dts`)或内核配置文件(`.config`)。 - 在设备树中添加或修改`chosen`节点: ``` chosen { bootargs = "console=ttyS0,115200n8 earlyprintk=ttyS0"; }; ``` - 或在`.config`中设置: ``` CONFIG_CONSOLE_UART_BAUDRATE=115200 # 波特率值 ``` - 重新编译内核,并将新镜像刷入设备(引用[2][^2])。 3. **设置开机日志** 对于嵌入式设备,还需确保uboot环境变量包含正确参数(引用[1][^1]): ```bash setenv bootargs console=ttyS0,115200n8 # 在uboot命令行中设置 saveenv ``` #### 注意事项 - **波特率兼容性**:串口硬件必须支持目标波特率(常见值为9600、19200、115200)。否则,日志可能乱码或无输出。 - **虚拟机环境**:在QEMU或VMware中,需先添加虚拟串口设备,再通过GRUB配置(引用[4][^4])。 - **日志级别**:如果日志输出不完整,调整printk级别(引用[5][^5])可确保所有消息可见。 - **工具依赖**:使用串口调试工具(如Cutecom或Picocom)时,确保其波特率与系统设置一致(引用[3][^3]提供C#示例,但Linux下可用类似原理)。 通过以上步骤,您可以在Linux系统中成功配置内核log输出的串口波特率。如果配置后日志无输出,检查串口连接和硬件是否正常[^1][^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值