uboot 源码导读MIPS(二) start.S篇

这一篇我会陆续加更多的注释。。。写得不对的请大家指正,感谢。我现在也是小白啊。

 mips架构说明;uboot 20121.04.01版本。

总体流程:

 

 

 

紫色部分我下面会有说明:

1 lowlevel_init:

这个在mips和arm不大一样。mips仅仅是设置有一些CP0的寄存器;arm有很多汇编;

 

2 board_init_f:

  2.1调用init_sequence 函数队列,对板子进行一些初始化:

init_fnc_t *init_sequence[] = {
	board_early_init_f,
	timer_init,
	env_init,		/* initialize environment */
#ifdef CONFIG_INCA_IP
	incaip_set_cpuclk,	/* set cpu clock according to env. variable */
#endif
	init_baudrate,		/* initialize baudrate settings */
	serial_init,		/* serial communications setup */
	console_init_f,
	display_banner,		/* say that we are here */
	checkboard,
	init_func_ram,
	NULL,
};

  2.2 然后计算:addr_sp, id, addr 这三个参数;

  2.3 最后调用:relocate_code(addr_sp, id, addr);

 

3 relocate_code:

  3.1 然后计算出了t0 t1 t2;

  3.2 将uboot.bin搬到RAM;

  3.3 flush_cache;

  3.4 计算in_ram地址,然后到in_ram执行代码;

  

4 board_init_r

  该函数在arch/mips/lib/board.c中。

  初始化:FLASH  NAND PCI console environment ...

  最后进入:main_loop (); //循环执行,试图自动启动,接受用户从串口输入的命令,然后进行相应的工作,设置延时时间,确定目标板是进入下载模式还是启动加载模式

 

下面是源码说明:

#include <asm-offsets.h>
#include <config.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>

#ifndef CONFIG_SYS_MIPS_CACHE_MODE
#define CONFIG_SYS_MIPS_CACHE_MODE CONF_CM_CACHABLE_NONCOHERENT
#endif

	/*
	 * For the moment disable interrupts, mark the kernel mode and
	 * set ST0_KX so that the CPU does not spit fire when using
	 * 64-bit addresses.
	 */
	 /*下面定义了两个宏,后面会用到*/
	.macro	setup_c0_status set clr
	.set	push
	mfc0	t0, CP0_STATUS
	or	t0, ST0_CU0 | \set | 0x1f | \clr
	xor	t0, 0x1f | \clr
	mtc0	t0, CP0_STATUS
	.set	noreorder
	sll	zero, 3				# ehb
	.set	pop
	.endm

	.macro	setup_c0_status_reset
#ifdef CONFIG_64BIT
	setup_c0_status ST0_KX 0
#else
	setup_c0_status 0 0
#endif
	.endm

#define RVECENT(f,n) \
   b f; nop
#define XVECENT(f,bev) \
   b f     ;           \
   li k0,bev

	.set noreorder

	.globl _start
	.text
_start:
	RVECENT(reset,0)			# U-boot entry point
	RVECENT(reset,1)			# software reboot
#ifdef CONFIG_SYS_XWAY_EBU_BOOTCFG
	/*
	 * Almost all Lantiq XWAY SoC devices have an external bus unit (EBU) to
	 * access external NOR flashes. If the board boots from NOR flash the
	 * internal BootROM does a blind read at address 0xB0000010 to read the
	 * initial configuration for that EBU in order to access the flash
	 * device with correct parameters. This config option is board-specific.
	 */
	.word CONFIG_SYS_XWAY_EBU_BOOTCFG
	.word 0x00000000
#else
	RVECENT(romReserved,2)
#endif
	RVECENT(romReserved,3)
	....     //此处省略若干行。
	RVECENT(romReserved,127)

	/*
	 * We hope there are no more reserved vectors!
	 * 128 * 8 == 1024 == 0x400
	 * so this is address R_VEC+0x400 == 0xbfc00400
	 */
	.align 4
reset:

	/* Clear watch registers */
	mtc0	zero, CP0_WATCHLO      /*# 把数据送到协处理器0*/
	mtc0	zero, CP0_WATCHHI

	/* WP(Watch Pending), SW0/1 should be cleared */
	mtc0	zero, CP0_CAUSE        /**清 CAUSE 寄存器**/

	setup_c0_status_reset  /**汇编宏代码见本文件开始位置*/

	/* Init Timer  这两个寄存器一起构造高精度时钟*/
	mtc0	zero, CP0_COUNT
	mtc0	zero, CP0_COMPARE

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	/* CONFIG0 register  将t0和CONFIG寄存器置2*/
/*
下面给大家注释下CONFIG_SKIP_LOWLEVEL_INIT 这个宏的意思。
3406 - CONFIG_SKIP_LOWLEVEL_INIT
3407                 [ARM, NDS32, MIPS only] If this variable is defined, then certain
3408                 low level initializations (like setting up the memory
3409                 controller) are omitted and/or U-Boot does not
3410                 relocate itself into RAM.
3411 
3412                 Normally this variable MUST NOT be defined. The only
3413                 exception is when U-Boot is loaded (to RAM) by some
3414                 other boot loader or by a debugger which performs
3415                 these initializations itself.

*/
	li	t0, CONF_CM_UNCACHED
	mtc0	t0, CP0_CONFIG
#endif

	/* Initialize $gp */
	bal	1f
	 nop
	.word	_gp       /*此处放一个word 型的值*/
1:
	lw	gp, 0(ra)      /*bal 之后的ra 寄存器保存的是当前存储介质的绝对地址。*/

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	/* Initialize any external memory  la指令用于将一个地址(或者标签)存入一个通用寄存器*/
	la	t9, lowlevel_init        /*这是第一个需要我们自己定义的函数。由于没有初始化堆栈,这里只能用汇编。*/
	jalr	t9     /*jal、jalr:这两条指令分别实现了直接和间接子程序调用。
					在跳转到指定地址实现子程序调用的同时,
					需要将返回地址(当前指令地址+8)保存到ra($31)寄存器中。*/
	 nop

	/* Initialize caches... */
	la	t9, mips_cache_reset /*cache.S 它会来清理数据和指令的cache,并设置为正确的值。然后就可以打开cache了*/
	jalr	t9
	 nop

	/* ... and enable them  cache 当内存用了*/
	li	t0, CONFIG_SYS_MIPS_CACHE_MODE   /*宏定义中定义的: 3*/
	mtc0	t0, CP0_CONFIG   /*寄存器16, CPU 参数设定*/
#endif

	/* Set up temporary stack */
	li	t0, CONFIG_SYS_SDRAM_BASE + CONFIG_SYS_INIT_SP_OFFSET
	la	sp, 0(t0)  /*sp is stack pointer 使用堆栈时,一般需要重新设定SP的初始值。*/

	la	t9, board_init_f /*C code 做一些初始化的工作*/
	jr	t9
	 nop

/*
 * void relocate_code (addr_sp, gd, addr_moni)
 *
 * This "function" does not return, instead it continues in RAM
 * after relocating the monitor code.
 *
 * a0 = addr_sp
 * a1 = gd
 * a2 = destination address
 */
	.globl	relocate_code     /*表示全局可见*/
	.ent	relocate_code
relocate_code:                     /*relocate_code的工作就是将代码搬移到RAM中执行*/
	move	sp, a0			# set new stack pointer

	li	t0, CONFIG_SYS_MONITOR_BASE    /*#假设CONFIG_SYS_MONITOR_BASE 是0xbfc00000。*/
	la	t3, in_ram      /*	这里将一个地址存入通用寄存器t3
							#in_ram是个标号,此刻是相对于0xbfc00000。
							顺便介绍下in_ram的作用。
							in_ram的主要工作是:
							更新GOT;清空BSS段;
							最后跳到board_init_r。	
							我们可以看到:
							board_init_r 最后一个参数是在分支延迟槽中赋值的。
						*/

	/*#关注lds文件,在链接的时候会确定uboot_end_data 的值,此刻取值是从FLASH中。*/
	lw	t2, -12(t3)		# t2 <-- uboot_end_data
	move	t1, a2
	move	s2, a2			# s2 <-- destination address

	/*
	 * Fix $gp:
	 *
	 * New $gp = (Old $gp - CONFIG_SYS_MONITOR_BASE) + Destination Address
	 */
	move	t6, gp             /*#把以前的gp 寄存器保存起来。*/
	sub	gp, CONFIG_SYS_MONITOR_BASE     /*#因为我们链接的时候指定了TEXT_BASE,所以可以算出gp相对于TEXT_BASE的偏移。*/

	/*#这里的a2,就是我们假设会relocate到内存中的起始地址0x800xxxxx
	,此刻我们的EXT_BASE变了,所以gp的地址要调整,
	但是它相对于TEXT_BASE的偏移是一定了,
	因此我们可以利用它相对CONFIG_SYS_MONITOR_BASE 的偏移求出新的
	RAM地址。*/
	add	gp, a2			# gp now adjusted

	/*#当然,不光是gp,其他的代码也要调整,
	所以把这个要调整的偏移记录下来。*/
	sub	s1, gp, t6		# s1 <-- relocation offset

	/*
	 * t0 = source address
	 * t1 = target address
	 * t2 = source end address
	 */

	/*
	 * Save destination address and size for later usage in flush_cache()
	 */
	move	s0, a1			# save gd in s0
	move	a0, t1			# a0 <-- destination addr
	sub	a1, t2, t0		# a1 <-- size

1:   /*#下面这段循环的代码就是把整个uboot.bin 从FLASH 复制到RAM中。*/
	lw	t3, 0(t0)     /*从flash读取一个字*/
	sw	t3, 0(t1)		/*将这一个字写到t1 的位置也就是RAM*/
	addu	t0, 4
	ble	t0, t2, 1b
	 addu	t1, 4

	/* If caches were enabled, we would have to flush them here. */

	/* a0 & a1 are already set up for flush_cache(start, size) */
	la	t9, flush_cache
	jalr	t9
	 nop

	/* Jump to where we've relocated ourselves */
	addi	t0, s2, in_ram - _start      /*in_ram 在ram中的地址计算*/
	jr	t0           /*start run in_ram...*/
	 nop


	/*#以下几个变量要结合lds 文件看*/
	.word	_gp
	.word	_GLOBAL_OFFSET_TABLE_
	.word	uboot_end_data
	.word	uboot_end
	.word	num_got_entries

in_ram:
	/*
	 * Now we want to update GOT.
	 *
	 * GOT[0] is reserved. GOT[1] is also reserved for the dynamic object
	 * generated by GNU ld. Skip these reserved entries from relocation.
	 */
	 /*#下面是从RAM中取值,不是从FLASH中了,虽然值的内容是一样的。*/
	lw	t3, -4(t0)		# t3 <-- num_got_entries
	lw	t4, -16(t0)		# t4 <-- _GLOBAL_OFFSET_TABLE_
	lw	t5, -20(t0)		# t5 <-- _gp

	/*#这里用另外的一种方法算偏移,为什么没有利用前面的s1?*/
	sub	t4, t5			# compute offset
	add	t4, t4, gp		# t4 now holds relocated _G_O_T_
	addi	t4, t4, 8		# skipping first two entries
	li	t2, 2

	/*#调整GOT中的内容*/
1:
	lw	t1, 0(t4)
	beqz	t1, 2f
	 add	t1, s1
	sw	t1, 0(t4)
2:
	addi	t2, 1
	blt	t2, t3, 1b
	 addi	t4, 4

	/* Clear BSS  #这里是从RAM中取值,不过取得的值是相对于0xbfc00000,所以需要调整。*/
	lw	t1, -12(t0)		# t1 <-- uboot_end_data
	lw	t2, -8(t0)		# t2 <-- uboot_end
	add	t1, s1			# adjust pointers
	add	t2, s1

	sub	t1, 4 /*#把BSS段清0。*/
1:
	addi	t1, 4
	bltl	t1, t2, 1b
	sw	zero, 0(t1)

	move	a0, s0			# a0 <-- gd
	la	t9, board_init_r         /*#board_init_r中的r应该代表的是RAM,
							前面提到的board_init_f中的f应该代表的是FLASH,到此刻为止
							运行C代码的环境已经构建完成。*/
	jr	t9  /*跳转开始执行*/
	 move	a1, s2

	.end	relocate_code

	/* Exception handlers */
romReserved:
	b	romReserved

romExcHandle:
	b	romExcHandle

  

posted @ 2015-03-15 23:33  JustRelax  阅读(2132)  评论(0)    收藏  举报