如何优化U-boot到内核切换代码?

平台:Xilinx Zynq SoC上的ARM Cortex A9上的Linux。

我问了一个问题: 为什么内核启动太晚了

基本上我试图理解,然后最小化这两个事件之间的延迟:

[Sat Apr 12 19:33:50.692 2014] Starting kernel ... [Sat Apr 12 19:33:50.692 2014] [Sat Apr 12 19:33:51.298 2014] Booting Linux on physical CPU 0x0 

第一行告诉控件现在正在给内核,而第二行告诉控件现在是内核并且正在由CPU执行。

从u-boot到内核的这种切换对我们的应用程序来说花费了太多时间。

为了理解这两个事件之间发生了什么,我在下面插入了printf语句:

1- bootm.c

我把以下行放在函数static void boot_jump_linux(bootm_headers_t *images, int flag)

  } if (!fake) {printf("above kernel_entry in boot_jump_linux in bootm.c\n"); kernel_entry(0, machid, r2); printf("below kernel_entry boot_jump_linux in bootm.c\n"); } } 

2- main.c

我把这样的声明放在start_kernel函数中:

 asmlinkage void __init start_kernel(void) { printk("I am the first statement in start_kernel in main.c" ); char * command_line; extern const struct kernel_param __start___param[], __stop___param[]; 

然后我编译了u-boot和内核,新的日志消息包含以下行:

 [Sat Apr 12 19:33:50.692 2014] Starting kernel ... [Sat Apr 12 19:33:50.692 2014] above kernel_entry in boot_jump_linux in bootm.c [Sat Apr 12 19:33:51.298 2014] I am the first statement in start_kernel in main.c [Sat Apr 12 19:33:51.298 2014] Booting Linux on physical CPU 0x0 

(实际上我把printf语句放在很多地方但是所有的thsoe都在“启动内核……”或“在物理CPU 0x0上启动Linux”之下,所以我在这个讨论中忽略它。我也用ftrace来看热点,但它没有报告u-bootfunction)。

我观察到“bootm.c中boot_jump_linux中的kernel_entry下面”永远不会打印在日志消息中的任何地方。 这表明在函数kernel_entry(0,machid,r2)之后控件不会返回; 之所以被调用,是因为linux现在已经控制并正在执行。

所以我的目标是知道在这两个事件中正在执行哪个function。

现在要了解发生了什么(即使在插入我的printf / printk消息后仍未清楚)我问了以下问题:

1- 在u-boot中,kernel_entry指向哪个函数?

2- 试图理解函数指针的用法

基于那里的答案,我怀疑我的热点,即花费很多时间的代码位于以下文件之一:

1- https://github.com/Xilinx/linux-xlnx/blob/master/arch/arm/kernel/head.S

2- https://github.com/Xilinx/linux-xlnx/blob/master/arch/arm/kernel/head-common.S

3- https://github.com/Xilinx/linux-xlnx/blob/master/arch/arm/boot/compressed/head.S

我的问题:

1-我的理解是否正确,我应该专注于上述文件?

2-调用kernel_entry(0, machid, r2); 是的,控件转到上面的代码和哪一行?

我怀疑文件https://github.com/Xilinx/linux-xlnx/blob/master/arch/arm/boot/compressed/head.S对我没用,因为这是解压缩所必需的,但是我的内核已解压缩,因为在u-boot日志中可以很早地看到以下行:

 [Sat Apr 12 19:33:50.596 2014] Uncompressing Kernel Image ... OK 

完整的日志在这里 。

有人可以在这方面让我高兴吗?

提前谢谢了!!

我的目标是尽可能快地进行切换。
你认为使用早期的printk我可以更快地启动,因为切换会提前吗?

您的问题以及计算“快速”或“延迟”的方法都是基于有缺陷的数据。

您认为0.5秒“延迟”实际上是U-Boot 实时输出“Starting kernel …” ,而内核缓冲推迟输出其“Booting Linux”直到系统和控制台初始化。 这是将苹果与橙子进行比较。
至少你必须通过启用早期的printk实时输出内核(就像U-Boot一样)。 然后您的时间戳将更好地指示实际经过的时间。

Linux内核映射第18章的摘录(由我强调):

在printk()的坚固性的盔甲中存在一个缝隙。 在控制台初始化之前,它在内核引导过程中的某个点之前是不可用的。 实际上,如果控制台没有初始化,输出应该在哪里?

这通常不是问题,除非您在引导过程中很早就调试问题(例如,在setup_arch()中执行特定于体系结构的初始化)。 这样的调试是一个挑战,并且没有任何类型的打印方法只会使问题复杂化。

有一些希望,但不是很多。 硬核架构黑客使用可以工作的硬件(比如串口)与外界进行通信。 相信我这对大多数人来说并不好玩。 一些受支持的架构确实实现了一个理智的解决方案,然而其他(包括i386)有可用的补丁,也节省了一天。

解决方案是一个printk()变体,可以在引导过程的早期输出到控制台:early_printk()。 行为与printk()相同,只更改名称及其先前工作的能力。 然而,这不是可移植的解决方案,因为并非所有支持的体系结构都实现了这样的方法。 它可能会成为你最好的朋友,如果它确实如此。

有关启用早期printk的详细信息,请参阅相同问题的答案 (由您的双胞胎?)。

所以不,使用早期的printk不会改善“切换”或整体启动时间。
但它应该有助于防止你寻找幻像障碍。

我想知道怎么发生。

您正在通过指向其他函数的函数指针显示直接函数调用。

我没有看到除了中断代码之外的任何东西都会干扰它。

所花费的时间是600毫秒,这并不是很多,但当然在很多情况下都很明显(特别是在Zynq相关的地方)。

要调查的事情:

  • 你可以单步执行函数指针跳转吗? 这将让你看到PC最终的位置。
  • 你可以关闭中断,只是为了确保没有任何虚假的事情发生吗?
  • 你可以记录日志记录所需的时间吗? 如果它是通过串口,它会阻塞吗? 打印“ [Sat Apr 12 19:33:51.298 2014] I am the first statement in start_kernel in main.c ”(82字节)到9600 bps的阻塞串口大约需要83 ms。