Chinaunix首页 | 论坛 | 博客
  • 博客访问: 54728
  • 博文数量: 16
  • 博客积分: 116
  • 博客等级: 民兵
  • 技术积分: 105
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-30 18:27
文章分类

全部博文(16)

文章存档

2015年(1)

2014年(2)

2012年(1)

2011年(11)

2010年(1)

我的朋友

分类:

2011-08-25 16:16:41

原文地址:s3c2410的Bootloader(Vivi) 作者:oldstorm

Bootloader(引导加载程序)是系统加电后运行的第一段代码,一般运行的时间非常短,但是对于嵌入式系统来说,这段代码非常重要。在我们的台式电脑当中,引导加载程序由BIOS(固件程序)和位于硬盘MBR中的操作系统引导加载程序(比如NTLOADER,GRUB和LILO)一起组成

   在嵌入式系统当中没有像BIOS这样的固件程序,不过也有一些嵌入式CPU会在芯片内部嵌入一小段程序,一般用来将bootloader装进RAM中,有点类似BIOS,但是功能比BIOS弱很多。在一般的典型系统中,整个系统的加载启动任务全由bootloader来完成。在ARM中,系统上电或复位时通常从地址0x00000000处开始执行,而在这个位置,通常安排的就是系统的BOOTLOADER。通过这小段程序可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境设置到一个合适的状态!以为最终调用操作系统内核准备好正确的环境。

嵌入式LINUX系统从软件的角度可看成是4个层次:

(1)引导加载程序,包括固化在固件中(firmware)中的启动代码(可选)和BOOTLOADER两大部分。

(2)内核。特定于板子的定制内核以及控制内核引导系统的参数。

(3)文件系统。包括根文件系统和建立与FLASH内存设备上的文件系统。

(4)用户应用程序。特定于用户的应用程序,有时还包括一个GUI。

—————————————————————————————

| Bootloader   | 参数   |  Kernel     |  文件系统             |

—————————————————————————————

 大多书BOOTLOASER都包含两中模式,启动加载模式和下载模式

 

BOOTLOADER的启动流程

   大多书分为两个阶段,第一个阶段主要是包含依赖于CPU的体系结构的硬件初始化代码,通常都是用汇编语言来实现的。这个阶段的任务有:

●     基本的硬件设备初始化(屏蔽所有中断、关闭处理器内部指令/数据CACHE等)

●     为第二阶段准备RAM空间

●     如果是从某个固态存储媒质中,则复制BOOTLOADER的第二阶段代码到RAM

●     设置堆栈

●     跳转到第二阶段的C程序入口点

第二阶段通常是由C语言实现的,这个阶段的主要任务有:

●     初始化本阶段所要用到的硬件设备

●     检测系统的内存映射

●     将内核映像和根文件系统映像从FLASH读到RAM

●     为内核设置启动参数

●     调用内核

BOOTLOADER调用LINUX内核的方法是直接跳转到内核的第一条指令处,即跳转MEM_START+0x8000地址处,在跳转的时候必须满足下面的条件

●     CPU寄存器:R0为0,R1为机器类型ID,R2为启动参数,标记列表在RAM中的起始基地址

●     CPU模式:必须禁止中断,CPU设置为SVC模式

●     Cache和MMU设置:MMU必须关闭,指令CACHE可以打开也可以关闭,数据CACHE必须关闭

 

 

 

 

 

  Vivi简介

vivi是韩国mizi 公司开发的bootloader, 适用于ARM9处理器。 Vivi有两种工作模式:启动加载模式和下载模式。启动加载模式可以在一段时间后(这个时间可更改)自行启动linux内核,这时vivi的默认模式。在下载模式下,vivi为用户提供一个命令行接口,通过接口可以使用vivi提供的一些命令,见下表:

命令
 功能
 
Load
 把二进制文件载入Flash或RAM
 
Part
 操作MTD分区信息。显示、增加、删除、复位、保存MTD分区
 
Param
 设置参数
 
Boot
 启动系统
 
Flash
 管理Flash,如删除Flash的数据
 


 

1 vivi的配置与编译

1.1 建立交叉开发环境

   网上关于这方面的内容有很多,也可以用最简单的cross-tool来自动建立开发环境!

 

1.2 配置和编译vivi

如果vivi的源代码已根据开发板作了相应改动,则需要对源代码进行配置和编译,以生成烧入flash的vivi 二进制映象文件。

需修改/vivi/Makefile里的一些变量设置: 

LINUX_INCLUDE_DIR = /usr/local/arm/2.95.3/include

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

 

进入/vivi目录执行make distclean。(目的是确保编译的有效性,在编译之前将vivi里所有的“*.o”和“*.o.flag”文件删掉)

进入/vivi目录里,输入“make menuconfig”,开始选择配置。可以Load一个写好的配置文件也可以自己修改试试。注意Exit时一定要选“Yes”保存配置。

再输入“make”正式开始编译,一会儿就完了。如果不报错,在/vivi里面就有你自己的“vivi”了。这个就是后面要烧写到flash中的bootloader。

 

 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定义了与开发板相关的资源配置参数,我们往往只需要修改这个文件就可以配置目标板的参数,如波特率、引导参数、物理内存映射等。

 

 

vivi的第一阶段


vivi的第一阶段

首先应该注意的就是为什么使用head.S而不是用head.s?有了GNU AS和GNU Gcc的基础,不难理解主要原因就是为了使用C预处理器的宏替换和文件包含功能(GNU AS的预处理无法完成此项功能)。这样的好处就是可以使用C预处理器的功能来提高ARM汇编的程序设计环境,更加方便。但是因为ARM汇编和C在宏替换的细节上有所不同,为了区分,引入了__ASSEMBLY__这个变量,这是通过Makefile中AFLAGS来引入的,具体如下:

 AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS)
 


     在后面的头文件中,会看到很多#ifdef __ASSEMBLY__等的操作,就是用来区分这个细节的。在编译汇编文件时,加入AFLAGS选项,所以__ASSEMBLY__传入,也就是定义了__ASSEMBLY__;在编译C文件时,没有用AFLAGS选项,自然也就没有定义__ASSEMBLY__。

 

Head.S:

 

#include "config.h"

#include "linkage.h"

#include "machine.h"

include/config.h只是包含一个autoconf.h。如果写一个专用的bootloader,不采用vivi的配置机制,那么配置部分就没有这么复杂了,只需要在include文件夹中包含一个配置头文件即可。现在bootloader的设计有两种趋势,一种是针对特定应用,有特殊要求,也就是“专用”。那么设计时,不需要过多的配置,只需要简单的完成引导内核的功能就可以了。二是普通应用,一般是对基本“通用”的bootloader,比如uboot等,然后根据相应的模版进行移植。这就需要了解uboot等的架构,可以进行定制和功能的增加。uboot完成的不仅仅是一个bootloader的功能,还可以提供调试等功能,所以其角色还包含驻留程序这个功能,也就是uboot真正的角色是monitor。当然,可以不加区分,统称为bootloader。

include/linkage.h就是实现了ENTRY宏的封装。其实这个头文件也仅仅为head.S提供了服务

include/machine.h则是利用条件编译来选择适合自己开发板的头文件,本开发板的头文件是【include/platform/smdk2410.h】,主要是一些寄存器的初始值(以v开头)和一些相关的地址等等的定义。一般开发板不同,都是修改此文件相应的部分。

@ Start of executable code

ENTRY(_start)                   @程序入口点

ENTRY(ResetEntryPoint)

 

 

下面是装载中断向量表,

开始对中断向量表很疑惑。现在的理解比较清晰了,在硬件实现上,会支持中断机制,这个可以参考微机接口原理部分详细理解。现在的中断机制处理的比较智能,对每一种中断会固定一个中断向量,比如说,发生IRQ中断,中断向量地址为0x00000018(当然,这还与ARM9TDMI core有关,其中一个引脚可以把中断向量表配置为高端启动,或者低端启动。你可以通过CP15的register 1的bit 13的V bit来设置,可以查看Datasheet TABLE 2-10来解决。),那么PC要执行的指令就是0x00000018。如果在这个地址放上一个跳转指令(只能使用b或者ldr),那么就可以跳到实际功能代码的实现区了。

ARM规定,在起始必须有8条跳转指令,你可以用b,也可以用ldr pc,文件名。这样的8条规则的标志被arm定义为bootloader的识别标志,检测到这样的标志后,就可以从该位置启动。这样的做法是因为开始的时候不一定有bootloader,必须有一种识别机制,如果识别到bootloader,那么就从bootloader启动。

@

@ 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的这些magic number,虽然设计在这里,不过大部分还是没有使用的。其中0x20和0x24没有使用,在0x2C处,倒是设计了一个magic number,组成的格式如下:bit[31:24]为platform,bit[23:16]为cpu type,bit[15:0]为machine id。关于ARCHITECTURE_MAGIC的定义,在【include/platform/smdk2410.h】

@

@ 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

@

 

 

vivi的第二阶段


vivi的第二阶段

 

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())。

  注意这里vGPxCON中的v表示value,oGPxCON中的o表示offset

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(\vivi\include\platform\目录下)文件中定义。

(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()。(定义在\vivi\lib\heap.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()。(定义在\vivi\lib\priv_data\rw.c文件中)

     此部分的功能是把vivi可能用到的所有私有参数都放在预先规划的内存区域,大小为48K,基地址为0x33df0000。在内存的分配示意图方面,可以参考《s3c2410完全开发》,到此为止,vivi作为bootloader的三大核心任务:initialise various devices, and eventually call the Linux kernel,passing information to the kernel.,现在只是完成第一方面的工作,设备初始化基本完成,实际上step 6是为启动Linux内核和传递参数做准备的,把vivi的私有信息,内核启动参数,mtd分区信息等都放到特定的内存区域,等待后面两个重要工作使用(在step 8完成,后面的step 7也是为step 8服务的)。

这48K区域分为三个组成部分:MTD参数、vivi parameter、Linux启动命令。

 

(7)    初始化内置命令,init_builtin_cmds()。

通过add_command函数,加载vivi内置的几个命令。

(8)    启动boot_or_vivi()。

启动成功后,将通过vivi_shell()启动一个shell(如果配置了CONFIG_SERIAL_TERM),此时vivi的任务完成。

阅读(829) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~