没有合适的资源?快使用搜索试试~ 我知道了~
首页U-Boot启动流程与内核引导深度解析
本文档深入剖析了U-Boot的内部工作机制和核心代码,包括启动流程、内存布局、命令实现、环境变量管理、配置文件解析、与内核交互以及移植过程。首先,我们从`start.s`文件出发,讲解ARM920T处理器的启动过程,介绍了全局数据结构的初始化,以及如何调用通用初始化函数来设置硬件设备和环境变量。 在U-Boot的链接器脚本(.lds)分析中,讨论了如何将各个模块正确地链接到内存,以及`ldr`和`adr`指令在处理标号表达式上的差异。接下来,通过学习笔记的形式,概述了U-Boot内存的布局和启动步骤,涉及环境变量的初始化、保存和使用,如`env_init`、`env_relocate`等函数的作用。 对于U-Boot编译过程,重点介绍了`mkconfig`文件在自定义配置中的角色,以及如何从NAND闪存启动U-Boot的设计,包括NAND闪存的工作原理和启动策略。此外,文档还涉及如何设计支持NAND闪存的启动程序和U-Boot命令,以及与内核通信的参数传递机制,如`structtag`的使用。 移植部分深入探讨了u-boot工程的总体结构,包括源代码组织、makefile的使用,以及如何实现平台无关性。此外,对u-boot的启动流程、关键数据结构和内存分配进行了详细阐述,强调了移植过程中可能遇到的关键细节和挑战。 最后,文章针对ST2410平台,介绍了基于NORFLASH和NANDFLASH启动U-boot的具体步骤,以及命令编程的技巧。整个文档为理解U-Boot的工作原理提供了全面而深入的视角,适合对嵌入式系统和Linux启动流程有深入研究需求的专业人士。
资源详情
资源推荐
.rodata : { *(.rodata) } ;指定只读数据段
. = ALIGN(4);
.data : { *(.data) } ;指定读/写数据段
. = ALIGN(4);
.got : { *(.got) } ;指定 got 段, got 段式是 uboot 自定义的一个段, 非标准段
__u_boot_cmd_start = . ;把__u_boot_cmd_start 赋值为当前位置, 即起始位
置
.u_boot_cmd : { *(.u_boot_cmd) } ;指定 u_boot_cmd 段, uboot 把所有的 u
boot 命令放在该段.
__u_boot_cmd_end = .;把__u_boot_cmd_end 赋值为当前位置,即结束位置
. = ALIGN(4);
__bss_start = .; 把__bss_start 赋值为当前位置,即 bss 段的开始位置
.bss : { *(.bss) }; 指定 bss 段
_end = .; 把_end 赋值为当前位置,即 bss 段的结束位置
}
分享一篇我总结的 uboot 学习笔记(转)
1. 下面代码是系统启动后 U-boot 上电后运行的第一段代码,他是什么意思?
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
他们是系统定义的异常,一上电程序跳转到 reset 异常处执行相应的汇编指令,下面定义出
的都是不同的异常,比如软件发生软中断时,CPU 就会去执行软中断的指令,这些异常中断
在 CUP 中地址是从 0 开始,每个异常占 4 个字节。
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
操作系统先注册一个总的中断,然后去查是由哪个中断源产生的中断,再去查用户注册的中
断表,查出来后就去执行用户定义的用户中断处理函数。
ldr pc, _undefined_instruction 表示把_undefined_instruction 存放的数值存放到 pc 指针上,
_undefined_instruction: .word undefined_instruction 表示未定义的这个异常是由.word 来定
义的,它表示定义一个字,一个 32 位的数,.word 后面的数表示把该标识的编译地址写入
当前地址,标识是不占用任何指令的。把标识存放的数值 copy 到指针 pc 上面,那么标识上
存放的值是什么?是由.word undefined_instruction 来指定的,pc 就代表你运行代码的地址,
她就实现了 CPU 要做一次跳转时的工作。
什么是编译地址?什么是运行地址?
32 位的处理器,它的每一条指令是 4 个字节,以 4 个字节存储顺序,进行顺序执行,CPU
是顺序执行的,只要没发生什么跳转,它会顺序进行执行,编译器会对每一条指令分配一个
编译地址,这是编译器分配的,在编译过程中分配的地址,我们称之为编译地址。
运行地址是指,程序指令真正运行的地址,是由用户指定的,用户将运行地址烧录到哪里,
哪里就是运行的地址。比如有一个指令的编译地址是 0x5,实际运行的地址是 0x200,如果
用户将指令烧到 0x200 上,那么这条指令的运行地址就是 0x200,当编译地址和运行地址不
同的时候会出现什么结果?结果是不能跳转,编译后会产生跳转地址,如果实际地址和编译
后产生的地址不相等,那么就不能跳转。C 语言编译地址都希望把编译地址和实际运行地址
放在一起的,但是汇编代码因为不需要做 C 语言到汇编的转换,可以人为的去写地址,所以
直接写的就是他的运行地址,这就是为什么任何 bootloader 刚开始会有一段汇编代码,因为
起始代码编译地址和实际地址不相等,这段代码和汇编无关,跳转用的运行地址。编译地址
和运行地址如何来算呢?假如有两个编译地址 a=0x10,b=0x7,b 的运行地址是 0x300,那
么 a 的运行地址就是 b 的运行地址加上两者编译地址的差值,a-b=0x10-0x7=0x3,a 的运行
地址就是 0x300+0x3=0x303。
假设 uboot 上两条指令的编译地址为 a=0x33000007 和 b=0x33000001,这两条指令都落在
bank6 上,现在要计算出他们对应的运行地址,要找出运行地址的始地址,这个是由用户烧
录进去的,假设运行地址的首地址是 0x0,则 a 的运行地址
为 0x7,b 为 0x1,就是这样算出来的。
为什么要分配编译地址?这样做有什么好处,有什么作用?
比如在函数 a 中定义了函数 b,当执行到函数 b 时要进行指令跳转,要跳转到 b 函数所对应
的起始地址上去,编译时,编译器给每条指令都分配了编译地址,如果编译器已经给分配了
地址就可以直接进行跳转,查找 b 函数跳转指令所对应的表,进行直接跳转,因为有个编译
地址和指令对应的一个表,如果没有分配,编译器就查找不到这个跳转地址,要进行计算,
非常麻烦。
什么是相对地址?
以 NOR Flash 为例,NOR Falsh 是映射到 bank0 上面,SDRAM 是映射到 bank6 上面,uboot
和内核最终是在 SDRAM 上面运行,最开始我们是从 Nor Flash 的零地址开始往后烧录,uboot
中至少有一段代码编译地址和运行地址是不一样的,编译 uboot 或内核时,都会将编译地址
放入到 SDRAM 中,他们最终都会在 SDRAM 中执行,刚开始 uboot 在 Nor Flash 中运行,运
行地址是一个低端地址,是 bank0 中的一个地址,但编译地址是 bank6 中的地址,这样就会
导致绝对跳转指令执行的失败,所以就引出了相对地址的概念。那么什么是相对地址呢?至
少在 bank0 中 uboot 这段代码要知道不能用 b+编译地址这样的方法去跳转指令,因为这段
代码的编译地址和运行地址不一样,那如何去做呢?要去计算这个指令运行的真实地址,计
算出来后再做跳转,应该是 b+运行地址,不能出现 b+编译地址,而是 b+运行地址,而运行
地址是算出来的。
_TEXT_BASE:
.word TEXT_BASE //0x33F80000,在 board/config.mk 中
这段话表示,用户告诉编译器编译地址的起始地址
Uboot 启动流程
1. 设置 CPU 的启动模式
reset:
//设置 CPU 进入管理模式 即设置相应的 CPSR 程序状态字
/* * set the cpu to SVC32 mode*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
2. 关闭看门狗,关闭中断,所谓的喂狗是每隔一段时间给某个寄存器置位而已,在实际
中会专门启动一个线程或进程会专门喂狗,当上层软件出现故障时就会停止喂狗,停止喂狗
之后,cpu 会自动复位,一般都在外部专门有一个看门狗,做一个外部的电路,不在 cpu 内
部使用看门狗,cpu 内部的看门狗是复位的 cpu,当开发板很复杂时,有好几个 cpu 时,就
不能完全让板子复位,但我们通常都让整个板子复位。看门狗每隔短时间就会喂狗,问题是
在两次喂狗之间的时间间隔内,运行的代码的时间是否够用,两次喂狗之间的代码是否在两
次喂狗的时间延迟之内,如果在延迟之外的话,代码还没改完就又进行喂狗,代码永远也改
不完。
//关闭看门狗的实际代码
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON //将 pwtcon 寄存器地址赋给 R0
mov r1, #0x0 //r1 的内容为 0
str r1, [r0] //将 R1 的内容送到 Ro 寄存器中去
3. 屏蔽所有中断,为什么要关中断?中断处理中 ldr pc 是将代码的编译地址放在了指针上,
而这段时间还没有搬移代码,所以编译地址上面没有这个代码,如果进行跳转就会跳转到空
指针上面
/* * mask all IRQs by setting all bits in the INTMR - default*/
mov r1, #0xffffffff //寄存器中的值全为 11111111111111111111111111111111
ldr r0, =INTMSK //将管理中断的寄存器地址赋给 ro
str r1, [r0] //将全 1 的值赋给 ro 地址中的内容
#if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
#endif
剩余87页未读,继续阅读
mengguiren
- 粉丝: 0
- 资源: 3
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 十种常见电感线圈电感量计算公式详解
- 军用车辆:CAN总线的集成与优势
- CAN总线在汽车智能换档系统中的作用与实现
- CAN总线数据超载问题及解决策略
- 汽车车身系统CAN总线设计与应用
- SAP企业需求深度剖析:财务会计与供应链的关键流程与改进策略
- CAN总线在发动机电控系统中的通信设计实践
- Spring与iBATIS整合:快速开发与比较分析
- CAN总线驱动的整车管理系统硬件设计详解
- CAN总线通讯智能节点设计与实现
- DSP实现电动汽车CAN总线通讯技术
- CAN协议网关设计:自动位速率检测与互连
- Xcode免证书调试iPad程序开发指南
- 分布式数据库查询优化算法探讨
- Win7安装VC++6.0完全指南:解决兼容性与Office冲突
- MFC实现学生信息管理系统:登录与数据库操作
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功