注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
416561760的个人空间 https://home.eeworld.com.cn/space-uid-116357.html [收藏] [复制] [分享] [RSS]
日志

U-boot移植心得《二》

已有 2818 次阅读2013-8-5 15:45 |个人分类:u-boot

U-boot移植心得《二》

下面我开始分析start.S,在分析之前,我们要了解,u-boot引导linux加载的过程分为2个阶段,第一个阶段的终极目的就是设置好各种硬件环境,将代码从nor flashNAND flash中拷贝到外部RAM中,然后跳转到C函数中去。第二阶段的终极目标就是在RAM中运行u-boot代码,执行u-boot的各种命令,加载引导linux映像并,最终跳到linux系统中,至此u-boot的生命结束。

好,我们看第一阶段的代码:

_start:     b       reset      @u-boot首先从_start处开始运行,跳转到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

 

reset:

/ * set the cpu to SVC32 mode   cpu为管理模式    */

mrs r0,cpsr

bic   r0,r0,#0x1f

orr  r0,r0,#0xd3

msr cpsr,r0 

/* turn off the watchdog */     关闭看门狗,就是操作相关寄存器

#if defined(CONFIG_S3C2400)

# define pWTCON        0x15300000

# define INTMSK         0x14400008    /* Interupt-Controller base addresses */

# define CLKDIVN       0x14800014    /* clock divisor register */

#elif defined(CONFIG_S3C2410)

# define pWTCON        0x53000000

# define INTMOD     0X4A000004

# define INTMSK         0x4A000008   /* Interupt-Controller base addresses */

# define INTSUBMSK  0x4A00001C

# define CLKDIVN       0x4C000014   /* clock divisor register */

#endif

 

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)

ldr     r0, =pWTCON

mov     r1, #0x0

str     r1, [r0]

 

   /* mask all IRQs by setting all bits in the INTMR – default */

③屏蔽中断,此处只设置了2410,如果是2440或其他处理器型号,需要设置修改

mov r1, #0xffffffff

ldr   r0, =INTMSK

str   r1, [r0]

# if defined(CONFIG_S3C2410)

ldr   r1, =0x3ff

ldr   r0, =INTSUBMSK

str   r1, [r0]

# endif

没经修改的u-boot源码中没有屏蔽此处,此处的功能是设置2410的时钟,如果是2410的开发板此处可不改动,但我们移植的这个u-boot要同时兼容24102440,所以讲此处屏蔽掉重新写一个时钟初始化的函数clock_init,让其即支持2410又支持2440.放到代码后面某个合适的地方去。

#if 0            

       /* FCLK:HCLK:PCLK = 1:2:4 */

       /* default FCLK is 120 MHz ! */

       ldr   r0, =CLKDIVN

       mov r1, #3

       str    r1, [r0]

#endif

比较_start_TEXT_BASE的地址判断代码运行在外部RAM下还是内部ROM,如果代码地址相同,则目前运行在RAM中,忽略此步骤中的所有工作。否则运行在内部ROM中,跳转到cpu_init_crit

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

       adr  r0, _start              /* r0 <- current position of code   */

       ldr   r1, _TEXT_BASE        /* test if we run from flash or RAM */

       cmp     r0, r1                  /* don't reloc during debug         */

blne cpu_init_crit   /* 如果运行在ROM中(内部4K),则跳转到 cpu_init_crit ,代码见下节*/

#endif

/* Set up the stack */

stack_setup:  分配堆栈空间,

       ldr   r0, _TEXT_BASE        /* upper 128 KiB: relocated uboot   */

       sub  r0, r0, #CFG_MALLOC_LEN    /* malloc area  */

       sub  r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */

 

#ifdef CONFIG_USE_IRQ

       sub  r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

       sub  sp, r0, #12            /* leave 3 words for abort-stack    */

 

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

    bl clock_init  设置系统时钟,代码见下节

#endif   

重新定位,判断代码在不在RAM中运行,如果不在,则复制代码到RAM中运行。

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

relocate:                      /* relocate U-Boot to RAM      */

       adr  r0, _start              /* r0 <- current position of code   */

       ldr   r1, _TEXT_BASE        /* test if we run from flash or RAM */

       cmp     r0, r1                  /* don't reloc during debug         */

       beq     clear_bss  判断,跳过复制代码程序

      

       ldr   r2, _armboot_start

       ldr   r3, _bss_start

       sub  r2, r3, r2              /* r2 <- size of armboot            */

#if 1

       bl  CopyCode2Ram    /* r0: source, r1: dest, r2: size */ 复制NANDNOR代码到RAM,代码见下节。

#else     下面else下的代码不执行,跳过

       add r2, r0, r2              /* r2 <- source end address         */

 

copy_loop:

       ldmia     r0!, {r3-r10}         /* copy from source address [r0]    */

       stmia      r1!, {r3-r10}         /* copy to   target address [r1]    */

       cmp r0, r2                    /* until source end addreee [r2]    */

       ble   copy_loop

#endif

#endif     /* CONFIG_SKIP_RELOCATE_UBOOT */

 

clear_bss:   bss

       ldr   r0, _bss_start        /* find start of bss segment        */

       ldr   r1, _bss_end         /* stop here                        */

       mov       r2, #0x00000000           /* clear                            */

 

clbss_l:str      r2, [r0]          /* clear loop...                    */

       add r0, r0, #4

       cmp r0, r1

       ble   clbss_l

 

SetLoadFlag:

       /* Set a global flag, PreLoadedONRAM */

       adr  r0, _start              /* r0 <- current position of code   */

       ldr   r1, _TEXT_BASE        /* test if we run from flash or RAM */

       cmp     r0, r1                  /* don't reloc during debug         */

       ldr r2, =PreLoadedONRAM

       mov r3, #1

       streq r3, [r2]

 

 

关于:adr     r0, _start              /* r0 <- current position of code   */

       ldr   r1, _TEXT_BASE        /* test if we run from flash or RAM */

       cmp     r0, r1                  /* don't reloc during debug         */

看了下网上的帖子,adr指令,网上很多人被这这个指令弄郁闷,我看杜春雷的<<arm体系结构与编程>>P143,这个指令是基于PC或者寄存器的,读到是地址无关的,一般被编译器替换为SUB r0, pc,#offset ,不要理解为读取符合表中_start符号的地址(0x33f80000).在我们上电开始执行时,pc0开始,所以现在r0值为0 +offset,不等于_TEXT_BASE(0x33f80000).接下来要用到链接时确定的符号地址了,_armboot_start(0x33f80000)., _bss_start(0x33f97954)这些可以在u-boot.map里面的看到, size of armboot =0x33f97954-0x33f80000 ,_start:0x0 (norflsh).text ,.data的代码往SDRAM_TEXT_BASE确定的地址: 0x33f80000搬运.s3c2410SDRAM基地址是0x3000_0000,由于uboot支持的这个board SDRAM64M,(0x3000_0000---0x3400_0000),所以把u-boot.bin搬运到内存的高端地址.然后跳到内存中执行,提高速度.
之后就relocate &#61664; stack_setup &#61664; clear_bss &#61664; ldr pc, _start_armboot ( ROM&#61664;RAM)
_start_armboot: .word start_armboot ( u-boot-1.1.4\lib_arm\board.c)
stack_setup , clear_bss
设置堆栈清bss,都是为进入C语言做初始化准备,通过对start_armboot链接后以及把这个函数地址已经绑定在RAM,当执行完ldr pc, label 指令,程序将从标号绑定地址开始执行,从而实现了从地址无关程序到地址相关的转变,我们做代码搬移也是为了跳转做准备,如果没有搬移,直接访问地址相关,由于RAM中都是随机值,一跳转就马上飞了.当进入start_armboot C函数,剩下的都没什么难度了.可以慢分析源码搞定.2410没有remap寄存器, relocate时候要容易些,remap寄存器的芯片在relocate时候进行remap会让情况更复杂些.不过原理都差不多.
在进入board.c,uboot还做了一次代码搬运如下,大概如下图,不过分两种,一种是把pc机传的image通过串口或者网络传到内存开始执行,或者从nandflash里把应用搬到内存开始执行,不过原理都差不多.

 

#if 0   屏蔽不执行,跳过

       /* try doing this stuff after the relocation */

       ldr     r0, =pWTCON

       mov     r1, #0x0

       str     r1, [r0]

 

       /*

        * mask all IRQs by setting all bits in the INTMR - default

        */

       mov r1, #0xffffffff

       ldr   r0, =INTMR

       str   r1, [r0]

 

       /* FCLK:HCLK:PCLK = 1:2:4 */

       /* default FCLK is 120 MHz ! */

       ldr   r0, =CLKDIVN

       mov r1, #3

       str   r1, [r0]

       /* END stuff after relocation */

#endif

 

       ldr   pc, _start_armboot   进入u-boot第二阶段,跳转到start_armboot

 

_start_armboot:   .word start_armboot

 

cpu/arm920t/start.S中,将text relocate Ram后,其代码段的最后1行有条语句:

    ldr pc _start_armboot

    _start_armboot: .word start_armboot

     start_armboot是一个函数指针,这个symbol对应了符号表里的函数地址,这个函数是一个C语言的函数,他就是u-bootstage2的入口点,这个stage2应该是在RAM里面执行的。问题就来了,既然我们只是手动将text relocate到了RAM里面,此时FLASHRAM里面都有start_armboot的代码,为什么程序就要跳转到RAM里,而不是依然在FLASH里?

     我最初感觉除非是ld在连接的时候,修改了符号表里的内容,指定start_armboot符号的地址是RAM里的地址,这样只要我们执行时取其地址,取到的肯定是RAM中的地址。

     经查阅资料,有如下一段解释:

    转自:http://www.mail-archive.com/u-boot-users@lists.sourceforge.net/msg04018.html

    ----- Original Message ----

    > > > > From: Vishal Oliyil Kunnil

    > > > > To: Tiju

    > > > >

    > > > > Sent: Monday, 31 March, 2008 2:58:12 PM

    > > > > Subject: Re: [U-Boot-Users] s3c2440 -- serial_init

    > > > >

    > > > > TEXT_BASE is the address for which u-boot is linked for.If you take an

    > > > > objdump of u-boot

    > > > > elf, you will see that it links for address beginning with that

    > > > > specified by TEXT_BASE.

    > > > > Meaning, you link for the address thus specified.

    > > > > Typically the binary will be run from the reset vector of the

    > > > > processor, which is not necessarily

    > > > > TEXT_BASE : say 0x0 flash address. U-boot starts executing from the

    > > > > reset vector,

    > > > > relocates to RAM and since it is linked for TEXT_BASE, the ldr pc,

    > > > > _start_armboot

    > > > > will branch to the start_armboot which is in RAM.

    > > > > -------snip - start.S --------------

    > > > > ldr pc, _start_armboot

    > > > > _start_armboot: .word start_armboot

    > > > > -------snip - start.S --------------

    > > > > Regards,

    > > > > Vishal 上面的意思是说,我们在u-bootC代码的编译环境里制定了TEXT_BASE的值,然后所有生成的C函数的可执行代码都是以TEXT_BASE作为连接基地址,而不是我们的0x00000000(u-bootFLASH开始执行的起始地址),也就是说这是我们u-boot的代码中在FLASH里,除了最开始的一部分汇编码是在FLASH里执行的,其他由C语言实现的部分都只能在RAM里执行,因为我们给他们定好的基地址就是RAM的地址。

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章