-
Notifications
You must be signed in to change notification settings - Fork 25
/
Linux系统移植.c
213 lines (194 loc) · 17.6 KB
/
Linux系统移植.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
一、u-boot工程
1、BootLoader介绍
BootLoader是操作系统运行之前要执行的一段程序,它负责初始化硬件设备、建立内容空间映射,从而操作系统的运行做好准备,是一个专门加载操作系统的程序。
对于嵌入式系统而言,没有通用的硬件平台,因此也没有通用的BootLoader,不同的平台、不同的CPU构架都有不同的BootLoader,因为BootLoader不光依赖CPU的体系结构也依赖硬件平台的配置,对于不同的开发板而言,那怕它的CPU一样,而BootLoader都会有区别,因此我们要为每一款开发板制作它属于它的BootLoader程序。
但大部分开发板的BootLoader仍有许多共性,所以我们没有必须全部从零制作,而对一个基础的BootLoader进行修改,而制作出能用的BootLoader程序。
2、u-boot简介
u-boot( Universal Boot Loader)是BootLoader的一种,它遵循GPL通用许可证的开源项目,它的源码的目录、编译形式都和Linux的源码很相似,可以说u-boot就是仿照Linux而开发的。
3、u-boot源码的获取
1、源头的代码是u-boot官网下载的,这种源码是最干净最纯粹的,一般CPU的生产厂商会下载这种源码,当CPU厂商生产出一款CPU之后就会使用这款CPU制作出一块公板(它会把这款CPU所具备的功能全部体现出来),然后会根据公板修改出一份符合它的u-boot。
2、开发板的供应商会购买CPU厂商的公板,然后对公板进行裁剪(去掉一些不需要的功能、替换掉一些不必要的硬件),然后再根据裁剪后开板有厂商提供的u-boot进行修改,从而制作出属于这款开发板的u-boot。
3、官网、CPU厂商、开发板厂商处可以获取u-boot源码,而从开发板厂商处获取的u-boot源码基本上是可以使用的,不需要做什么大的修改。
4、u-boot源码结构
顶层的u-boot源有30多个目录,大致分为三类:
1、与CPU体系结构或开发板硬件直接相关的代码。
2、能用的函数、驱动程序
3、应用程序、工具、文档
board 平台相关 存储电路板相关的目录文件
cpu 平台相关 与CPU的体系结构相关的库文件,由于S5PC11x的CPU早于S5PV210,而还兼容S5PV210,所以在这个目录只能看到S5PC110。
include 通用 u-boot通用的一些头文件,还有一些各种硬件平台的汇编文件、系统配置和支持文件系统,configs目录有与开发板配置相关的头文件。
common 通用 里面有u-boot所有支持的命令,每一个文件就代表一个命令。
lib_generic 通用 里面是通用的库函数。
net 通用 里面是与网络协议相关的代码。
fs 通用 所支持的文件系统(管理硬盘上的文件的程序)。
drivers 通用 所支持的设备驱动,如:网卡、串口、USB。
disk 通用 对磁盘的支持。
doc 文档 里面非常完整的u-boot说明文档。
tools 通用 u-boot的一些工具
examples 案例 一些能够独立运行的应用程序。
5、u-boot的配置编译
u-boot是通过Makefile组织编译的,顶层的Makefile可以对开发板进行整体配置,然后递归调用各级目录下的Makefile,最后把所有编译过的代码链接成u-boot镜像。
注意:编译前确定交叉编译,Makefile的147行。
1、清理之前的编译的残留。
make distclean
2、配置编译方法
make x210_sd_config
注意:chmod +x mkconfig
3、多线程编译
make -jn 一般n是CPU内核数量的2倍。
6、u-boot的运行
1、u-boot是ELF格式的,而u-boot.bin才是纯二进制指令格式的。
2、u-boot.bin并不能直接运行,原因是没有添加校验和,而且之前添加校和的工具不能继续使用。
3、sd_fusing目录中有为u-boot.bin添加校验和的工具,但并不能直接使用,需要在Makefile添加以下指令。
291 all:
cd sd_fusing && make
./sd_fusing/mkbl1 u-boot.bin sd-bl1-8k.bin 8192
dd if=sd-bl1-8k.bin of=u-boot_image.bin
dd if=u-boot.bin of=u-boot_image.bin seek=48
rm -rf sd-bl1-8k.bin
cp u-boot_image.bin /media/sf_shared/image/
二、u-boot常用命令
1、帮助命令
help/p/?
2、环境变量相关命令
printenv 显示所有环境变量
setenv 添加/删除/修改环境变量
saveenv 保存环境变量到硬盘
注意:环境变量都是字符串,没有数据类型之分。
注意:修改只是临时有效,重启后就会还原,必须使用保存命令才能长期有效。
u-boot中的环境变量
netmask:子网掩码
ipaddr:板子的ip地址
ethaddr:mac地址
baudrate:串口波特率
bootdelay:启动系统前的等待秒数
gatewayip:网关ip地址
bootcmd:默认启动时执行的命令
bootargs:启动时传递给内核的参数
serverip:服务器ip地址
3、网络相关的命令
ping ip 测试与服务器是连通
tftp 0x01020304 file.bin 可以从服务器下载程序到指定的内存地址。
1、ubuntu系统需要搭建tftp服务器
2、在ubuntu系统关机的情况下,设置网络模式为桥接模式,然后启动。
3、设置ubuntu的ip地址,保证ubuntu、windows、开发板三者ip地址在同一网段、子网掩码一致。
4、开发板ping通windows系统。
5、开发板ping通ubuntu系统。
6、在u-boot中执行 tftp d0020010 led.bin,可以将程序下载到开发板的d0020010地址处,go d0020010 可以到此地址执行。
注意:Windows防火墙,如果不会配置规则,建议关闭。
4、启动命令
boottm 0x01020304 从该内存地址启动系统。
boot 它需要与bootcmd环境变量配合,根据bootcmd的设置来启动系统。
一、u-boot编译过程分析
u-boot顶层目录下的Makefile负责u-boot的整体编译过程,因此想要了解u-boot的编译过程需要阅读Makefile文件。
Makefile中没有数据类型之分,全部都是字符串数据。
具体分析过程参见 Makefile_bck 文件
总结:
1、u-boot的源码不是一行行代码写出来的,而拼凑出来的,一份u-boot源码中可能包含了适用于各种开发板的源码,用条件编译进行区分的。
2、很多文件不自带的,而是配置过程根据一些原材料生成的。
3、u-boot的整体的代码构架是参考了Linux内核,而编译过程与Linux也很相似。
二、u-boot的配置过程
1、执行make x210_sd_config 命令,然后make会执行Makefile脚本中的x210_sd_config目标。
2、x210_sd_config依赖了unconfig,unconfig中会删除所有旧的头文件、mk文件。
3、然后调用了mkconfig脚本,并传递了6个参数,分别是x210_sd arm s5pc11x x210 samsung s5pc110。
4、在mkconfig脚本中先确定的开发板的名字,检查参数的数量。
5、根据参数3,删除旧的链接文件,创建新的链接文件。
6、然后把参数2、3、4、5、5写到include目录下的config.mk中
7、在include目录下创建config.h头文件,并让它实际指向,config/下的以参数1命名的头文件。
8、把0xc3e00000写入board/samsung/x210/config.mk文件中,用于指定链接地址。
三、u-boot的链接脚本
u-boot的链接脚本在board/samsung/x210/u-boot.lds,与裸机课程中讲的链接脚本没有本质区别,只是复杂度高一些,文件多一些,使用的技巧多一些。
1、ENTRY(_start) 指定整个uboot的入口地址,类似于C语言中的main函数。
2、指定程序链接地址的方法有两种
在Makefile文件中通过参数 -Ttext=0xc3e00000
在链接脚本中的SECTIONS里 .=0x00000000
注意:如果两个方式都设置,优先使用-Ttext设置的地址,而u-boot实际使用的是0xc3e00000。
一、u-boot第一阶段启动流程
根据u-boot的配置过程可以找到它的链接脚本,然后根据链接脚本中代码段的排列位置找到u-boot的入口代码应该是cpu/s5pc11x/start.S文件。
1、start.S
a、导入了一些头文件,这些头文件都配置过程中生成的链接文件(u-boot不能在windows目录下配置、编译)。
b、config.h头文件是配置过程中生成了,里面的内容只有一行,#include <configs/x210_sd.h>,所以实际被包含的是configs/x210_sd.h。
c、x210_sd.h文件中有大量的宏,里面都u-boot所需要的一些参数。
d、version.h里面只记录一行有效内容,#include "version_autogenerated.h",version_autogenerated.h是配置过程中自动生成的,里面记录是u-boot的版本号。
e、asm不是u-boot中的原生目录,而配置过程中生成的软链接,实际指向是asm-arm目录,而最终被包含的应该是asm-arm/proc-armv/domain.h。
f、regs.h配置过程中生成的,实际被包含的是s5pc110.h。
g、16个字节的校验和,而裸机课程时mkv210_image.c的功能就添加16个字节的校验和。
h、_start:函数中就是用指令模拟的异常向量表,而_start依赖了reset函数,因此真正的入口函数是reset。
i、而reset函数中禁用了外部中断和快速中断,设置cpu为SVC(特权模式)。
j、接下顺序执行了cpu_init_crit函数,首先禁用了二级缓存,然后设置二级缓存,最后再启动二级缓存。
k、然后初始化一级缓存、禁用MMU(内存管理单元,负责内存映射),然后读取启动介绍信息,根据r2寄存器中的值,确定启动介绍,最终会以SD/MMC BOOT方式启动。
l、设置sram内存中位置为栈指针,然后调用lowlevel_init函数,而此函数在board/samsung/x210/lowlevel_init.S。
2、lowlevel_init.S
a、检查复位状态,原因是reset函数被执行的原因有很多,比如:冷上电、热启动、睡眠状态下的唤醒,而它们区别是冷上电需要初始化DDR内存,而其它不用,如果是冷上则继续向下执行,否则调用wake。
b、关闭看门狗,初始化SRAM相关的GPIO管脚,电源锁定。
c、判断当前代码在什么位置,如果在DDR内存中则直接开始加载系统,如果在SRAM中则需要初始化时钟、DDR等。
d、初始化时钟,205行~385行的代码都是初始化时钟系统。
e、初始化DDR内存,mem_ctrl_asm_init函数不在当前文件中,而是定义在cpu/s5pc11x/s5pc110/cpu_init.S。
f、初始uart,当完成uart的初始化后,向uart发送一个'O'。
g、初始化TrustZone,开户系统保护。
h、关闭基带模拟,到此为止lowlevel_init的工作已经全部完成,向uart发送一个'K',然后返回start.S中。
总结lowlevel_init中一共做哪些事情:
检查复位状态、SRAM相关GPIO管理的初始化,关闭看门狗、电源锁定、初始化时钟、初始化DDR内存、初始化串口关打印'O',启动TZPC、关闭基带模拟关打开'K'。
3、Start.S
a、从lowlevel_init函数中返回,再次锁定电源。
b、再次设置栈指针,因为之前已经设置过了,但当时DDR内存还没有初始化只能调到到SRAM中,而此时DDR已经完成初始化可以设置到DDR内存中了。
c、再交判断代码的执行位置,这次判断的目的与之前的不同,这是为了决定是否重定位而判断的,如果此时代码运行在SRAM中,则说当前代码不是完成的u-boot,而初始化工作也已经全部完成,接下来则要把完整的u-boot从SD拷贝到DDR中运行。
d、确定SD卡的通道号,会根据开发板的拨码开关设置自动在内存中设置相应的值。
e、确定拷贝源,也就是什么介质中拷贝u-boot,最终会调用mmcsd_boot函数。
f、mmcsd_boot函数中首先调用了movi_bl2_copy函数,而movi_bl2_copy函数会调用iROM中的拷贝函数。
u32(*copy_sd_mmc_to_mem)(
u32 channel, 表示通道号
u32 start_block, 开始扇区号
u16 block_size, 要拷贝的扇区数量
u32 *trg, 拷贝到的目标位置
u32 init); 保留
g、重定位完成后则开始执行after_copy函数,开启域访问控制(为MMU做准备)。
h、接下来设置TTB(内存映射转换表),然后开户MMU。
物理地址:就是设置在生产时赋予的地址,它是根据在CPU管理上的接线位置决定的,物理地址是硬件编码一旦确定无法修改。
我们在裸机编程时使用到的寄存器的地址都是物理地址,可以通过查阅芯片手册获取并操作,物理地址的缺点就是不够灵活。
虚拟地址:就是在物理的软件操作之间加一个转换,这个转换操作就地址映射。
物理地址到虚拟地址的映射时会建立一个地址映射表,操作硬件时,只需要操作虚拟地址,然后MMU芯片会根据地址映射表自动去操作对应的物理地址,这个过程由MMU芯片负责。
地址映射的好处:
1、可以编程更灵活
2、使用代码的通用性更强
3、在映射时还可以给虚拟地址设置权限,如:可读、可写、可执行等,提高内存的安全性。
4、还可以把零散的内存映射成为一整块的内存,x210共有两个内存接入口,DRAM0可以接512M内存实际只接入了256M,DRAM1可以接1024M内存而实际只接入了256M内存,因此DRAM0与DRAM1中间是不连续的,而通过MMU的映入可以把DRAM0与DRAM1合并成一整块512M内存更方便使用。
i、接下来再次设置栈指针,此次设置是在MMU开启之后,现在已经有了一块连续的512M内存,这次的设置会让内存的使用更安全,更紧凑,更节约内存。
j、清理bss内存段,如果不清理全局变量和static变量就无法定义、使用。
k、以后工作完成后则调用start_armboot函数,此函数位于lib_arm/board.c中,是一个C语言的函数,此时会跳转到DDR内存中开始第二阶段的u-boot运行。
u-boot第一阶段启动流程总结:
1、构建异常向量表
2、设置CPU工作模式为特权模式
3、关闭看门狗
4、开发板电源锁定
5、时钟系统初始化
6、DDR内存初始化
7、初始化串口并打印OK
8、重定位
9、建立映射表并开户MMU
10、跳转到第二阶段
二、u-boot第二阶段启动流程
1、从宏观角度分析u-boot第二阶段启动流程应该做什么
a、第一阶段主要是初始化CPU内部的一些硬件(看门狗、时钟、uart等),然后初始化DDR内存、重定位、开户MMU。
2、第二阶段就应该初始化CPU外部的一些硬件设备了,如:iNand、网卡、ADC。
3、u-boot本身所需要的一些事情,如:环境变量、命令。
4、完成所有的初始化工作后开始倒计时:
1、等待超时,开始加载操作、加载文件系统,然后就是操作系统启动完成。
2、按任意键,进入u-boot的命令行,在命令行中中可以执行命令、设置或查看环境变量。
u-boot的命令行就是个死循环,不停的接收命令、解析命令、执行命令。
三、u-boot源码分析
1、u-boot的源码不是某个人或某人组织完成的,而是不同的组织完成了不同的工作,最终让u-boot具备引导操作系统的能力。
u-boot官网负责整体代码的组织架构。
半导体生产广商,根据CPU的特性对BL1段的代码进行修改,添加。
开发板的生产广商,根据开发板的外部硬件对BL1、BL2阶段的代码进行修改、添加。
2、所有在修改u-boot代码时都遵循这样的原则:
尽量不删除代码,那代码会接下的启动流程也是选择注释掉,而不是删除。
u-boot为了兼容更多的CPU会做一些对于个别CPU无意义的事情。
尽量多做不要少做,那怕做一些无意义的事情,也要让操作系统顺利启动。
3、由于u-boot只负责把操作系统启动起来,因此它不需要效率高、安全、稳定。
4、由于u-boot的代码构成比较复杂,所以质量不高,因此没有借鉴价值。
5、但u-boot代码是按照Linux内存的框架设计的,因此我们分析、研究、了解是为Linux内核的学习打开基础。
四、u-boot启动过程特征总结
1、从代码角度来看,第一阶段主要以汇编代码为主,第二阶段以C代码为主。
2、从内存角度来看,第一阶段主要运行在SRAM中,第二阶段在主要运行在DDR中。
3、从硬件角度来看,第二阶段注重的是CPU内部,第二阶段注意的是CPU外部、开发板的内部。