最近一直在学习编写Bootloader,对于Bootloader也有了一些简单的认识。Vboot作为一个非常精简的bootloader程序,是十分值得刚入门的学习。把Vboot的源代码认真分析一遍之后,再去看其他bootloader,比如supervivi、u-boot等,应该就会好理解一些。

值得注意的是,vboot只有最基本的内核引导功能,基于S3C2440,从vboot启动。其他CPU芯片如果需要使用vboot需要根据实际情况来进行改写。

为了阅读源代码,我使用了一款工具Source Insight,目前还比较生疏,正在慢慢熟悉中。

vboot主要代码有三个:head.s main.c nand.c,其余文件都是一些头文件。从文件名称上来看,head.s由汇编语言编写,作为bootloader引导的第一个阶段,主要实现了硬件初始化(包括禁用看门狗,禁用所有中断,初始化系统时钟等)、初始化RAM为第二阶段做准备,初始化串口;main.c作为bootloader第二个阶段的入口。主要实现功能为:使能Dcache和Icache,初始化一些IO口,初始化Nand Flash,设置了内核映像在nand flash的起始地址和大小,还有设置内核映像被拷贝到ram的起始地址,然后开始从Nand Flash读,加载内核等。

  1. SECTIONS {    
  2.   . = 000000;   
  3.   .myhead ALIGN(0): {*(.text.FirstSector)}   
  4.   .text ALIGN(512): { *(.text) }   
  5.   .bss ALIGN(4)  : { *(.bss*)  *(COMMON) }   
  6.   .data ALIGN(4) : { *(.data*) *(.rodata*) }   
  7. }   

程序入口位于text.FirstSector这个段里(因为程序是从nand flash的0地址开始执行的),它在head.S文件里定义:

  1. @   
  2. @ Exception vector table (physical address = 0×00000000)   
  3. @   
  4.     .section .text.FirstSector   
  5.     .globl first_sector   
  6.   
  7. first_sector:   
  8. 0×00: Reset   
  9.     b   Reset   
  10.   
  11. 0×04: Undefined instruction exception   
  12. UndefEntryPoint:   
  13.     b   UndefEntryPoint   
  14.   
  15. 0×08: Software interrupt exception   
  16. SWIEntryPoint:   
  17.     b   SWIEntryPoint   
  18.   
  19. 0x0c: Prefetch Abort (Instruction Fetch Memory Abort)   
  20. PrefetchAbortEnteryPoint:   
  21.     b   PrefetchAbortEnteryPoint   
  22.   
  23. 0×10: Data Access Memory Abort   
  24. DataAbortEntryPoint:   
  25.     b   DataAbortEntryPoint   
  26.   
  27. 0×14: Not used   
  28. NotUsedEntryPoint:   
  29.     b   NotUsedEntryPoint   
  30.   
  31. 0×18: IRQ(Interrupt Request) exception   
  32. IRQEntryPoint:   
  33.     b   IRQHandle   
  34.   
  35. 0x1c: FIQ(Fast Interrupt Request) exception   
  36. FIQEntryPoint:   
  37.     b   FIQEntryPoint   
  38.   
  39. @0×20: Fixed address global value. will be replaced by downloader.   
  40.   
  41.     .long ZBOOT_MAGIC   
  42.     .byte OS_TYPE, HAS_NAND_BIOS, (LOGO_POS & 0xFF), ((LOGO_POS >>8) &0xFF)   
  43.     .long OS_START   
  44.     .long OS_LENGTH   
  45.     .long OS_RAM_START   
  46.     .string LINUX_CMD_LINE  

其中7到37行表示安装异常向量表,在下面将会看到reset向量的具体执行代码,如下:

  1. .section .text   
  2. eset:   
  3. @ disable watch dog timer (禁用看门狗,向0×53000000寄存器中写值即可)  
  4. mov r1, #0×53000000  
  5. mov r2, #0×0  
  6. str r2, [r1]   
  7.   
  8. @ disable all interrupts  (禁用所有中断,关闭掩码寄存器中所有位即可,也就是赋1)
  9. mov r1, #INT_CTL_BASE   
  10. mov r2, #0xffffffff  
  11. str r2, [r1, #oINTMSK]   
  12. ldr r2, =0x7ff  
  13. str r2, [r1, #oINTSUBMSK]      
  14.   
  15. @ initialise system clocks   (初始化系统时钟)
  16. mov r1, #CLK_CTL_BASE   
  17. mvn r2, #0xff000000  
  18. str r2, [r1, #oLOCKTIME]   
  19.   
  20. mov r1, #CLK_CTL_BASE   
  21. ldr r2, clkdivn_value   
  22. str r2, [r1, #oCLKDIVN]   
  23.   
  24.   
  25. @set the asynchronous   (到这里时钟即将分为三个频率,所以需要设置异步总线模式,应该是用到了协寄存器)
  26. mrc p15, 0, r1, c1, c0, 0       @ read ctrl register    
  27. orr r1, r1, #0xc0000000     @ Asynchronous     
  28. mcr p15, 0, r1, c1, c0, 0       @ write ctrl register   
  29.   
  30. mov r1, #CLK_CTL_BASE   
  31.   
  32. ldr r2, =S3C2440_UPLL_48MHZ_Fin12MHz   
  33. str r2, [r1, #oUPLLCON]   
  34.   
  35.   @(连续多个短延时)
  36. nop   
  37. nop   
  38. nop   
  39. nop   
  40. nop   
  41. nop   
  42. nop   
  43. nop   
  44. nop   
  45.   
  46. ldr sp, DW_STACK_START  @ setup stack pointer   (设置栈指针,为main函数中的C语言程序做准备)
  47.   
  48. ldr     r2, mpll_value_USER         @ clock user set 12MHz   (设置FCLK=400MHz,HCLK=200MHz,PCLK=100MHz)
  49. str r2, [r1, #oMPLLCON]   
  50. bl  memsetup   (跳到内存初始化函数)
  51.   
  52. @ set GPIO for UART   (初始化串口,为打印提示消息做准备:115200 8N1)
  53. mov r1, #GPIO_CTL_BASE   
  54. add r1, r1, #oGPIO_H   
  55. ldr r2, gpio_con_uart      
  56. str r2, [r1, #oGPIO_CON]   
  57. ldr r2, gpio_up_uart   
  58. str r2, [r1, #oGPIO_UP]    
  59. bl  InitUART   
  60.   
  61.   
  62. @ get read to call C functions   (设置fp和a2寄存器)
  63. mov fp, #0          @ no previous frame, so fp=0  
  64. mov a2, #0          @ set argv to NULL    
  65.   
  66. bl  Main               (跳到main函数执行)。
  67.   
  68. :   b   1b @  

上述代码,16-18行,设置了系统时钟稳定(锁定)时间,20-22行,设置时钟分频比1:4:8,其他说明在上述已经标明。下面是上述程序调用的一个子例程,memsetup:

  1. memsetup:   
  2.     @ initialise the static memory    
  3.   
  4.     @ set memory control registers   
  5.     mov r1, #MEM_CTL_BASE   
  6.     adrl    r2, mem_cfg_val @ adrl跳转?   
  7.     add r3, r1, #52  
  8. 1:  ldr r4, [r2], #4  
  9.     str r4, [r1], #4  
  10.     cmp r1, r3   
  11.     bne 1b   
  12.     mov pc, lr  

由于S3C2440是13个内存设置相关的寄存器,因此第7行是13*4=52,第8-11行依次循环设置13个寄存器。

到这里,汇编代码已经执行完毕,接下来进入C语言执行阶段。

Bootloader之vboot详解(一)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.