u-boot启动过程分解 1

时间:2022-09-30 16:36:12

这篇文章是tiny6410到手以后分析的第一个程序后写下的记录,由于各种原因整理一半以后就被搁置了,有些未解决问题,

U-Boot启动代码分析第一部分(汇编语言):

       主要完成cpu初始化所涉及到寄存器设置,并将第二部分C语言代码复制到RAM中;

       此部分内容起始于/cpu/s3c64xx/S3c6410/start.S

涉及文件:

       /cpu/s3c64xx/S3c6410/start.S

       /board/Samsung/Mini6410/lowlevel_init.S   

       Note:此文件夹为FriendlyARM针对Mini6410修改代码,移植于Smdk6410

       /include/s3c6410.h

定义中断向量表

/cpu/start.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

 

_undefined_instruction:

       .word undefined_instruction

_software_interrupt:

       .word software_interrupt

_prefetch_abort:

       .word prefetch_abort

_data_abort:

       .word data_abort

_not_used:

       .word not_used

_irq:

       .word irq

_fiq:

       .word fiq

/*

 * exception handlers

 */

       .align       5

undefined_instruction:

       get_bad_stack

       bad_save_user_regs

       bl     do_undefined_instruction

 

       .align       5

software_interrupt:

       get_bad_stack_swi

       bad_save_user_regs

       bl     do_software_interrupt

在跳转中断服务程序前,首先要有两个宏操作,一个是对stack的操作,一个是对用户reg保存;

       .align       5

prefetch_abort:

       get_bad_stack

       bad_save_user_regs

       bl     do_prefetch_abort

 

       .align       5

data_abort:

       get_bad_stack

       bad_save_user_regs

       bl     do_data_abort

 

       .align       5

not_used:

       get_bad_stack

       bad_save_user_regs

       bl     do_not_used

 

#ifdef CONFIG_USE_IRQ

 

       .align       5

irq:

       get_irq_stack

       irq_save_user_regs

       bl     do_irq

       irq_restore_user_regs

 

       .align       5

fiq:

       get_fiq_stack

       /* someone ought to write a more effiction fiq_save_user_regs */

       irq_save_user_regs

       bl     do_fiq

       irq_restore_user_regs

 

#else

 

       .align       5

irq:

       get_bad_stack

       bad_save_user_regs

       bl     do_irq

 

       .align       5

fiq:

       get_bad_stack

       bad_save_user_regs

       bl     do_fiq

 

#else

 

       .align       5

irq:

       get_bad_stack

       bad_save_user_regs

       bl     do_irq

 

       .align       5

fiq:

       get_bad_stack

       bad_save_user_regs

       bl     do_fiq

对应的中断子程序将在 /cpu/$(ARCH)/interrupts.c中执行

存储器映射定义:

_TEXT_PHY_BASE:

       .word      CFG_PHY_UBOOT_BASE

 

.globl _armboot_start

_armboot_start:

       .word _start

 

/*

 * These are defined in the board-specific linker script.

 */

.globl _bss_start

_bss_start:

       .word __bss_start

 

.globl _bss_end

_bss_end:

       .word _end

 

#ifdef CONFIG_USE_IRQ

/* IRQ stack memory (calculated at run-time) */

 

.globl IRQ_STACK_START

IRQ_STACK_START:

       .word      0x0badc0de

 

/* IRQ stack memory (calculated at run-time) */

 

.globl FIQ_STACK_START

FIQ_STACK_START:

       .word 0x0badc0de

#endif


上电后执行动作:

1. 上电后执行reset:设置CPUSVC模式:

reset:

       /*

        * set the cpu to SVC32 mode

        */

       mrs  r0,cpsr

       bic   r0,r0,#0x1f

       orr   r0,r0,#0xd3

       msr  cpsr,r0

2. CPU初始化循环:

       2.1 无效caches,关闭cachesMMU

cpu_init_crit:

       /*

        * flush v4 I/D caches

        */

       mov r0, #0

       mcr p15, 0, r0, c7, c7, 0      /* flush v3/v4 cache */

       mcr p15, 0, r0, c8, c7, 0      /* flush v4 TLB */

       /*

        * disable MMU stuff and caches

        */

       mrc p15, 0, r0, c1, c0, 0

       bic   r0, r0, #0x00002300      @ clear bits 13, 9:8 (--V- --RS)

       bic   r0, r0, #0x00000087      @ clear bits 7, 2:0 (B--- -CAM)

       orr   r0, r0, #0x00000002      @ set bit 2 (A) Align

       orr   r0, r0, #0x00001000      @ set bit 12 (I) I-Cache

       mcr p15, 0, r0, c1, c0, 0

 

       /* Peri port setup bank0 and bank1 set to 1 as 16bits mode*/

       ldr   r0, =0x70000000    /*70000000-SROM bus width and wait control*/

       orr   r0, r0, #0x13         /*bank0 and bank1 setting 16-bit*/

    mcr p15,0,r0,c15,c2,4  

@ 256M(0x70000000-0x7fffffff)

注:此处无效掉I/D caches,关闭cachesMMU,同时设置用于bank0bank1的数据总线宽度控制器为16-bit模式;


S3c64xxs3c24xx此处初始化有些诧异:在S3c64xx中强制进行了cpu_init_crit

s3c24xx中首先判断,若未定义CONFIG_SKIP_LOWLEVEL_INIT,则进入cpu_init_crit

2.2 配置ONENAND控制器:

#ifdef CONFIG_BOOT_ONENAND

      ldr   r0, =0x70000000           @ onenand controller setup

      orr   r0, r0, #0x100000

      ldr   r1, =0x4000

      orr   r1, r1, #0xe0

      str   r1, [r0]

 

#if defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430)

      orr   r0, r0, #300           @ disable watchdog

      mov r1, #1

      str   r1, [r0]

 

      mov r1, #0x23000000           @ start buffer register

      orr   r1, r1, #0x30000

      orr   r1, r1, #0xc800

#else

      mov r1, =0x20000000           @ start buffer register

      orr   r1, r1, #0xc30000

      orr   r1, r1, #0xc800

#endif

 

      sub  r0, r1, #0x0400             @ start address1 register

 

      ldr   r2, [r1, #0x84]              @ ecc bypass

      orr   r2, r2, #0x100

      str   r2, [r1, #0x84]

 

      mov r3, #0x0         @ DFS, FBA

      str   r3, [r0, #0x00]

      str   r3, [r0, #0x04]              @ select dataram for DDP as 0

 

      mov r4, #0x104             @ interrupt register

      mov r5, #0x0002           @ FPA, FSA

      mov r6, #0x0800           @ BSA

 

onenand_bl1_load:

      str   r5, [r0, #0x1c]              @ save FPA, FSA

      orr   r6, r6, #0x02         @ BSC

      str   r6, [r1, #0x00]              @ save BSA, BSC

      str   r3, [r1, r4]             @ clear interrupt

      str   r3, [r1, #0x80]              @ write load command

 

      mov r7, #0x100             @ need small delay

 

onenand_wait_loop1:

      subs r7, r7, #0x1

      bne  onenand_wait_loop1

 

      add  r5, r5, #0x2           @ next FPA, FSA

      sub  r6, r6, #0x2

      add  r6, r6, #0x200        @ next BSA

      cmp r5, #0x8

      bne  onenand_bl1_load

#endif

若启动定义了CONFIG_BOOT_ONENAND,对ONENAND flash初始化,此类型ROM暂时不涉及,内容跳过。

 

2.3 调用lowlevel_init对底层的初始化:

lowlevel_init:

       mov r12, lr     /*保存程序返回地址,待调用执行完成后返回原地址*/

 

       /* LED on only #8 */

       ldr   r0, =ELFIN_GPIO_BASE

       ldr   r1, =0x55540000

       str   r1, [r0, #GPNCON_OFFSET]

 

       ldr   r1, =0x55555555

       str   r1, [r0, #GPNPUD_OFFSET]

 

       ldr   r1, =0xf000

       str   r1, [r0, #GPNDAT_OFFSET]

 

       ldr   r0, =ELFIN_GPIO_BASE

       ldr   r1, =0x1

       str   r1, [r0, #GPECON_OFFSET]

       ldr   r1, =0x0

       str   r1, [r0, #GPEDAT_OFFSET]

 

       ldr   r0, =ELFIN_GPIO_BASE

       ldr   r1, =0x2A5AAAAA

       str   r1, [r0, #GPPCON_OFFSET]

       ldr   r1, =0x0

       str   r1, [r0, #GPPDAT_OFFSET]

      

 

       ldr   r1, =0x55555555

       str   r1, [r0, #MEM1DRVCON_OFFSET]

       /*以上为配置GPNIOGPEIOGPPIO端口输出*/

       /* Disable Watchdog关闭看门狗 */

       ldr   r0, =0x7e000000           @0x7e004000

       orr   r0, r0, #0x4000

       mov r1, #0

       str   r1, [r0]

 

       @ External interrupt pending clear清除外部中断标志

       ldr   r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET)    /*EINTPEND*/

       ldr   r1, [r0]

       str   r1, [r0]

 

       ldr   r0, =ELFIN_VIC0_BASE_ADDR        @0x71200000

       ldr   r1, =ELFIN_VIC1_BASE_ADDR        @0x71300000

 

       @ Disable all interrupts (VIC0 and VIC1)关闭中断

       mvn r3, #0x0

       str   r3, [r0, #oINTMSK]

       str   r3, [r1, #oINTMSK]

 

       @ Set all interrupts as IRQ设置所有中断类型为IRQ

       mov r3, #0x0

       str   r3, [r0, #oINTMOD]

       str   r3, [r1, #oINTMOD]

 

       @ Pending Interrupt Clear清除中断标志

       mov r3, #0x0

       str   r3, [r0, #oVECTADDR]

       str   r3, [r1, #oVECTADDR]

       /*

* 以下为系统时钟初始化、串口初始化、NAND flash初始化调用,均为

* 对寄存器相应位设置,参考用户手册

*/

/*

* init system clock :关闭看门狗、屏蔽中断、设置FCLK:HCLK:PCLK*钟比例

*/

       bl system_clock_init

 

       /* for UART */

       bl uart_asm_init

 

#if defined(CONFIG_NAND)

       /* simple init for NAND */

       bl nand_asm_init

#endif

/*

* 猜测:此部分已屏蔽判断初始化地址,转为跳转至电源唤醒模式处

*/

#if 0

       ldr   r0, =0xff000fff

       bic   r1, pc, r0        /* r0 <- current base addr of code */

       ldr   r2, _TEXT_BASE         /* r1 <- original base addr in ram */

       bic   r2, r2, r0        /* r0 <- current base addr of code */

       cmp       r1, r2                    /* compare r0, r1                  */

       beq  1f                  /* r0 == r1 then skip sdram init   */

#endif

 

       bl     mem_ctrl_asm_init

 

#if 1

        ldr     r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)

        ldr     r1, [r0]

        bic     r1, r1, #0xfffffff7

        cmp     r1, #0x8

        beq     wakeup_reset

 

#endif

/*

* UART channel 0 transmit buffer register setting 0x4b4b4b4b

*/

1:

       ldr   r0, =ELFIN_UART_BASE

       ldr   r1, =0x4b4b4b4b

       str   r1, [r0, #UTXH_OFFSET]

 

       mov lr, r12

       mov pc, lr

 

此刻lowlevle_inti执行完成,返回start.Sline240


2.4 判断启动地址并复制程序至RAM

      ldr   r0, =0xff000fff

      bic   r1, pc, r0        /* r0 <- current base addr of code */

      ldr   r2, _TEXT_BASE         /* r1 <- original base addr in ram */

      bic   r2, r2, r0        /* r0 <- current base addr of code */

      cmp     r1, r2                  /* compare r0, r1                  */

      beq     after_copy             /* r0 == r1 then skip flash copy   */

 

此处判断程序运行地址是否在RAM中,如果没有,复制启动程序否则调用after_copy

 

2.5 不同ROM设备的程序复制工作

        Line252-line364针对NOR FLASH/NAND FLASH/MOVINAND FLASH/ONE NAND FLASH设备启动的代码复制进行区分设置,此部分代码量较大,不再附上;

 

2.6 copy code 后进行的工作(line365-line393):

       1. GPP输出0xc00why??)

       2. enable domain accesswhy??)

3. 设置TTB寄存器

4. 使能MMU

 

2.7 清除RAM空间(line397-line423):

       1. 通过_TEXT_BASE地址推算出映像的起始地址;

       2. 设置_bss_start_bss_end地址;

       3. 清除bss空间;

 

2.8 程序跳转到_start_armboot处。


U-Boot启动第二部分(C语言):

       继续汇编程序后的_start_armboot衔接部分,此部分已复制到RAM中,转为C语言启动过程继续;

       此部分内容起始于:/Lib_arm/Board.c

       涉及文件:

              /Lib_arm/Board.c


C语言的入口函数:/board/Lib_arm/board.c void start_armboot(void);

1.       定义所需变量并为全局变量指针gd初始化:

      init_fnc_t **init_fnc_ptr;

      char *s;

#ifndef CFG_NO_FLASH

      ulong size;

#endif

 

#if defined(CONFIG_VFD) || defined(CONFIG_LCD)

      unsigned long addr;

#endif

 

#if defined(CONFIG_BOOT_MOVINAND)

      uint *magic = (uint *) (PHYS_SDRAM_1);

#endif

 

      /* Pointer is writable since we allocated a register for it */

#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */

      ulong gd_base;

 

      gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);

#ifdef CONFIG_USE_IRQ

      gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);

#endif

      gd = (gd_t*)gd_base;

#else

      gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

#endif

 

      /* compiler optimization barrier needed for GCC >= 3.4 */

      __asm__ __volatile__("": : :"memory");

 

      memset ((void*)gd, 0, sizeof (gd_t));

      gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

      memset (gd->bd, 0, sizeof (bd_t));

 

monitor_flash_len = _bss_start - _armboot_start;

       此处定义init_fnc_ptrs指针;

如果未定义CFG_NO_FLASH定义size

如果定义CONFIG_VFDCONFIG_LCD则定义addr

如果定义CONFIG_BOOT_MOVI_NAND则定义magic并初始化为PHYS_SDRAM_1

 

如果定义CONFIG_MEMORY_UPPER_ CODE则定义gd_base并初始化为SDRAM起始地址;如果定义CONFIG_USE_IRQ重新计算,减去IRQFIQ长度,并将gd_base计算值作为地址对gd初始化;

gd指针初始化设置内存空间;

gd->bd指针初始化地址,并设置内存空间;