1.start.s代码分析(第一阶段) /* 以下是具有arm特色的异常向量表,为中断异常准备 */ -------------------- .globl _start _undefined_instruction: .balignl 16,0xdeadbeef --------------------
/* 当发生中断异常时,pc会跳转到.word的后面地址处 处理异常, undefined异常由arm核译码单元检测,并触发未定义指令异常请求,硬件设置pc的值为0x4,强制程序从内存0x4地址执行指令; 0x8存放软件中断处理指令,arm中使用swi指令时触发软件中断,硬件设置PC的值为0x8,同时进入系统模式,多用在系统库的编写; prefetch异常,预取指中止异常,导致正在取的指令无法正常取出,这里需要注意流水线造成的pc值 ; data中止,无法获取数据,产生的原因有可能是内存未准备好、内存无读或写权限等一些原因产生的异常; 0x14暂时未使用; 0x18提供系统硬件中断跳转接口,一般我们的处理器都会引出很多的外部中断线,在这里能做的就是判断系统中断线产生的中断,注册中断,初始化中断,调用中断函数等等; 0x1c地址为_fiq快速中断,一个系统在中断流水线上可能产生很多中断,但快中断只会有一个 */ -------------------- _undefined_instruction: .balignl 16,0xdeadbeef --------------------
/* _TEXT_BASE标号所代表的是uboot代码的运行地址,对于s3c6410 系统来说,如果nand flash启动方式,系统会把0xc000000里面前4KB的内容映射到引导镜像区,即0x0地址,但是我们需要把 uboot代码放到我们的SDRAM,原因是我们代码里面需要对变量做更改并且增加代码执行效率等 下面代码的含义是定义uboot程序执行的运行地址,值为0xc7e00000,.word后面的值TEXT_BASE在编译的时候, 通过向编译器传递参数获得,-DTEXT_BASE方式向编译器传递宏参,在编译的时候可以注意下编译的时候都会指定它的值,值得定义在 config.mk中,Makefile会包含它。 */ -------------------- _TEXT_BASE: --------------------
/* 在uboot里面会开启MMU,下面是在MMU开启前uboot在内存存放的真实物理地址,值为0x57e00000。强调一下,我们做的开发板的SDRAM在DMC1上,即访问物理内存的实际物理地址从0x50000000开始,SDRAM的大小为256M,正好是一个DMC1,所以内存的访问地址就是0x50000000-0x6FFFFFFF之间了。 */ -------------------- _TEXT_PHY_BASE: --------------------
/* 这个不解释也是可以的,但是还是要解释。很多人对_start的值有疑惑,认为是0x0,因为看到_start的标号在代码段最开始处,其实是错误的,汇编代码里面的标号是和编译时指定的运行地址有关系的。我们在编译程序的时候会通过-DTEXT_BASE=0xc7e00000参数告诉编译器我们程序将会运行在0xc7e00000地址,那么自然编译器会认为代码开始的时候就运行在这个地址,那么_start的值自然就是0xc7e00000了。总结之,标号的值与编译时指定的程序地址有关系,而与程序实际存放在内存出的位置无关。 小心使用哦。特别在使用一些伪指令的时候 */ -------------------- .globl _armboot_start --------------------
/* 下面的代码__bss_start的值是在u-boot.lds脚本里面定义的,虽然没给值,但是你要知道文件的大小和位置是由 编译器指定的,那么还需要我们告诉它值吗?所以没值胜有值啦,由编译时编译器决定它们的值 */ -------------------- .globl _bss_start .globl _bss_end --------------------
/* uboot开始执行的第二条代码处即在这里了,下面的代码使得cpu的模式为管理模式,如果想使得为cpu为管理模式,需要保证cpsr寄存 器的最低5位为10011,下面是把0xd3的值赋值给cpsr,0xd3即1101 0011,最高两位置1的意思为关闭中断和快中断,这是为了防止代码 正在执行时,产生外部中断,导致程序跳转到异常向量表而无法正常按顺序执行。5位为0的意思是cpu的状态为arm状态,如果是1则cpu进入thumb态,thumb态处理16位指令代码和数据。 */ /* -------------------- reset: mrs r0,cpsr --------------------
/* 以下标号所在处的代码比较多,将做逐步分析,这段代码主要的工作也就是改了一些硬件寄存器和内存初始化工作 */ -------------------- cpu_init_crit: -------------------- /* p15代表协处理器,0为一定的值,指令中0b0000四位来表示,现在无具体作用,如果不是0则结果未知,后面的r0是即将写入 c7目标寄存器中的值,后面还有个c7所代表的意思为额外操作码,如果不是c0,则表示的是同一个寄存器的不同物理寄存器,因为 同一个寄存器的名字并不代码通一个物理内存,我们在学rpsr的时候应该知道这点,最后的0提供附加信息,用于区分同一寄存器的 不同物理寄存器,如无附加信息,请保持为0值,否则结果不可预测 下面三行代码不难看出,c7、c8的值被清为0,为什么要清为零呢,你需要去看arm1176jzf-s芯片手册了,其中是有说明的,不再累述,
-------------------- -------------------- /* c1,那么你就该去查c1是用来干什么的。查看得知,是控制寄存器,查看手册是online books12.2.2 Primary register allocation 一节,其中13,9,8位为 V、R、S:V位是对高端异常向量表的支持,如果选择0异常向量表为0x00000000-0x0000001c,如果选择 1异常向量表就是FFFF0000-FFFF001c;R位用于ROM保护的,具体的还要与c5里面的配合,这都是MMU惹的祸,很烦,但是现在 我们还没有讲到MMU,所以为什么这样做,也必须到讲到MMU的时候才见分晓了,S在这里面的意思也是用于系统保护的,和MMU 又是有很大的关系,好吧,后面会找MMU算账的,这里就先不深入了,接下来再分析下下面的指令含义 bic r0, r0, #0x00000087 @ clear bits 7, 2:0(B--- -CAM) 的B位为0表示支持小little-endian,1表示支持big-endian格式的系统内存 CAM为第三位,M为0代表禁止MMU,反之打开,A代表地址对齐检查,0代表禁止,C代表指令数据cache控制,0为禁止 orr r0, r0, #0x00000002 @ set bit 2 (A) Align 这段指令又比较犯贱了,打开地址对齐检查了,这是应该的O(∩_∩)O~,后面又 设置12位为1,含义是如果数据cache和指令cache是分开的话,这里面置1的含义将会打开指令缓存 */ /* -------------------- --------------------
/* 以下代码的作用是为了给256M的内存在MMU开启的时候把0x70000000作为重映射的基地址 c15协处理器寄存器在s3c6410上有特殊作用,它是外部内存端口映射寄存器,32位,在开关MMU的时候发生作用,且优先级最高 这里的0x70000000为外部端口的基地址,0x13的二进制为0x10011,0x10011的意思为256M,代表映射的 大小为256M,0x10010为128M。假如你没开MMU,PHY和Peri port映射的地址将相同。通过下面的内容后,我们知道我们原来uboot 代码是放置到0x57e00000的,现在便只能通过0x57e00000+0x70000000虚拟地址来访问uboot起始地址了。 使用C15的方法是: 1.Opcode_1 set to 0 2.CRn set to c15 3.CRm set to c2 4.Opcode_2 set to 4 还有问题请参考arm1176jzfs芯片手册,如下图: */ -------------------- /* Peri port setup */ --------------------
/* 下面是一条跳转指令,代码这里不贴,但是其中的代码很重要,在lowlevel.S中实现比如说点亮LED灯、关闭watchdog、关闭中断、系统 时钟初始、nand flash初始化、内存控制器初始化。不过说实在的,去仔细分析这些初始化的过程,对于你对如何控制硬件有很大的帮 助, 对于这个函数,所要说的东西太多,会在后面的文章中单独分析它,现在先知道功能就好,没有它代码无法启动。 */ -------------------- bl lowlevel_init --------------------
/* 跳转出来以后,继续执行下面的代码,下面的代码是判断程序是否已经在ram中了,在的话就不拷贝,直接跳转到after_copy了,否则 继续执行下面的代码 */ -------------------- ldr r0, =0xff000fff --------------------
/* 下面代码通过函数copy_from_nand函数把代码拷贝到ram中。steppingstone只能拷贝4KB,我们需要把所有的代码搬运到内存中哦 我们知道s3c6410可以通过SD、onenand、nand启动,但是我们这里做了简化,先只从nand启动,以后会再增加SD卡启动 copy_from_nand代码也在start.S中,做了修改以适合大页访问,如有需要请留言告知,将添加copy_from_nand代码分析 */ -------------------- #ifdef CONFIG_BOOT_NAND --------------------
/* SD卡启动方式,这个宏我没有定义,先保留吧 */ -------------------- #ifdef CONFIG_BOOT_MOVINAND --------------------
/* 这里我啥都没做*/ after_copy:
/* 打开MMU功能 协处理器c3的作用是存储的保护和控制,用在MMU中为内存的域访问控制 c3为32位寄存器,每两位为一个访问控制特权,0x00代表没有访问权限,这时候访问将失效;0x01为客户类型,将根据 地址变换条目中的访问控制位决定是否允许特定内存访问;0x10是保留的,暂时没有使用,0x11为管理者权限,不考虑 地址变换条目中的权限控制位,将不会访问内存失效。 ldr r5, =0x0000ffff 接着下面通过mcr p15, 0, r1, c2, c0, 0指令给c2赋值,c2用于保存页表基地址。所谓页表基地址即是虚实转换的内存页表的首地址。 这里r1的值赋值给了c2,r1的值为0x57exxxxx,c2高14位是储存页表的基地址 最后代码很简单,打开MMU。 */ -------------------- #ifdef CONFIG_ENABLE_MMU
/* Set the TTB register */
/* Enable the MMU */ --------------------
/* 堆栈初始化代码,我们在这里定义了CONFIG_MEMORY_UPPER_CODE sp的值为0xC7FFFFE8 */ -------------------- skip_hw_init: #define CFG_UBOOT_SIZE (2*1024*1024) #else
#endif --------------------
/* 清零BSS段内容为0 */ -------------------- clear_bss: clbss_l: --------------------
/* 跳转到uboot代码的第二个阶段,第二阶段基本上都是用C实现的,幸好前面sp的值已经设置好了 */ -------------------- ldr pc, _start_armboot _start_armboot: -------------------- |