printk与 uart console关系分析(草稿)

2024-03-01 14:32

本文主要是介绍printk与 uart console关系分析(草稿),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1,了解kernel启动过程

asmlinkage void __init start_kernel(void)
{...parse_args("Booting kernel", static_command_line, __start___param,__stop___param - __start___param,-1, -1, &unknown_bootoption);...console_init();...rest_init();
}

了解下console_setup, console_init, uart_add_one_port.

console_setup如何调用而来:

在kernel/printk.c中console_setup代码:

static int __init console_setup(char *str)
{char buf[sizeof(console_cmdline[0].name) + 4]; /* 4 for index */char *s, *options, *brl_options = NULL;int idx;#ifdef CONFIG_A11Y_BRAILLE_CONSOLEif (!memcmp(str, "brl,", 4)) {brl_options = "";str += 4;} else if (!memcmp(str, "brl=", 4)) {brl_options = str + 4;str = strchr(brl_options, ',');if (!str) {printk(KERN_ERR "need port name after brl=\n");return 1;}*(str++) = 0;}
#endif/** Decode str into name, index, options.*/if (str[0] >= '0' && str[0] <= '9') {strcpy(buf, "ttyS");strncpy(buf + 4, str, sizeof(buf) - 5);} else {strncpy(buf, str, sizeof(buf) - 1);}buf[sizeof(buf) - 1] = 0;if ((options = strchr(str, ',')) != NULL)*(options++) = 0;//记录option,例如1152008n
#ifdef __sparc__if (!strcmp(str, "ttya"))strcpy(buf, "ttyS0");if (!strcmp(str, "ttyb"))strcpy(buf, "ttyS1");
#endiffor (s = buf; *s; s++)if ((*s >= '0' && *s <= '9') || *s == ',')break;idx = simple_strtoul(s, NULL, 10);//得到console=ttySxxx中串口号, 例如0,1,2...*s = 0;//使得buf内容不再包含串口号。__add_preferred_console(buf, idx, options, brl_options);//完成对console_cmdline数组的填充console_set_on_cmdline = 1;return 1;
}
__setup("console=", console_setup);

__add_preferred_console函数:

static int __add_preferred_console(char *name, int idx, char *options,char *brl_options)
{struct console_cmdline *c;int i;/**      See if this tty is not yet registered, and*      if we have a slot free.*/for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)if (strcmp(console_cmdline[i].name, name) == 0 &&console_cmdline[i].index == idx) {if (!brl_options)selected_console = i;return 0;}if (i == MAX_CMDLINECONSOLES)return -E2BIG;if (!brl_options)selected_console = i;//记录selected_console的值>=0c = &console_cmdline[i];strlcpy(c->name, name, sizeof(c->name));//记录串口名字c->options = options;//记录串口设置参数
#ifdef CONFIG_A11Y_BRAILLE_CONSOLEc->brl_options = brl_options;
#endif c->index = idx;//记录作为串口的index号,在console_setup中从console=ttySxxx中得到。return 0;
}


__setup跟踪, 在include/linux/init.h

#define __setup_param(str, unique_id, fn, early)                        \static const char __setup_str_##unique_id[] __initconst \__aligned(1) = str; \static struct obs_kernel_param __setup_##unique_id      \__used __section(.init.setup)                   \__attribute__((aligned((sizeof(long)))))        \= { __setup_str_##unique_id, fn, early }#define __setup(str, fn)                                        \__setup_param(str, fn, fn, 0)
知道该类函数定义在*.init.setup区。并由start_kernel一直调用到console_setup.

console_init()函数定义在drivers/tty/tty_io.c,主要完成对console_initcall 声明函数的调用:

void __init console_init(void)
{initcall_t *call;/* Setup the default TTY line discipline. */tty_ldisc_begin();/** set up the console device so that later boot sequences can* inform about problems etc..*/call = __con_initcall_start;while (call < __con_initcall_end) {(*call)();call++;//依次调用console_initcall声明的函数,like:console_initcall(sirfsoc_uart_console_init);}
}
在sirfsoc_uart_console_init中:

static int __init sirfsoc_uart_console_init(void)
{register_console(&sirfsoc_uart_console);return 0;
}
console_initcall(sirfsoc_uart_console_init);
下面对register_console函数进行分析:

void register_console(struct console *newcon)
{int i;unsigned long flags;struct console *bcon = NULL;/** before we register a new CON_BOOT console, make sure we don't* already have a valid console*/if (console_drivers && newcon->flags & CON_BOOT) {//boot console 如果开启了early_printk hacking,可以跟踪分析arch/arm/kernel/early_printk.c/* find the last or real console */for_each_console(bcon) {if (!(bcon->flags & CON_BOOT)) {printk(KERN_INFO "Too late to register bootconsole %s%d\n",newcon->name, newcon->index);return;}}}if (console_drivers && console_drivers->flags & CON_BOOT)//boot consolebcon = console_drivers;if (preferred_console < 0 || bcon || !console_drivers)preferred_console = selected_console;//对于real console preferred_console >=0if (newcon->early_setup)newcon->early_setup();/**      See if we want to use this console driver. If we*      didn't select a console we take the first one*      that registers here.*/if (preferred_console < 0) {if (newcon->index < 0)newcon->index = 0;if (newcon->setup == NULL ||newcon->setup(newcon, NULL) == 0) {newcon->flags |= CON_ENABLED;if (newcon->device) {newcon->flags |= CON_CONSDEV;preferred_console = 0;}}}/**      See if this console matches one we selected on*      the command line.*/for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];i++) {if (strcmp(console_cmdline[i].name, newcon->name) != 0)continue;if (newcon->index >= 0 &&newcon->index != console_cmdline[i].index)continue;if (newcon->index < 0)newcon->index = console_cmdline[i].index;//对于console_initcall类型函数调用的register_console, 对console的index
//附上console=ttySxxx传过来的串口号。
#ifdef CONFIG_A11Y_BRAILLE_CONSOLEif (console_cmdline[i].brl_options) {newcon->flags |= CON_BRL;braille_register_console(newcon,console_cmdline[i].index,console_cmdline[i].options,console_cmdline[i].brl_options);return;}
#endifif (newcon->setup &&newcon->setup(newcon, console_cmdline[i].options) != 0)break;//第一次register_cosole不能成功从这里退出,因为port->mapbase == 0, mapbase 没有在probe中被设置。newcon->flags |= CON_ENABLED;//如果setup成功设置CON_ENABLED标记。newcon->index = console_cmdline[i].index;if (i == selected_console) {newcon->flags |= CON_CONSDEV;preferred_console = selected_console;}break;}if (!(newcon->flags & CON_ENABLED))return;/** If we have a bootconsole, and are switching to a real console,* don't print everything out again, since when the boot console, and* the real console are the same physical device, it's annoying to* see the beginning boot messages twice*/if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))newcon->flags &= ~CON_PRINTBUFFER;//如注释所示,避免了real-console再次打印log信息/**      Put this console in the list - keep the*      preferred driver at the head of the list.*/console_lock();if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {newcon->next = console_drivers;console_drivers = newcon;if (newcon->next)newcon->next->flags &= ~CON_CONSDEV;} else {newcon->next = console_drivers->next;console_drivers->next = newcon;}if (newcon->flags & CON_PRINTBUFFER) {/** console_unlock(); will print out the buffered messages* for us.*/raw_spin_lock_irqsave(&logbuf_lock, flags);console_seq = syslog_seq;console_idx = syslog_idx;console_prev = syslog_prev;raw_spin_unlock_irqrestore(&logbuf_lock, flags);/** We're about to replay the log buffer.  Only do this to the* just-registered console to avoid excessive message spam to* the already-registered consoles.*/exclusive_console = newcon;}console_unlock();console_sysfs_notify();/** By unregistering the bootconsoles after we enable the real console* we get the "console xxx enabled" message on all the consoles -* boot consoles, real consoles, etc - this is to ensure that end* users know there might be something in the kernel's log buffer that* went to the bootconsole (that they do not see on the real console)*/if (bcon &&((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&!keep_bootcon) {/* we need to iterate through twice, to make sure we print* everything out, before we unregister the console(s)*/printk(KERN_INFO "console [%s%d] enabled, bootconsole disabled\n",newcon->name, newcon->index);//开启了early_printkfor_each_console(bcon)if (bcon->flags & CON_BOOT)unregister_console(bcon);} else {printk(KERN_INFO "%sconsole [%s%d] enabled\n",(newcon->flags & CON_BOOT) ? "boot" : "" ,newcon->name, newcon->index);//未开启early_printk}
}
EXPORT_SYMBOL(register_console);

对probe函数中uart_add_one_port的分析:



1, 名词解析:

console_sem:

/** console_sem protects the console_drivers list, and also* provides serialisation for access to the entire console* driver system.*/

console_drivers:

static DEFINE_SEMAPHORE(console_sem);
struct console *console_drivers;
EXPORT_SYMBOL_GPL(console_drivers);

在这里将console_drivers设置成export_symbol的原因是在include/linux/console.h中定义了一个宏:

#define for_each_console(con) \for (con = console_drivers; con != NULL; con = con->next)

用来便利console_drivers的列表。

2,函数解析

register_console:

arch/arm/kernel/early_printk.c:

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

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

在boot/uboot/include/configs/atlas6cb.h中设置bootargs:

"devargs=" \"setenv bootargs no_console_suspend retain_initrd " \"earlyprintk resumewait " \"mem=${meminfo} " \"console=ttySiRF3 " \"lpj=7995392 " \"real_root=/dev/${bootinf}blk${bootdev}p${rootpart} " \"resume=/dev/${bootinf}blk${bootdev}p${resumepart} "\"scert_start=0x3800 " \"scert_size=0x1000\0" \

与pr_notice("Kernel command line: %s\n", boot_command_line);

3, 解压过程中log 打印“Uncompressing Linux... done, booting the kernel.”

make kmenuconfig->Kernel Hacking->Kernel low-level debugging functions->Platform ownself debug console.

由于选择了自己平台的console口,所以在选中CONFIG_DEBUG_LL的同时就没有选中Kernel low-level debug output via semihosting I/O,这就是CONFIG_DEBUG_SEMIHOSTING选项,开启DEBUG_LL只能选择一个debug口,因而CONFIG_DEBUG_SEMIHOSTING没有打开。代开arch/arm/Kconfig.debug文件:

choiceprompt "Kernel low-level debugging port"depends on DEBUG_LLconfig DEBUG_SEMIHOSTINGbool "Kernel low-level debug output via semihosting I/O"helpSemihosting enables code running on an ARM target to usethe I/O facilities on a host debugger/emulator through asimple SVC call. The host debugger or emulator must havesemihosting enabled for the special svc call to be trappedotherwise the kernel will crash.This is known to work with OpenOCD, as well asARM's Fast Models, or any other controlling environmentthat implements semihosting.For more details about semihosting, please seechapter 8 of DUI0203I_rvct_developer_guide.pdf from ARM Ltd.endchoice

config中的choice选项只能选择一种。

接着打开arch/arm/kernel/debug.S

#if !defined(CONFIG_DEBUG_SEMIHOSTING)
#include CONFIG_DEBUG_LL_INCLUDE
#endif

第一句就是要包含DEBUG_LL_INCLUDE config选项:即arch/arm/Kconfig.debug文件中:

config DEBUG_LL_INCLUDEstringdefault "debug/bcm2835.S" if DEBUG_BCM2835default "debug/cns3xxx.S" if DEBUG_CNS3XXXdefault "debug/exynos.S" if DEBUG_EXYNOS_UARTdefault "debug/highbank.S" if DEBUG_HIGHBANK_UARTdefault "debug/icedcc.S" if DEBUG_ICEDCCdefault "debug/imx.S" if DEBUG_IMX1_UART || \DEBUG_IMX25_UART || \DEBUG_IMX21_IMX27_UART || \DEBUG_IMX31_UART || \DEBUG_IMX35_UART || \DEBUG_IMX51_UART || \DEBUG_IMX53_UART ||\DEBUG_IMX6Q_UARTdefault "debug/mvebu.S" if DEBUG_MVEBU_UARTdefault "debug/mxs.S" if DEBUG_IMX23_UART || DEBUG_IMX28_UARTdefault "debug/nomadik.S" if DEBUG_NOMADIK_UARTdefault "debug/omap2plus.S" if DEBUG_OMAP2PLUS_UARTdefault "debug/picoxcell.S" if DEBUG_PICOXCELL_UARTdefault "debug/pxa.S" if DEBUG_PXA_UART1 || DEBUG_MMP_UART2 || \DEBUG_MMP_UART3default "debug/sirf.S" if DEBUG_SIRFPRIMA2_UART1 || DEBUG_SIRFMARCO_UART1default "debug/socfpga.S" if DEBUG_SOCFPGA_UARTdefault "debug/sunxi.S" if DEBUG_SUNXI_UART0 || DEBUG_SUNXI_UART1default "debug/tegra.S" if DEBUG_TEGRA_UARTdefault "debug/ux500.S" if DEBUG_UX500_UARTdefault "debug/vexpress.S" if DEBUG_VEXPRESS_UART0_DETECT || \DEBUG_VEXPRESS_UART0_CA9 || DEBUG_VEXPRESS_UART0_RS1default "debug/vt8500.S" if DEBUG_VT8500_UART0default "debug/zynq.S" if DEBUG_ZYNQ_UART0 || DEBUG_ZYNQ_UART1default "mach/debug-macro.S"

包含板子内部所带的debug文件。

同时在arch/arm/kernel/debug.S中:

#ifndef CONFIG_DEBUG_SEMIHOSTINGENTRY(printascii)addruart_current r3, r1, r2b       2f
1:              waituart r2, r3senduart r1, r3busyuart r2, r3teq     r1, #'\n'moveq   r1, #'\r'beq     1b
2:              teq     r0, #0ldrneb  r1, [r0], #1teqne   r1, #0bne     1bmov     pc, lr
ENDPROC(printascii)ENTRY(printch)addruart_current r3, r1, r2mov     r1, r0mov     r0, #0b       1b
ENDPROC(printch)

定义了printascii 和 printch函数,并调用了waituart,senduart,busyuart等函数。这些函数就是DEBUG_LL_INCLUDE中要包含的板子调试信息中所定义,如:

在arch/arm/include/debug/sirf.S

#if defined(CONFIG_DEBUG_SIRFPRIMA2_UART1)
#define SIRFSOC_UART1_PA_BASE          0xb0060000
#elif defined(CONFIG_DEBUG_SIRFMARCO_UART1)
#define SIRFSOC_UART1_PA_BASE          0xcc060000
#else
#define SIRFSOC_UART1_PA_BASE          0
#endif#define SIRFSOC_UART1_VA_BASE           0xFEC60000#define SIRFSOC_UART_TXFIFO_STATUS      0x0114
#define SIRFSOC_UART_TXFIFO_DATA        0x0118#define SIRFSOC_UART1_TXFIFO_FULL                       (1 << 5)
#define SIRFSOC_UART1_TXFIFO_EMPTY                      (1 << 6).macro  addruart, rp, rv, tmpldr     \rp, =SIRFSOC_UART1_PA_BASE             @ physicalldr     \rv, =SIRFSOC_UART1_VA_BASE             @ virtual.endm.macro  senduart,rd,rxstr     \rd, [\rx, #SIRFSOC_UART_TXFIFO_DATA].endm.macro  busyuart,rd,rx.endm.macro  waituart,rd,rx
1001:   ldr     \rd, [\rx, #SIRFSOC_UART_TXFIFO_STATUS]tst     \rd, #SIRFSOC_UART1_TXFIFO_EMPTYbeq     1001b.endm

定义了寄存器的地址,以及上述*uart的含义,直接想硬件寄存器放字符去打印出来。这就是lowlevel打印的函数形成的过程。

putc形成过程:

arch/arm/Kconfig.debug文件:

config DEBUG_UNCOMPRESSbooldefault y if ARCH_MULTIPLATFORM && DEBUG_LL && \!DEBUG_OMAP2PLUS_UART && \!DEBUG_TEGRA_UART

在打开DEBUG_LL后,默认应该打开了DEBUG_UNCOMPRESS config 选项。在arch/arm/boot/compressd/Makefile文件中:

ifeq ($(CONFIG_DEBUG_UNCOMPRESS),y)
OBJS    += debug.o

即编译arch/arm/boot/compressed/debug.S文件。该文件对putc函数进行了定义同样是使用arch/arm/include/debug/sirf.S中定义的*uart 函数。

在arch/arm/include/debug/uncompress.h文件中包含putc()函数的定义

#ifdef CONFIG_DEBUG_UNCOMPRESS
extern void putc(int c);
#else
static inline void putc(int c) {}
#endif

因而在真正的uart console启动起来之前,putc和printascii均调用的是直接想串口丢数据到txfifo,数据打印出来。

在sirf平台默认的就是uart1口。


4, console_setup:

__setup("console=", console_setup);
/** Decode str into name, index, options.
*/
__add_preferred_console(buf, idx, options, brl_options);
console_set_on_cmdline = 1; 




这篇关于printk与 uart console关系分析(草稿)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/762774

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

POJ1269 判断2条直线的位置关系

题目大意:给两个点能够确定一条直线,题目给出两条直线(由4个点确定),要求判断出这两条直线的关系:平行,同线,相交。如果相交还要求出交点坐标。 解题思路: 先判断两条直线p1p2, q1q2是否共线, 如果不是,再判断 直线 是否平行, 如果还不是, 则两直线相交。  判断共线:  p1p2q1 共线 且 p1p2q2 共线 ,共线用叉乘为 0  来判断,  判断 平行:  p1p

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除