MPC8560 uboot笔记


MPC8560 平台是freescale公司是PowerpcQUICC II的下一代通讯处理器。它为网络和通讯外设提高了强大的计算能力,从而提高了整个系统的吞吐量和性能。它的主要架构是由一个高性能的e500的核和一个 通讯处理器模块(CPM)组成. e500的核实现了powerpc BOOK E的ISA。

u-boot代码分析

按照e500的架构描述,MPC8560启动时,会执行0xFFFF_FFFC地址的指令,这里存放的是u-boot代码中的cpu/mpc85xx/resetvec.S一条跳转指令,

如下:

	 .section .resetvec,"ax"    //定义一个段resetvec
	 b  _start_e500		    //跳转到_start_e500	

而在链接脚本 board/pq37pc/pq37pc_8560/u-boot.lds中,将resetvec这个段放到了0xffff_fffc这个地址
     28 SECTIONS
     29 {
     30   .resetvec 0xFFFFFFFC :
     31   {
     32     *(.resetvec)
     33   } = 0xffff
     34
     35   .bootpg 0xFFFFF000 :
     36   {
     37     cpu/mpc85xx/start.o (.bootpg)
     38     board/pq37pc/pq37pc_8560/init.o (.bootpg)
     39   } = 0xffff
     40
     41   /* Read-only sections, merged into text segment: */
     42   . = + SIZEOF_HEADERS;
     43   .interp : { *(.interp) }
     44   .hash          : { *(.hash)       }
     45   .dynsym        : { *(.dynsym)     }
     46   .dynstr        : { *(.dynstr)     }
     47   .rel.text      : { *(.rel.text)       }
     48   .rela.text     : { *(.rela.text)  }
     49   .rel.data      : { *(.rel.data)       }
     50   .rela.data     : { *(.rela.data)  }
     51   .rel.rodata    : { *(.rel.rodata)     }
     52   .rela.rodata   : { *(.rela.rodata)    }
     53   .rel.got       : { *(.rel.got)        }
     54   .rela.got      : { *(.rela.got)       }
     55   .rel.ctors     : { *(.rel.ctors)  }
     56   .rela.ctors    : { *(.rela.ctors) }
     57   .rel.dtors     : { *(.rel.dtors)  }
          ................
	  ................
 	}




因此,CPU上电复位后,将跳转到_start_e500执行。


在cpu/mpc85xx/start.S中:

     33 #include <....>
       ...............
     51
     52 /*
     53  * Set up GOT: Global Offset Table
     54  *
     55  * Use r14 to access the GOT
     56  */
     57     START_GOT
     58     GOT_ENTRY(_GOT2_TABLE_)
     59     GOT_ENTRY(_FIXUP_TABLE_)
     60
     61     GOT_ENTRY(_start)
     62     GOT_ENTRY(_start_of_vectors)
     63     GOT_ENTRY(_end_of_vectors)
     64     GOT_ENTRY(transfer_to_handler)
     65
     66     GOT_ENTRY(__init_end)
     67     GOT_ENTRY(_end)
     68     GOT_ENTRY(__bss_start)
     69     END_GOT
     70

**********************************************************************************************************************************************************************************
附注:关于START_GOT, GOT_ENTRY, END_GOT宏
**********************************************************************************************************************************************************************************
相关的宏在./include/ppc_asm.tmpl中定义:
#define START_GOT   /
 .section ".got2","aw"; /
.LCTOC1 = .+32768
#define END_GOT    /
 .text
#define GET_GOT    /
 bl 1f  ; /
 .text  2  ; /
0: .long .LCTOC1-1f ; /
 .text   ; /
1: mflr r14  ; /
 lwz r0,0b-1b(r14) ; /
 add r14,r0,r14 ;
#define GOT_ENTRY(NAME) .L_ ## NAME = . - .LCTOC1 ; .long NAME
#define GOT(NAME)  .L_ ## NAME (r14)

总体来说:
START_GOT ——用于定义表的开始
END_GOT     ——用于定义表的结束
GOT_ENTRY——用于将offset写入表中
GOT                ——用于从表中读出 offset
GET_GOT     ——用于将表进行初始化
下面详细解释之:
START_GOT定义了段“got2”,aw属性为“allocatable and writable”,并定义了变量.LCTOC1,.LCTOC1的值是表的最高地址。如果设表的起始地址为TABLE_START,则.LCTOC1的 值为TABLE_START+0x8000。

END_GOT定义为子段text 0的开始。

GOT_ENTRY定义了变量.L_NAME,其值为当前表项的地址(.)-.LCTOC1。如果设NAME的表项偏移地址为 NAME_OFFSET,那么.L_NAME = . - .LCTOC1 = TABLE_START + NAME_OFFSET - (  TABLE_START + 0x8000 ) =  NAME_OFFSET - 0x8000。之后将名字为NAME的offset值写入当前表项,这些offset值是在编译的时候确定的。
 
GOT(NAME)的值定义为.L_NAME(r14),这里面r14的值为表的最高地址,也就是.LCTOC1的值(参见下面关于 GET_GOT的说明)。这样GOT(NAME) = .L_NAME + r14 = .L_NAME + .LCTOC1 = NAME_OFFSET - 0x8000 + TABLE_START + 0x8000 = NAME_OFFSET + TABLE_START,也就是NAME所在表项的地址。这样,通过查表,就可以找到当初存储在表中的名字为NAME的offset值。


GET_GOT用于初始化GOT表。首先程序跳转到标号为“1”的地址处(bl 1f),然后将lr的值赋值给r14(此时lr的值为“1”的地址值)。然后另r0 = 0b - 1b(r14),0b为“0”处的地址值,1b为“1”处的地址值。这样r0就等于“0”处的值,也就是.LCTOC1-1f。最后r14 = r0 + r14 = .LCTOC1 - 1f + 1f = .LCTOC1,也就是等于GOT表的最高地址。

**************************************************************************************************************************************************************************************

继续看start.S中_start_e500,这个函数比较长,我们捡取主要部分分析:

上电之后,MMU只配置了4k空间,在这4k空间内必须初步配置MMU,使MMU能够映射u-boot真实的有效地址空间。

   
     71 /*
     72  * e500 Startup -- after reset only the last 4KB of the effective
     73  * address space is mapped in the MMU L2 TLB1 Entry0. The .bootpg
     74  * section is located at THIS LAST page and basically does three
     75  * things: clear some registers, set up exception tables and
     76  * add more TLB entries for 'larger spaces'(e.g. the boot rom) to
     77  * continue the boot procedure.
     78
     79  * Once the boot rom is mapped by TLB entries we can proceed
     80  * with normal startup.
     81  *
     82  */
     83
     84     .section .bootpg,"ax"
     85     .globl _start_e500
     86
     87 _start_e500:
     88     mfspr   r0, PVR
     89     lis r1, PVR_85xx_REV1@h
     90     ori r1, r1, PVR_85xx_REV1@l
     91     cmpw    r0, r1  
     92     bne 1f        //条件向下跳到标号1处执行
     93
     94     /* Semi-bogus errata fixup for Rev 1 */
     95     li  r0,0x2000
     96     mtspr   977,r0
     97
     98     /*
     99      * Before invalidating MMU L1/L2, read TLB1 Entry 0 and then
    100      * write it back immediately to fixup a Rev 1 bug (Errata CPU4)
    101      * for this initial TLB1 entry 0, otherwise the TLB1 entry 0
    102      * will be invalidated (incorrectly).
    103      */
    104     lis r2,0x1000
    105     mtspr   MAS0,r2
    106     tlbre
    107     tlbwe
    108     isync
    109
    110 1:
    111     /*
    112      * Clear and set up some registers.
    113      * Note: Some registers need strict synchronization by
    114      * sync/mbar/msync/isync when being "mtspr".
    115      * BookE: isync before PID,tlbivax,tlbwe
    116      * BookE: isync after MSR,PID; msync_isync after tlbivax & tlbwe
    117      * E500:  msync,isync before L1CSR0
    118      * E500:  isync after BBEAR,BBTAR,BUCSR,DBCR0,DBCR1,HID0,HID1,
    119      *    L1CSR0, L1CSR1, MAS[0,1,2,3,4,6],MMUCSR0, PID[0,1,2],
    120      *    SPEFCSR
    121      */
    122
    123     /* invalidate d-cache */
    131     /* disable d-cache */  禁止数据cache
 	
    135     /* invalidate i-cache */    141     /* disable i-cache */ 禁止指令cache

    146     /* clear registers */
  

    185     /* Setup interrupt vectors */  建立中断向量表
    222     /* Invalidate MMU L1/L2*/  禁止MMU
    231     /* Invalidate all TLB0 entries.*/ 禁止TLB0

    237     /*
    238      * To avoid REV1 Errata CPU6 issues, make sure
    239      * the instruction following tlbivax is not a store.
    240      */
    241
    242     /*
    243      * After reset, CCSRBAR is located at CFG_CCSRBAR_DEFAULT, i.e.
    244      * 0xff700000-0xff800000. We need add a TLB1 entry for this 1MB
    245      * region before we can access any CCSR registers such as L2
    246      * registers, Local Access Registers,etc. We will also re-allocate
    247      * CFG_CCSRBAR_DEFAULT to CFG_CCSRBAR immediately after TLB1 setup.
    248      *
    249      * Please refer to board-specif directory for TLB1 entry configuration.
    250      * (e.g. board/<yourboard>/init.S)
    251      *
    252      */

    299     /* set up local access windows, defined at board/<boardname>/init.S */
    300     lis r7,CFG_CCSRBAR@h
    301     ori r7,r7,CFG_CCSRBAR@l
    302
    303     bl  law_entry

    326     b   _start    //最后跳转到_start

_start函数将做一些基本寄存器的配置,初始化堆栈等,最后会调到以下函数:

bl cpu_init_f //对CPU做一些初始化,对BR和BO做一些预初始化
bl icache_enable // 使能指令cache
bl board_init_f

cpu_init_f函数

      该函数是系统执行的第一个C语言的函数,主要是做一些CPU 寄存器的初始化,其中最重要的部分是初始化Local Access Windows的值、Local Bus上的片选BR,OR的值和配置MMU的LTB1、LTB0。这些值需要在/include/configs/PQ37PC_8560.h中配置好。

board_init_f函数

      该函数为板级初始化的第一个函数,会对板子上很多硬件外设做初始化,其中最重要的为init_sequence数组,该数组里面包含了很多板级的硬件初始化函数,在board_init_f函数中会依次的调用该数组中的函数去初始化各个硬件,可以看到时钟,串口,控制台,内存等初始化函数的调用。此时代码仍在ROM中运行。

void board_init_f (ulong bootflag)
{
  	/* Pointer is writable since we allocated a register for it */
 	gd = (gd_t *) (CFG_INIT_RAM_ADDR + CFG_GBL_DATA_OFFSET);
 	/* compiler optimization barrier needed for GCC >= 3.4 */
 	__asm__ __volatile__("": : :"memory");
	//初始化gd结构体
	//调用init_sequence数组中的函数
 	for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) 
 	{
  		if ((*init_fnc_ptr) () != 0) 
  		{
   			hang ();
  		}
 	}
  	len = (ulong)&_end - CFG_MONITOR_BASE;
 	addr = CFG_SDRAM_BASE + gd->ram_size;//top of ram

	 /* round down to next 4 kB limit ,对齐准则*/
 	addr &= ~(4096 - 1);
 	debug ("Top of RAM usable for U-Boot at: %08lx\n", addr);

 	/* 在RAM的末端为内核日志缓冲区、保护RAM、LCD 帧缓冲区、monitor code和bd结构体预留空间,注意数据对齐*/
 	addr -= len;
 	addr &= ~(4096 - 1);

 	debug ("Reserving %ldk for U-Boot at: %08lx\n", len >> 10, addr);

	 /*为malloc预留空间 */
 	addr_sp = addr - TOTAL_MALLOC_LEN;
 	debug ("Reserving %dk for malloc() at: %08lx\n",TOTAL_MALLOC_LEN >> 10, addr_sp);

 	/*为bd_t和gd_t预留空间*/
 	addr_sp -= sizeof (bd_t);
 	bd = (bd_t *) addr_sp;
 	gd->bd = bd;
 	debug ("Reserving %d Bytes for Board Info at: %08lx\n", sizeof (bd_t), addr_sp);
 	addr_sp -= sizeof (gd_t);
 	id = (gd_t *) addr_sp;
 	debug ("Reserving %d Bytes for Global Data at: %08lx\n",sizeof (gd_t), addr_sp);

 	/*增大栈空间:与SP之间预留16bytes的空间,注意数据对齐, Clear initial stack frame */
 	addr_sp -= 16;
 	addr_sp &= ~0xF;
 	s = (ulong *)addr_sp;
	 *s-- = 0;
 	*s-- = 0;
 	addr_sp = (ulong)s;
 	debug ("Stack Pointer at: %08lx\n", addr_sp);

	/*将局部变量保存到bd_t和gd_t中*/

 	bd->*=....;
 	debug ("New Stack Pointer is: %08lx\n", addr_sp);
 
 	memcpy (id, (void *)gd, sizeof (gd_t));
    	//将flash中的uboot代码,数据及bss搬运到ram
 	relocate_code (addr_sp, id, addr);
 	/* NOTREACHED - relocate_code() does not return */
}





relocate_code函数


      到目前为止,boot代码都是在Flash中运行,但是代码最终是要到RAM中运行的,在上面的board_init_f函数中已经将RAM初始化好了,具备了在RAM中运行程序的能力,现在relocate_code函数需要做两个事情:

      1)从Flash中拷贝u-boot的代码到RAM;

      2)记下现在执行代码的偏移,跳转到RAM中相应的位置执行。

relocate_code重新调回到汇编代码中执行一些操作后,调用board_init_r

board_init_r函数


      该函数为板级初始化的第二阶段,主要是初始化PCI,PCIE,网口,Flash等设备,关闭看门狗,把前面分配给dcache做堆栈的空间解锁,还给cache。在一切设备都初始化好后,便会进main_loop的死循环中。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值