您的位置:首页 > 编程语言

s3c2410的Bootloader(Vivi)源代码分析

2008-01-23 20:12 441 查看
导读:
  1.1 Vivi简介
  Vivi 是韩国mizi 公司开发的bootloader, 适用于ARM9处理器。 Vivi有两种工作模式:启动加载模式和下载模式。启动加载模式可以在一段时间后(这个时间可更改)自行启动linux内核,这时vivi的默认模式。在下载模式下,vivi为用户提供一个命令行接口,通过接口可以使用vivi提供的一些命令,见下表:
  命令 功能
  Load 把二进制文件载入Flash或RAM
  Part 操作MTD分区信息。显示、增加、删除、复位、保存MTD分区
  Param 设置参数
  Boot 启动系统
  Flash 管理Flash,如删除Flash的数据
  1.2 vivi的配置与编译
  1.2.1 建立交叉开发环境
  1、在宿主机上安装标准Linux 操作系统:Redhat 9.0 ( 主机系统为win2000,用虚拟机vmware安装的Redhat 9.0,内核版本为2.4.18) 。
  2、宿主机上安装交叉编译器。
  我这边的2410开发板提供的光盘上已附交叉编译器工具:arm-linux-gcc-2.95.3(源码为cross-2.95.3.tar.bz2)。
  先以root 用户的身份登陆到linux 下。
  进入/usr/local 目录,创建名为arm的目录:
  cd /usr/local
  mkdir arm
  将光盘提供的cross-2.95.3.tar.bz2解压到/usr/local/arm目录:
  tar jxvf cross-2.95.3.tar.bz2 –C /usr/local/arm
  
  然后修改修改PATH 变量:为了可以方便使用arm-linux-gcc编译器系统, 把arm-linux工具链目录加入到环境变量PATH中:
  修改/etc/profile文件,添加pathmunge /usr/local/arm/2.95.3/bin即可。
  # Path manipulation
  if [ `id -u` = 0 ]; then
  pathmunge /sbin
  pathmunge /usr/sbin
  pathmunge /usr/local/sbin
  pathmunge /usr/local/arm/2.95.3/bin
  fi
  pathmunge /usr/X11R6/bin after
  设置环境变量后,最好是重启或注销一下,这样设置的环境变量才能生效。
  1.2.2 配置和编译vivi
  如果vivi的源代码已根据开发板作了相应改动,则需要对源代码进行配置和编译,以生成烧入flash的vivi 二进制映象文件。
  由于vivi要用到kernel的一些头文件,所以需要kernel的源代码,所以先要把linux的kernel准备好。将vivi和kernel都解到相应目录下(例如我将光盘提供的vivi源代码解压到/home/chenjun目录下,光盘提供的Linux kernel源码kernel-h2410eb.041024.tar.gz也解压到/home/chenjun目录下,解压后的文件名为kerne-h2410eb)。
  然后需修改/vivi/Makefile里的一些变量设置:
  LINUX_INCLUDE_DIR = /kernel/include/
  (LINUX_INCLUDE_DIR 为kernel/include的对应目录,我的是/home/chen/kerne-h2410eb /include/)
  因此修改为:
  LINUX_INCLUDE_DIR = /home/chenjun/ kerne-h2410eb/include/
  CROSS_COMPILE = /usr/local/arm/2.95.3/bin/arm-linux-
  (CROSS_COMPILE 为arm-linux安装的相应目录,我的是/usr/local/arm/2.95.3/bin/arm-linux-)
  因此修改为:
  CROSS_COMPILE = /usr/local/arm/2.95.3/bin/arm-linux-
  ARM_GCC_LIBS = /usr/local/arm/2.95.3/lib/gcc-lib/arm-linux/2.95.3
  (需根据你arm-linux的安装目录修改,我的是/usr/local/arm/2.95.3/lib/gcc-lib/arm-linux/2.95.3)
  进入/vivi目录执行make distclean。(目的是确保编译的有效性,在编译之前将vivi里所有的“*.o”和“*.o.flag”文件删掉)
  进入/vivi目录里,输入“make menuconfig”,开始选择配置。可以Load一个写好的配置文件也可以自己修改试试。注意Exit时一定要选“Yes”保存配置。
  再输入“make”正式开始编译,一会儿就完了。如果不报错,在/vivi里面就有你自己的“vivi”了。这个就是后面要烧写到flash中的bootloader。
  
  1.3 vivi代码分析
  vivi的代码包括arch,init,lib,drivers和include等几个目录,共200多条文件。
  Vivi主要包括下面几个目录:
  arch:此目录包括了所有vivi支持的目标板的子目录,例如s3c2410目录。
  drivers:其中包括了引导内核需要的设备的驱动程序(MTD和串口)。MTD目录下分map、nand和nor三个目录。
  init:这个目录只有main.c和version.c两个文件。和普通的C程序一样,vivi将从main函数开始执行。
  lib:一些平台公共的接口代码,比如time.c里的udelay()和mdelay()。
  include:头文件的公共目录,其中的s3c2410.h定义了这块处理器的一些寄存器。Platform/smdk2410.h定义了与开发板相关的资源配置参数,我们往往只需要修改这个文件就可以配置目标板的参数,如波特率、引导参数、物理内存映射等。
  1.4 vivi的运行
  vivi的运行也可以分为两个阶段:
  1.4.1 vivi的第一阶段
  完成含依赖于CPU的体系结构硬件初始化的代码,包括禁止中断、初始化串口、复制自身到RAM等。相关代码集中在head.S(viviarchs3c2410目录下):Head.S:
  #include "config.h"
  #include "linkage.h"
  #include "machine.h"
  @ Start of executable code
  ENTRY(_start)
  ENTRY(ResetEntryPoint)
  @
  @ Exception vector table (physical address = 0x00000000) ;异常向量表物理地址
  @
  @ 0x00: Reset ;复位
  b Reset
  @ 0x04: Undefined instruction exception ;未定义的指令异常
  UndefEntryPoint:
  b HandleUndef
  @ 0x08: Software interrupt exception ;软件中断异常
  SWIEntryPoint:
  b HandleSWI
  @ 0x0c: Prefetch Abort (Instruction Fetch Memory Abort) ;内存操作异常
  PrefetchAbortEnteryPoint:
  b HandlePrefetchAbort
  @ 0x10: Data Access Memory Abort ;数据异常
  DataAbortEntryPoint:
  b HandleDataAbort
  @ 0x14: Not used ;未使用
  NotUsedEntryPoint:
  b HandleNotUsed
  @ 0x18: IRQ(Interrupt Request) exception ;慢速中断处理
  IRQEntryPoint:
  b HandleIRQ
  @ 0x1c: FIQ(Fast Interrupt Request) exception;快速中断处理
  FIQEntryPoint:
  b HandleFIQ
  @
  @ VIVI magics
  @
  @ 0x20: magic number so we can verify that we only put
  .long 0
  @ 0x24:
  .long 0
  @ 0x28: where this vivi was linked, so we can put it in memory in the right place
  .long _start
  @ 0x2C: this contains the platform, cpu and machine id
  .long ARCHITECTURE_MAGIC
  @ 0x30: vivi capabilities
  .long 0
  #ifdef CONFIG_PM ;vivi考虑不需要使用电源管理
  @ 0x34:
  b SleepRamProc
  #endif
  #ifdef CONFIG_TEST
  @ 0x38:
  b hmi
  #endif @
  @ Start VIVI head
  @
  Reset:
  @ disable watch dog timer;禁止看门狗计时器
  mov r1, #0x53000000 ;WTCON寄存器地址是
  0x53000000,清0
  mov r2, #0x0
  str r2, [r1]
  #ifdef CONFIG_S3C2410_MPORT3 ;不符合条件,跳到下面的关中断
  /**** 在/vivi/include/autoconf.h中#undef CONFIG_S3C2410_MPORT3******/
  mov r1, #0x56000000 ;GPACON寄存器地址是
  0x56000000
  mov r2, #0x00000005
  str r2, [r1, #0x70] ;配置GPHCON寄存器
  mov r2, #0x00000001
  str r2, [r1, #0x78] ;配置GPHUP寄存器
  mov r2, #0x00000001
  str r2, [r1, #0x74] ;配置GPHDAT寄存器
  #endif
  @ disable all interrupts ;禁止全部中断
  mov r1, #INT_CTL_BASE
  mov r2, #0xffffffff
  str r2, [r1, #oINTMSK] ;掩码关闭所有中断
  ldr r2, =0x7ff
  str r2, [r1, #oINTSUBMSK]
  @ initialise system clocks ;初始化系统时钟
  mov r1, #CLK_CTL_BASE
  mvn r2, #0xff000000
  str r2, [r1, #oLOCKTIME]
  
  @ldr r2, mpll_50mhz
  @str r2, [r1, #oMPLLCON]
  #ifndef CONFIG_S3C2410_MPORT1 ;满足条件,向下执行
  /**** 在/vivi/include/autoconf.h中#undef CONFIG_S3C2410_MPORT1******/
  @ 1:2:4
  mov r1, #CLK_CTL_BASE
  mov r2, #0x3
  str r2, [r1, #oCLKDIVN]
  mrc p15, 0, r1, c1, c0, 0 @ read ctrl register
  orr r1, r1, #0xc0000000 @ Asynchronous
  mcr p15, 0, r1, c1, c0, 0 @ write ctrl register
  @ now, CPU clock is 200 Mhz ;CPU的频率是200MHz
  mov r1, #CLK_CTL_BASE
  ldr r2, mpll_200mhz
  str r2, [r1, #oMPLLCON]
  #else
  @ 1:2:2
  mov r1, #CLK_CTL_BASE
  ldr r2, clock_clkdivn
  str r2, [r1, #oCLKDIVN]
  mrc p15, 0, r1, c1, c0, 0 @ read ctrl register
  orr r1, r1, #0xc0000000 @ Asynchronous
  mcr p15, 0, r1, c1, c0, 0 @ write ctrl register
  @ now, CPU clock is 100 Mhz ;CPU的频率是100MHz
  mov r1, #CLK_CTL_BASE
  ldr r2, mpll_100mhz
  str r2, [r1, #oMPLLCON]
  #endif
  bl memsetup ;跳转到memsetup函数
  /*****************************
  Memsetup函数的实现:
  ENTRY(memsetup)
  @ initialise the static memory
  @ set memory control registers ;设置内存控制寄存器的初值
  mov r1, #MEM_CTL_BASE
  adrl r2, mem_cfg_val
  /*******************
  @
  @ Data Area
  @
  @ Memory configuration values
  .align 4
  mem_cfg_val: ;定义好的13*4=52个字节初值
  .long vBWSCON ;在/vivi/include/platform/smdk2410.h中赋值
  /****** SDRAM从32位变成16位,需要修改vBWSCON的值******/
  .long vBANKCON0
  .long vBANKCON1
  .long vBANKCON2
  .long vBANKCON3
  /********** 网卡控制器vBANKCON3的值可能需要修改**************/
  .long vBANKCON4
  .long vBANKCON5
  .long vBANKCON6
  /****** SDRAM从32位变成16位,可能需要修改vBANKCON6的值******/
  .long vBANKCON7
  .long vREFRESH
  .long vBANKSIZE
  /****** SDRAM从64MB变成32MB,需要修改vBANKSIZE的值******/
  .long vMRSRB6
  .long vMRSRB7
  ********************/
  add r3, r1, #52
  1: ldr r4, [r2], #4
  str r4, [r1], #4
  cmp r1, r3
  bne 1b ;循环操作,直到13个寄存器赋值完成
  mov pc, lr
  /*******************************/
  #ifdef CONFIG_PM ;vivi考虑不需要使用电源管理
  @ Check if this is a wake-up from sleep
  ldr r1, PMST_ADDR
  ldr r0, [r1]
  tst r0, #(PMST_SMR)
  bne WakeupStart ;查看状态,判断是否需要跳转到WakeupStart
  #endif
  #ifdef CONFIG_S3C2410_SMDK ;SMDK开发板使用
  @ All LED on ;点亮开发板上的LED
  mov r1, #GPIO_CTL_BASE
  add r1, r1, #oGPIO_F ;LED使用GPIOF组的管脚
  ldr r2,=0x55aa ;使能EINT0,EINT1,EINT2,EINT3,
  ;另四个管脚配置成输出,屏蔽EINT4,5,6,7
  str r2, [r1, #oGPIO_CON]
  mov r2, #0xff
  str r2, [r1, #oGPIO_UP] ;disable the pull-up function
  mov r2, #0x00
  str r2, [r1, #oGPIO_DAT]
  #endif
  #if 0
  @ SVC
  mrs r0, cpsr
  bic r0, r0, #0xdf
  orr r1, r0, #0xd3
  msr cpsr_all, r1
  #endif
  @ set GPIO for UART ;设置串口
  mov r1, #GPIO_CTL_BASE
  add r1, r1, #oGPIO_H ;设置GPIO_H组管脚为串口
  ldr r2, gpio_con_uart
  str r2, [r1, #oGPIO_CON]
  ldr r2, gpio_up_uart
  str r2, [r1, #oGPIO_UP]
  /*************************
  @ inital values for GPIO
  gpio_con_uart:
  .long vGPHCON ;vGPHCON在/vivi/include/platform/smdk2410.h中赋值
  ;#define vGPHCON 0x0016faaa
  ;GPIO_H配置为nCTS0,nRTS0, RXD0,TXD0, RXD1,
  ;TXD1,nCTS1,nRTS1,
  /**** 三个串口都使能,可能需要修改#define vGPHCON 0x0016aaaa ****/
  gpio_up_uart:
  .long Vgphup ;同上#define vGPHUP 0x000007ff
  ;The pull-up function is disabled.
  ************************/
  bl InitUART ;跳转到InitUART串口初始化函数
  /****************************************************
  @ Initialize UART
  @
  @ r0 = number of UART port
  InitUART:
  ldr r1, SerBase
  /*******************
  .align 4 ;缺省情况下在vivi中只初始化了UART0
  SerBase:
  #if defined(CONFIG_SERIAL_UART0)
  .long UART0_CTL_BASE ;基地址在/vivi/include/s3c2410.h中定义
  #elif defined(CONFIG_SERIAL_UART1)
  .long UART1_CTL_BASE
  #elif defined(CONFIG_SERIAL_UART2)
  .long UART2_CTL_BASE
  #else
  #error not defined base address of serial
  #endif
  /********************/
  mov r2, #0x0
  str r2, [r1, #oUFCON]
  str r2, [r1, #oUMCON]
  mov r2, #0x3
  str r2, [r1, #oULCON]
  ldr r2, =0x245
  str r2, [r1, #oUCON]
  #define UART_BRD ((50000000 / (UART_BAUD_RATE * 16)) - 1)
  mov r2, #UART_BRD
  str r2, [r1, #oUBRDIV]
  mov r3, #100
  mov r2, #0x0
  1: sub r3, r3, #0x1
  tst r2, r3
  bne 1b
  #if 0
  mov r2, #'U'
  str r2, [r1, #oUTXHL]
  1: ldr r3, [r1, #oUTRSTAT]
  and r3, r3, #UTRSTAT_TX_EMPTY
  tst r3, #UTRSTAT_TX_EMPTY
  bne 1b
  mov r2, #'0'
  str r2, [r1, #oUTXHL]
  1: ldr r3, [r1, #oUTRSTAT]
  and r3, r3, #UTRSTAT_TX_EMPTY
  tst r3, #UTRSTAT_TX_EMPTY
  bne 1b
  #endif
  mov pc, lr
  /****************************************************/
  #ifdef CONFIG_DEBUG_LL ;打印调试信息,缺省未定义
  @ Print current Program Counter
  ldr r1, SerBase
  mov r0, #'r'
  bl PrintChar
  mov r0, #'n'
  bl PrintChar
  mov r0, #'@'
  bl PrintChar
  mov r0, pc
  bl PrintHexWord
  #endif
  #ifdef CONFIG_BOOTUP_MEMTEST
  @ simple memory test to find some DRAM flaults.
  bl memtest
  #endif
  #ifdef CONFIG_S3C2410_NAND_BOOT ;从NAND Flash启动
  bl copy_myself ;跳转到copy_myself函数
  /**********************************************
  @
  @ copy_myself: copy vivi to ram
  @
  copy_myself:
  mov r10, lr
  @ reset NAND
  mov r1, #NAND_CTL_BASE
  ldr r2, =0xf830 @ initial value
  str r2, [r1, #oNFCONF]
  ldr r2, [r1, #oNFCONF]
  bic r2, r2, #0x800 @ enable chip
  str r2, [r1, #oNFCONF]
  mov r2, #0xff @ RESET command
  strb r2, [r1, #oNFCMD]
  mov r3, #0 @ wait
  1: add r3, r3, #0x1
  cmp r3, #0xa
  blt 1b
  2: ldr r2, [r1, #oNFSTAT] @ wait ready
  tst r2, #0x1
  beq 2b
  ldr r2, [r1, #oNFCONF]
  orr r2, r2, #0x800 @ disable chip
  str r2, [r1, #oNFCONF]
  @ get read to call C functions (for nand_read())
  ldr sp, DW_STACK_START @ setup stack pointer
  mov fp, #0 @ no previous frame, so fp=0
  @ copy vivi to RAM
  ldr r0, =VIVI_RAM_BASE
  /*********在/vivi/linux/platform/smdk2410.h中定义
  #define VIVI_RAM_BASE (DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE)
  ***************************************/
  mov r1, #0x0
  mov r2, #0x20000 ;0x20000-〉128k字节
  bl nand_read_ll ;nand_read_ll在/vivi/arch/s3c2410/nand_read.c中定义
  ;r0,r1,r2分别为函数的三个参数
  ;从NANDFlash的0地址拷贝128k到SDRAM指定处
  tst r0, #0x0
  beq ok_nand_read
  #ifdef CONFIG_DEBUG_LL
  bad_nand_read:
  ldr r0, STR_FAIL
  ldr r1, SerBase
  bl PrintWord
  1: b 1b @ infinite loop
  #endif
  ok_nand_read:
  #ifdef CONFIG_DEBUG_LL
  ldr r0, STR_OK
  ldr r1, SerBase
  bl PrintWord
  #endif
  @ verify
  mov r0, #0
  ldr r1, =0x33f00000
  mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
  go_next:
  ldr r3, [r0], #4
  ldr r4, [r1], #4
  teq r3, r4
  bne notmatch
  subs r2, r2, #4
  beq done_nand_read
  bne go_next
  notmatch:
  #ifdef CONFIG_DEBUG_LL
  sub r0, r0, #4
  ldr r1, SerBase
  bl PrintHexWord
  ldr r0, STR_FAIL
  ldr r1, SerBase
  bl PrintWord
  #endif
  1: b 1b
  done_nand_read:
  #ifdef CONFIG_DEBUG_LL
  ldr r0, STR_OK
  ldr r1, SerBase
  bl PrintWord
  #endif
  mov pc, r10 ;vivi拷贝到SDRAM完成,函数返回
  *********************************/
  @ jump to ram
  ldr r1, =on_the_ram
  add pc, r1, #0
  nop
  nop
  1: b 1b @ infinite loop
  on_the_ram:
  #endif
  #ifdef CONFIG_DEBUG_LL
  ldr r1, SerBase
  ldr r0, STR_STACK
  bl PrintWord
  ldr r0, DW_STACK_START
  bl PrintHexWord
  #endif
  @ get read to call C functions
  ldr sp, DW_STACK_START @ setup stack pointer
  mov fp, #0 @ no previous frame, so fp=0
  mov a2, #0 @ set argv to NULL
  bl main @ call main
  mov pc, #FLASH_BASE @ otherwise, reboot
  @
  @ End VIVI head
  @
  1.4.2vivi的第二阶段
  vivi的第二阶段是从main()函数开始,同一般的C语言程序一样,该函数在/init/main.c文件中,总共可以分为8个步骤。
  (1)函数开始,通过putstr(vivi_banner)打印出vivi的版本。Vivi_banner在/init/version.c文件中定义
  (2)对开发板进行初始化(board_init函数),board_init是与开发板紧密相关的,这个函数在/arch/s3c2410/smdk.c文件中。开发板初始化主要完成两个功能,时钟初始化(init_time())和通用IO口设置(set_gpios())。
  void set_gpios(void)
  {
  GPACON = vGPACON;
  GPBCON = vGPBCON;
  GPBUP = vGPBUP;
  GPCCON = vGPCCON;
  GPCUP = vGPCUP;
  GPDCON = vGPDCON;
  GPDUP = vGPDUP;
  GPECON = vGPECON;
  GPEUP = vGPEUP;
  GPFCON = vGPFCON;
  GPFUP = vGPFUP;
  GPGCON = vGPGCON;
  GPGUP = vGPGUP;
  GPHCON = vGPHCON;
  GPHUP = vGPHUP;
  EXTINT0 = vEXTINT0;
  EXTINT1 = vEXTINT1;
  EXTINT2 = vEXTINT2;
  }
  其中,GPIO口在smdk2410.h(viviincludeplatform目录下)文件中定义。
  (3)内存映射初始化和内存管理单元的初始化工作:
  mem_map_init();
  mmu_init();
  这两个函数都在/arch/s3c2410/mmu.c文件中。
  void mem_map_init(void)
  {
  #ifdef CONFIG_S3C2410_NAND_BOOT
  mem_map_nand_boot();
  #else
  mem_map_nor();
  #endif
  cache_clean_invalidate();
  tlb_invalidate();
  }
  如果配置vivi时使用了NAND作为启动设备,则执行mem_map_nand_boot(),否则执行mem_map_nor()。这里要注意的是,如果使用NOR启动,则必须先把vivi代码复制到RAM中。这个过程是由copy_vivi_to_ram()函数来完成的。代码如下:
  static void copy_vivi_to_ram(void)
  {
  putstr_hex("Evacuating 1MB of Flash to DRAM at 0x", VIVI_RAM_BASE);
  memcpy((void *)VIVI_RAM_BASE, (void *)VIVI_ROM_BASE, VIVI_RAM_SIZE);
  }
  VIVI_RAM_BASE、VIVI_ROM_BASE、VIVI_RAM_SIZE这些值都可以在smdk2410.h中查到,并且这些值必须根据自己开发板的RAM实际大小修改。这也是在移植vivi的过程中需要注意的一个地方。
  mmu_init()函数中执行了arm920_setup函数。这段代码是用汇编语言实现的,针对arm920t核的处理器。
  (4)初始化堆栈,heap_init()。(定义在vivilibheap.c文件中)
  int heap_init(void)
  {
  return mmalloc_init((unsigned char *)(HEAP_BASE), HEAP_SIZE);
  }
  (5)初始化mtd设备,mtd_dev_init()。
  int mtd_init(void)
  {
  int ret;
  #ifdef CONFIG_MTD_CFI
  ret = cfi_init();
  #endif
  #ifdef CONFIG_MTD_SMC
  ret = smc_init();
  #endif
  #ifdef CONFIG_S3C2410_AMD_BOOT
  ret = amd_init();
  #endif
  if (ret) {
  mymtd = NULL;
  return ret;
  }
  return 0;
  }
  
  这几个函数可以在/drivers/mtd/maps/s3c2410_flash.c里找到。
  (6) 初始化私有数据,init_priv_data()。(定义在vivilibpriv_datarw.c文件中)
  (7) 初始化内置命令,init_builtin_cmds()。
  通过add_command函数,加载vivi内置的几个命令。
  (8)启动boot_or_vivi()。
  启动成功后,将通过vivi_shell()启动一个shell(如果配置了CONFIG_SERIAL_TERM),此时vivi的任务完成。
  1.6 vivi的配置文件
  Vivi的初始配置文件位置:/vivi/arch/def-configs/smkd2410, 通过make menuconfig 修改后的配置保存在这个文件中,我们也可以载入一个自己的配置文件来进行编译。

本文转自
http://www.sduw.com/www/1/2007-09/80.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: