1 理解 uboot, kernel ,文件系统那几个概念
2 学会烧写系统
3 学会编译系统,把那几个文件生成出来
4 学习Linux 应用程序的编写和调试
5 学习最简单的字符类驱动的编写和调试
6 读源码
iROM-->BL1--->BL2---->uboot---->zImage---->挂接文件系统
1. 单片机和嵌入式的区别
- 单片机(51、MSP430、stm32、plc、avr)属于微控制器,属于MCU,也可以跑操作系统,比如UCOS;
- 高端的ARM(ARM9、ARM11、A8、A9;)属于MPU,MPU比MCU多了CACHE(高速缓存)、MMU(内存管理单元);
单片机属于物理地址,嵌入式属于虚拟地址。上电都会从从0地址(main)开始执行,4412的0地址就是IROM;
2. iROM
iROM: 就是芯片内部的一段程序,芯片自带的,源码不开源,4412可以从TF卡启动,或者EMMC或者USB启动。4412上电会先执行这一段代码 。
IROM执行之后会先找到外部栈程序,找到之后拷贝8k的程序到内部的RAM,这8K程序就是BL1(bootlloder1) BOOT在编译完成后,就会把BL1和自己拼接到一起。
3. BL1和BL2区别
BL1只能放在4个地方:Booting device(NAND、SD/MMC、eMMC、USB)
BL1在IRAM中运行,这个时候DDR3的控制器还没有初始化。
IRAM:
- 4412芯片内部的RAM,上电就能运行,不用初始化就能用;BL1和BL2都是在IRAM里执行的;BL1并没有初始化DDR3。
- 因为造价高,IRAM总共256k;所以不能把操作系统装进来,必须分段去启动;
- 其实IROM程序已经写死了,必须那么大8K或者15K.
- 上电启动执行IROM程序以后,加载BL1的时候,要对BL1进行多种安全检验,防止BL1是假的,然后BL1会对BL2进行各种检验
- 这样就有了BL1和BL2,BL2就是uboot的前面一小段程序,也就是前14K,但是这前14K也是要在IRAM里运行的!
- 其实UBOOT,取出前14K就是BL2;
- UBOOT也要做补齐,就是328K,无论怎么编译都是328K。后面有个区域 TZSW,这个东西必须放到固定的地址,所以必须为328K。
UBOOT中的Makefile
@./sdfuse_q/chksum
@./sdfuse_q/add_padding
@rm bl2a*
当uboot生成以后,会通过add_padding后续处理一下
隐身的BL1
为了防止任何人写的uboot都能够刷到系统运行的,必须拿到三星的BL1,才能通过安全认证,根据各个厂家的公钥,根据RSA算法算的,secure booting,为了保证系统安全,从源头避免病毒的进入。
shell命令:
if [ -z $1 ]
then
. ild_uboot.sh
else
. ild_uboot.sh $1
fi
4. Uboot启动:
1. uboot是个大软件
1.是一个BootLoader(BIOS属于同类)是个最大的裸机程序,包含很多功能,源码全开放。有协议栈(网络TCP/IP的协议栈),由文件系统(包含board、CPU、drivers)、但是他没有多任务的机制,不能实现任务的调度等操作系统应用的功能,内核启动之后UBOOT就没什么事了。
2. uboot 和Linux内核不一样
1.他没有多任务调度的代码,虽然可以挂接文件系统,也可以有网络协议栈
2. board:里面是讲的和某块板子相关的代码,是板级的代码。
3. CPU:目录里是对应的不同处理器的初始化代码,uboot能支持非常多的处理器,和非常多的不同的开发板,里面很多很多的处理器架构,这么多的处理器都能支持。
4. drivers:目录里面就是一些驱动,GPIO, I2C, PCI, LCD 等驱动都在里面.
3.build_uboot.sh
把这五个文件拼接,最终形成u-boot-iTOP-4412.bin 。
cat build_uboot.sh
E4412_N.bl1.SCP2G.bin
bl2.bin
all00_padding.bin
u-boot.bin
tzsw_SMDK4412_SCP_2GB.bin > u-boot-iTOP-4412.bin
- 第一个文件:E4412_N.bl1.SCP2G.bin 这个就是BL1 ,前8K就是他,三星的,不开源,很神秘。
- 第二个文件:bl2.bin 这个是BL2,也就是BL1执行后要加载的文件,确实是UBOOT前面截取的。
- 第三个文件: all00_padding.bin 这个完全是为了补齐用的,里面全是0000
- 第四个:u-boot.bin 这个才是真正的uboot源码编译出来的镜像文件
- 第五个文件:tzsw_SMDK4412_SCP_2GB.bin 这个是ARM的一项新技术,也就是trustzoon技术,为了系统安全用的,实际上也可以不用
4. uboot最初执行的是汇编
1.集中在那三个汇编文件当中,然后就是C代码了
-
u-boot.lds
编译连接脚本,用来描述输出文件的内存布局
.text :
{
cpu/arm_cortexa9/start.o (.text)
cpu/arm_cortexa9/s5pc210/cpu_init.o (.text)
board/samsung/smdkc210/lowlevel_init.o (.text)
common/ace_sha1.o (.text)
*(.text)
} -
先执行文件 start.s ,然后再执行cpu_init.s
_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
这个就是中断向量,一个中断来了,代码会跳到 ldr pc, _irq
如果执行了一个非法的指令,系统会跳到ldr pc, _undefined_instruction
ARM的每条指令是对齐的,都是占用4个字节,也就是说 ,中断向量表各个指令的位置是确定的,这样才能在中断发生时,系统能到固定的地址处找到相关指令,
中断向量:http://blog.chinaunix.net/uid-10569296-id-2947644.html -
cpu/arm_cortexa9/s5pc210/cpu_init.s
cpu_init.s 这个文件里的汇编,主要功能就是初始化DDR内存,也就是设置DDR的参数,让DDR3能工作起来。 -
board/samsung/smdkc210/lowlevel_init.s
cpu_init.s 这个文件里有个函数 mem_ctrl_asm_init_ddr3, 这个函数是可以被别的文件调用的,因为前面有个 globl 的关键字,在汇编里看到 ’ .globl‘的关键字,就说明这个函数是能被别的文件调用的,功能就是初始化内存DDR。cpu_init.s 里的这个函数’mem_ctrl_asm_init_ddr3‘在board/samsung/smdkc210/lowlevel_init.s,这个文件里面搜’mem_ctrl_asm_init_ddr3‘,就可以看到它被调用了。
也就是说’mem_ctrl_asm_init_ddr3‘被’lowlevel_init‘调用了。 -
汇编如何跳转到C语言
汇编执行完,ldr pc, _start_armboot它的意思是把函数start_armboot的地址,赋值给pc指针,ldr是装载命令,下一个指令就从start_armboot函数开始执行了,而start_armboot这个函数是个C函数,它在文件 lib_arm/board.c 中,这样,就完成了从执行汇编语句到执行C语言语句的转变。
5. uboot 加载linux内核zImage,挂载文件系统
1.uboot:开发板开机先运行uboot,检查内存、存储、设置处理器等设备时钟。之后引导内核,把zImage文件拷贝到内存运行,之后结束。
2. zImage: 就是Linux内核编译生成后的镜像文件,对各种外设进行驱动初始化、之后挂载一个文件系统,这个系统就是Android系统、QT系统、Ubuntu系统(实际上就是执行各个应用程序)。
3. linux必须挂载文件系统, Vxworks、ucos、这些都不需要;
4. linux会再最后挂载文件系统。
5 .LINUX驱动
-
一切皆文件
Linux把LED、蜂鸣器、I2C、LCD、触摸等等设备都看成了文件!(就是存在于我们文件管理器目录里的东西)不管是什么设备,对它的操作无非是 读或者 写 !
对于文件来讲只有读和写两个操作:比如ADC的驱动,就是去采集AD转换的数据,读就行了。 -
五个函数
对于文件的操作一般有5个函数。就是说Linux上层应用,对字符类设备调用的时候,就是通过这5个函数来完成。
open():打开
read() :读
write():写
ioctl():想从文件中间读数据,就要用这个函数把读取的指针指定到文件的中间。
close():关闭 -
做驱动
尤其是字符类驱动,就是做好open(),write(),ioctrl()等几个函数,然后等着上层去调用!
1. 如何区分?
Linux底层那么多驱动,有LED,LCD,TP,蜂鸣器设备文件节点包含两个信息:主设备号 和 次设备号?
主设备号用来区分不同类的设备,比如LED,串口,网络等。
次设备号区分同类设备里的不同的硬件,比如迅为的板子有四个串口,这四个串口主设 备号相同,次设备号却不相同。通过设备文件,或者说通过设备号来区分不同的硬件
2.如何挂载?
做了那几个函数以后,怎么挂接到Linux系统上
设备文件(或者叫做设备节点)它就是应用程序和驱动程序之间的桥梁!/dev这个目录里的所有文件 是 设备文件,而不是普通 的文件,所有的设备,只要是做好了驱动,都要在这个目录里生成一个文件,(比如我们 对LED操作,那就要用 open这个函数打开设备文件 /dev/leds)。
char *leds = “/dev/leds”;
if((fd = open(leds, O_RDWR|O_NOCTTY|O_NDELAY))<0)
3.如何注册?
做驱动,其实就是做那几个函数而已,比如open,read等,但是做完那几个函数以后,必须注册到系统!注册以后,Linux才能找到它通过register_chrdev()函数。
我们先写 open, read,write, close 那几个函数,然后把这几个函数,放到一个结构里面,也就是file_operations,最后通过函数register_chrdev(),把我们做的那几个函数和 设备文件以及设备号关联起来!
4. 注册函数 register_chrdev
int register_chrdev (unsigned int major, const char name, struct file_operationsfops);
第一个参数就是major ,也就是刚才说的主设备号!
第二个参数就是 name, 也就是设备文件的 名字!
第三个是个结构file_operations,这个里面就是函数指针,实际就是我们做的那5个函数存在里面!
struct file_operations simple_fops = {
.owner = THIS_MODULE,
.read = LED_read,
.write = LED_write,
.open = LED_open,
.release = LED_release,
};- 它里面的每个变量都是 函数指针,也就是说指向open ,write那些函数,也就是说把这个结构注册到系统,并和设备文件关联起来,所有事情都就完成了!
- 不同的设备,它的设备文件不一样,直接打开对应的设备文件就行了。
- 系统的open,找到文件、找到设备号,根据设备号,找到对应的驱动open。
- 应用程序调用的open 会最终调用 驱动里我们做的open 。