openwrt u-boot_mod 代码分析
u-boot_mod 是具有web 浏览器的uboot,也就是传说中的不死uboot,这里的不死指的是不管怎么刷firmware
都可以方便更换firmware,而不是uboot本身就是不死的。
这里将其的代码分析一下。
代码的网址是:https://github.com/pepe2k/u-boot_mod
uboot的代码在openwrt上是最底层的,就像PC的BIOS。
整个uboot最开始的入口,是一段汇编语言(MIPS)代码
要证明这段代码是一开始执行的,首先要看链接器脚本(u-boot-bootstrap.lds):
链接器脚本
OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips", "elf32-tradbigmips")
OUTPUT_ARCH(mips)
ENTRY(_start_bootstrap)
SECTIONS
{
. = 0x00000000;
. = ALIGN();
.text :
{
*(.text*)
}
. = ALIGN();
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN();
.data : { *(.data*) }
. = .;
_gp = ALIGN();
.got : {
__got_start_bootstrap = .;
*(.got)
__got_end_bootstrap = .;
}
. = ALIGN();
.sdata : { *(.sdata*) }
uboot_end_data_bootstrap = .;
num_got_entries = (__got_end_bootstrap - __got_start_bootstrap) >> ;
. = ALIGN();
.sbss : { *(.sbss*) }
.bss : { *(.bss*) . = ALIGN(); }
uboot_end_bootstrap = .;
}
这里可以看到,程序入口是_start_bootstrap,程序的入口代码段(.text)地址是 0x00000000
那么,接下来分析最开始执行的代码(MIPS)
_start_bootstrap
start_bootstrap.s这个文件的入口处是_start_bootstrap
最终会调用到以下代码:
la t9, bootstrap_board_init_r j t9
它的作用就是将bootstrap_board_init_r的地址load到t9寄存器,然后根据t9寄存器的内容跳转。
bootstrap_board_init_r
在bootstrap_board_init_r里面,会调用
fn = (void *)ntohl(hdr->ih_load); (*fn)(gd->ram_size);
这里的hdr是根据image来设定的,这里不仔细分析了,得知其跳转的地址是start.s的_start
函数
_start
最终会调用到以下代码:
la t9, board_init_r j t9
它的作用就是将board_init_r的地址load到t9寄存器,然后根据t9寄存器的内容跳转。
board_init_r
从这里开始,进入c语言的地盘了,下面逐个分析一下它们的作用。
从debug消息来看,这里已经开始在RAM上跑了。一开始是从硬件的0地址开始跑的,一开始的汇编中一般有将代码copy到RAM的过程(参考http://www.linuxidc.com/Linux/2012-08/68707.htm)。
printf("Now running in RAM - U-Boot at: %08lX\n", dest_addr);
接下来重新定位CMD table,
因为已经得到了 gd->reloc_off,那么将CMD table往后移动 gd->reloc_off。
/*
* We have to relocate the command table manually
*/
for(cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++){
ulong addr; addr = (ulong)(cmdtp->cmd) + gd->reloc_off; cmdtp->cmd = (int (*)(struct cmd_tbl_s *, int, int, char *[]))addr; addr = (ulong)(cmdtp->name) + gd->reloc_off;
cmdtp->name = (char *)addr; if(cmdtp->usage){
addr = (ulong)(cmdtp->usage) + gd->reloc_off;
cmdtp->usage = (char *)addr;
}
#ifdef CFG_LONGHELP
if(cmdtp->help){
addr = (ulong)(cmdtp->help) + gd->reloc_off;
cmdtp->help = (char *)addr;
}
#endif
}
那么什么是CMD
table呢?这里举个例子:
Cmd_boot.c (common):U_BOOT_CMD(go, CFG_MAXARGS, , do_go, "start application at address 'addr'\n", Cmd_boot.c (common):U_BOOT_CMD(reset, , , do_reset, "perform RESET of the CPU\n", NULL); Cmd_bootm.c (common):U_BOOT_CMD(bootm, , , do_bootm, "boot application image from memory\n", "[addr]\n" Cmd_bootm.c (common):U_BOOT_CMD(boot, , , do_bootd, "boot default, i.e., run 'bootcmd'\n", NULL); Cmd_bootm.c (common):U_BOOT_CMD(bootd, , , do_bootd, "boot default, i.e., run 'bootcmd'\n", NULL);
从 U_BOOT_CMD这个宏来看,已经将CMD
table的每一个entry和地址绑定了。
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
其中开始和结束的范围就是
__u_boot_cmd_start 和
__u_boot_cmd_end
从链接脚本可以看到:
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
除了CMD table 调整之外,这里对基本的硬件做了初始化:
/* 从硬件读取mac地址 */ memcpy(buffer, (void *)(CFG_FLASH_BASE + OFFSET_MAC_DATA_BLOCK + OFFSET_MAC_ADDRESS), ); /*get CPU/RAM/AHB clocks */ ar7240_sys_frequency(&cpu_freq, &ddr_freq, &ahb_freq); /* configure available FLASH banks */ size = flash_init(); /* initialize malloc() area */ mem_malloc_init(); /* std in/out 初始化 */ devices_init(); console_init_r(); /* ethernet 初始化 */ eth_initialize(gd->bd);
当这些东西初始化完之后,执行main_loop
/* main_loop() can return to retry autoboot, if so just run it again. */
for(;;){
main_loop();
}
main_loop
在这里做了一些主要的分支工作,
uboot 里面集成了一个hush shell,可以将其看成一个精简的bash,可以处理用户的命令和一些脚本。
在这个main
loop里面,决定了从以下几种方式选择启动:
1.web
failsafe
2.U-Boot
console
3.U-Boot
netconsole
4.boot
command
用下图概括:

reset_button_status
指的是读取reset按键的状态,检测其是否按下。
httpd则实现了用web页面来更新firmware的操作。
关于网页更新firmware的http接口这个特色功能后面再分析,下面关注一下do_bootm后面的流程。
do_bootm
这个函数里面做了下面几个事情:
.print_image_hdr //打印出firmware的头部,此时已经知道firmware所处的地址 .eth_halt //停止ethernet的协议栈,之前可能被开起来了,这里先关闭 .lzma_inflate //解压linux kernel 到内存 .do_bootm_linux //进入此函数,如果进入失败,就调用do_reset复位板子
do_bootm_linux
这里是uboot最后一个过程的函数了,它做了下面的事情:
1.linux_params_init/linux_env_set //生成即将传入linux的一些参数
2.theKernel(linux_argc, linux_argv, linux_env, 0); //启动linux kernel
其中,theKernel的地址是这样来的:
theKernel = (void (*)(int, char **, char **, int *))ntohl(hdr->ih_ep);
这里的ih_ep指针的数据可以通过mkimage工具来指定,详见mkimage.c。
到这里为止。uboot的大体流程就分析好了,这个uboot本来就是定制过的,通过汉化、修改网页的
手段可以进一步进行定制,其中已经有很多做好的版本了,如果不是要深入研究uboot,建议拿现成的
来使用,毕竟openwrt主要关注的是firmware部分。
参考资料:http://www.cnblogs.com/lagujw/p/3996576.html
openwrt u-boot_mod 代码分析的更多相关文章
- Android代码分析工具lint学习
1 lint简介 1.1 概述 lint是随Android SDK自带的一个静态代码分析工具.它用来对Android工程的源文件进行检查,找出在正确性.安全.性能.可使用性.可访问性及国际化等方面可能 ...
- pmd静态代码分析
在正式进入测试之前,进行一定的静态代码分析及code review对代码质量及系统提高是有帮助的,以上为数据证明 Pmd 它是一个基于静态规则集的Java源码分析器,它可以识别出潜在的如下问题:– 可 ...
- [Asp.net 5] DependencyInjection项目代码分析-目录
微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...
- [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable<>补充)
Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...
- 完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)
构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化 ...
- STM32启动代码分析 IAR 比较好
stm32启动代码分析 (2012-06-12 09:43:31) 转载▼ 最近开始使用ST的stm32w108芯片(也是一款zigbee芯片).开始看他的启动代码看的晕晕呼呼呼的. 还好在c ...
- 常用 Java 静态代码分析工具的分析与比较
常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...
- SonarQube-5.6.3 代码分析平台搭建使用
python代码分析 官网主页: http://docs.sonarqube.org/display/PLUG/Python+Plugin Windows下安装使用: 快速使用: 1.下载jdk ht ...
- angular代码分析之异常日志设计
angular代码分析之异常日志设计 错误异常是面向对象开发中的记录提示程序执行问题的一种重要机制,在程序执行发生问题的条件下,异常会在中断程序执行,同时会沿着代码的执行路径一步一步的向上抛出异常,最 ...
随机推荐
- SAM4E单片机之旅——17、通过UART进行标准IO
交互还是很有必要的,而且使用键盘和显示器的交互效率还是很高的.当然,可以直接使用UART进行字符的输入和输出.但是又何必浪费了C的标准输入输出的格式控制之类的功能呢? 这次内容就是使用scanf() ...
- 生成大小为100的数组,从1到100,随机插入,不连续,也不重复[C#]
生成大小为100的数组,从1到100,随机插入,不连续,也不重复. 实现思路 生成一个100位的集合listA,放1到100 创建一个空的集合listB,用来存放结果 创建一个变量c,临时存储生成的数 ...
- hdu 1506
题目中叫求一个最大的区域,则第i个矩形对应的面积是ave[i] = (r[i] – l[i] + 1) * a[i];l[i]表示以它这个高度所能到达的最左边的位置(最左一个高度不小于它的高度的位置) ...
- c++ Windows Socket实现最简单的C/S网络通信(TCP)
1.服务器端代码: #include<iostream> #include<WinSock2.h> #pragma comment(lib, "ws2_32.lib& ...
- [转]Ionic – Mobile UI Framework for PhoneGap/Cordova Developers
本文转自:http://devgirl.org/2014/01/20/ionic-mobile-ui-framework-for-phonegapcordova-developers/ Ionic i ...
- 《EXO指数型组织》阅读脑图
书籍:<EXO指数型组织:打造独角兽公司的11个最强属性> 预览链接:https://www.processon.com/view/link/57ce8279e4b08cbf6ca8f85 ...
- 51nod-1537 1537 分解(矩阵快速幂+找规律)
题目链接: 1537 分解 问(1+sqrt(2)) ^n 能否分解成 sqrt(m) +sqrt(m-1)的形式 如果可以 输出 m%1e9+7 否则 输出no Input 一行,一个数n.( ...
- POJ 1061 青蛙的约会【扩展欧几里德】
设跳的次数为t 根据题意可得以下公式:(x+mt)%L=(y+nt)%L 变形得 (x+mt)-(y+nt)=kL (n-m)t+kL=x-y 令a=(n-m),b=L,c=x-y 得 at+bk=c ...
- redis 一二事 - 搭建集群缓存服务器
在如今并发的环境下,对大数据量的查询采用缓存是最好不过的了,本文使用redis搭建集群 (个人喜欢redis,对memcache不感冒) redis是3.0后增加的集群功能,非常强大 集群中应该至少有 ...
- MySQL数据库学习笔记(十)----JDBC事务处理、封装JDBC工具类
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...