U-boot优化是嵌入式Linux启动优化中最重要的一环

既然不能使用新的u-boot,那就优化一点是一点,慢慢干吧。
1.去掉启动时的按键等待
u-boot 启动的时候出现一个 hit any key to stop autoboot 不爽,干吗要停上1秒?虽然可以通过设置参数bootdelay=0来关掉这个延时,但这样做了以后就再也进不去u-boot了,更烦。检查代码,发现是在main.c函数int abortboot(int bootdelay)来干这个活的,好吧,改掉它
static __inline__ int abortboot(int bootdelay)
{
int abort = 0;
char inputkey;
if (tstc())
{
inputkey = getc();
abort = (inputkey == 'u');
}
#ifdef config_silent_console
if (abort)
gd->flags &= ~gd_flg_silent;
#endif
return abort;
}
这样,就不需要等待了,如果想进入u-boot,就在上电的时候按住u吧,把它改成一个固定的键而不是任意键,因为串口线很容易受到干扰,如果是任意键的话,运行时即使不想进去有时也会进入u-boot的命令行。
2.去掉网卡的初始化
每次上电,u-boot 都会初始化网卡,其实这根本不需要,把配置文件中
#define config_mii 1
去掉,启动时就不会初始化了,需要使用tftp时,它会自动初始化,又节省了3.4秒的启动时间。
3.智能读取os image
u-boot 通过nand read 来读取os image,通常为了避免麻烦,我们设置的读取长度要比实际os长度长一些,多读的那部分纯粹是浪费cpu时间,能不能精确判断读取长度呢,当然可以,为了不影响系统的正常功能,扩展一个nand read.os 指令来读取,修改方法如下:
在 nand_read_options_t 里面添加一个成员 int is_os_img
在函数 int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
修改读操作的判断语句,添加 !strcmp(s, .os),然后设置opts.is_os_img = !strcmp(s, .os);大概修改后结果参考第7步代码。
最后,在函数int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)中修改
代码,检测如果opts->is_os_img == 1 并且是第一次读取(2024b)之后,检查度取得结果是否是os image,如果是更新需要读取的长度,否则,也不需要再往下读了,直接返回错误就可以了嵌入式linux启动优化手记2 u-boot优化
image_header_t *hdr = (image_header_t *)buffer;
if (image_check_magic(hdr) && image_check_hcrc (hdr))
{
size_t ossz = uimage_to_cpu(hdr->ih_size);//+ image_get_header_size();
imglen = ossz + + image_get_header_size();
printf(## find valid os image, at 0x%x, size: %d bytes = %d kb,
(unsigned int)mtdoffset, ossz, ossz/1024);
}
else
{
printf(invalid os image at 0x%x, (unsigned int)mtdoffset);
return -1;
}
4.去掉os image 内存复制过程
使用 nand read 读取os image 后,u-boot 使用 bootm 指令来启动linux,检查其实现代码
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
发现他会把已经读取到内存中的os image 在复制到一个指定的位置,os image 中的头部参数,这个值一般是固定的,本系统使用的是 0x70008000, 如果在 nand read 时读到的内存位置恰好合适,就可以省掉这些毫秒数了,做法如下:
nand read.os 0x70007fc0 0x100000 0x500000
(其中 0x70007fc0 = 0x70008000 - sizeof(sizeof(image_header_t)))
然后在内存复制的地方(函数do_bootm中),加入修改,跳过内存复制
switch (comp) {
case ih_comp_none:
if (load_start == (ulong)os_hdr) {
printf ( xip %s ... , type_name);
} else {
if (load_start != os_data)//位置不匹配,依然移动,否则就跳过此部
{
printf ( loading %s ... , type_name);
memmove_wd ((void *)load_start, (void *)os_data, os_len, chunksz);
puts(ok);
}
}
load_end = load_start + os_len;
对于我们的kernel,修改后大小是1.4m,省去这个搬移过程,节省了大约800ms的时间
5.减少内存初始化的时间
在u-boot 初始化时,在 start_armboot 函数中,多次使用到了 memset函数,其中最耗时的是在mem_malloc_init函数中调用memset 初始化 512k内存的调用,检查u-boot 1.3.4对memset的实现,发现是最简单的字节复制,把它改为按32bits复制的方式,这些memset 调用所花费的时间立即从202ms减少到了45ms
修改方法,再 string.c 中,找到memset函数,修改其实现(代码是从u-boot 2011.12 中复制过来的嵌入式linux启动优化手记2 u-boot优化)
void * memset(void * s,int c,size_t count)
{
unsigned long *sl = (unsigned long *) s;
unsigned long cl = 0;
char *s8;
int i;
if ( ((ulong)s & (sizeof(*sl) - 1)) == 0) {
for (i = 0; i < sizeof(*sl); i++) {
cl scan_bbt (mtd);最花费时间,这个scan_bbt扫描整个nand并检查坏块,重建坏块表,在启动过程中,这个耗时的操作毫无意义,去掉这一步,让nand_scan 函数直接返回0就可以了。
7.添加yaffs2支持
从网上各位前辈的论述中,都发现yaffs比jffs2要快,也决定测试一下,从yaffs网站下载最新的代码,按照说明加入到linux 中,重新编译内核,让内核支持yaffs2(按照默认的选项就可以了),弄一个空的分区,格式化成yaffs2格式,感觉的确比较快,把rootfs复制到这个分区,然后修改linux启动参数让它把yaffs2分区当作根分区启动,发现果然快了不少,初始化和挂载根分区仅需要370ms,比jffs2的速度快多了,决定就采用yaffs2作为根文件系统了。自己在u-boot中添加对yaffs2 image的支持
说起来容易,真正做起来还是很麻烦的,总是不能把yaffs2的image 烧写成功,不知道是image不正确还是uboot没改对,折腾了几天也没搞定,最后终于发现了一个第三方的工具
http://code.google.com/p/yaffs2utils/
下载,编译,制作image,验证,ok,把新工具生成的image与yaffs2自带的工具对照,发现yaffs2自带的工具生成的image不正确,晕死。
重新修改uboot,改了很少一部份代码,就可以了。
依然是在函数do_nand中修改,添加一个扩展 nand write.y 指令来写入image:
按照惯例,yaffs2的第一个块不使用,留给文件系统自己使用,在 nand_write_options_t 里面添加一个成员 int skip_first_block;
在函数 int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
修改读写操作的判断语句,添加 !strcmp(s, .y),然后设置opts.is_os_img = !strcmp(s, .os);大概修改后结果如下(红色部分)
s = strchr(cmd, '.');
if (s != null && (!strcmp(s, .jffs2) || !strcmp(s, .e) || !strcmp(s, .i) || !strcmp(s, .os) || !strcmp(s, .y))) {
if (read) {
nand_read_options_t opts;
memset(&opts, 0, sizeof(opts));
opts.buffer = (u_char*) addr;
opts.length = size;
opts.offset = off;
opts.readoob = 0;//remove this function.
opts.is_os_img = !strcmp(s, .os);
opts.quiet = quiet;
ret = nand_read_opts(nand, &opts);
//printf(call nand_read_opts buffer %lu len %lu offset %d off, ret %d, addr, size, off, ret);
} else {
nand_write_options_t opts;
memset(&opts, 0, sizeof(opts));
opts.buffer = (u_char*) addr;
opts.length = size;
opts.offset = off;
if (!strcmp(s, .y))
{
opts.pad = 0;
opts.writeoob = 1;
//opts.noecc = 1;
opts.skip_first_block = 1;
opts.autoplace = 1;
}
else
{
opts.pad = 1;
}
opts.blockalign = 1;
opts.quiet = quiet;
ret = nand_write_opts(nand, &opts);
}
} else if (s != null && !strcmp(s, .oob)) {...}
在函数nand_write_opts中相应修改
int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
{
int yaffs_skip_first = opts->skip_first_block;
...
while ((imglen > 0) && (mtdoffset size)) {
...
while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
do {
...
} while (offs oobblock;
if (opts->pad && (imglen < readlen))
...
}
...
}
8. 其它一些优化措施
经过这些折腾之后,整个系统的启动时间大大加快,然后优化linux自身的一些启动瓶颈
linux的启动参数优化:加上了 lpj=99072,节约了几十个毫秒,加上quiet,节约了大约1秒时间
修改内核编译选项,把不需要的内核模块干掉
最后,linux自身的启动速度约为1.1秒,整个系统的启动速度大约4秒多一点,初步达到了优化目标,系统的主要延时发生在u-boot 1.3.4的flash读取上,flash读取速度大约只有600kb/s。尝试把uboot 2011.12的flash驱动移植到u-boot 1.3.4上,花费了几天时间,终于可以编译成功了,可惜经常出一些莫名其妙的错误,太不稳定,只好放弃。
以我的能力,u-boot优化到这里就到头了,正准备结束工作时,发现了另外一条可以加速系统启动的方法
可以继续尝试,让我最终把系统的启动时间减少到了1.7秒。
请看下篇嵌入式linux启动优化手记2 u-boot优化

Prometheus PCB划线 3D打印机的机器人
彩云科技与海康威视签署战略合作协议 实现双方互惠互利发展
整流二极管品牌 正向电流1A 型号齐全
诺基亚+苹果 山寨机中的战斗机
骁龙 750G基于 8nm 工艺,支持3200 万像素 + 1600 万像素双模组?
U-boot优化是嵌入式Linux启动优化中最重要的一环
伺服输出电抗器的特点及规格型号
OPPOR17夜拍效果到底怎么样
工业机器人的视觉系统起着什么样的作用
AI安全和隐私的忧虑有哪些
企业微信升级:多次触发敏感词将被自动踢出群聊
全志h618和s905哪个好
FM解调器
一文教大家如何采用GaN实现48V至POL单级转换
创意led显示屏有什么用
城会玩:小米电视4A还能这么玩,厉害了小米家
为什么良好的PCB布局布线非常重要?
你不知道吧!骁龙820 不止性能强悍 连接技术也很牛
超大规模的技术革命,从半导体工业开始
芯片功耗的Power弱连接分析(1)