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

Bootloader(Vivi)源代码分析

2008-02-21 13:47 239 查看
Bootloader(Vivi)源代码分析

----基于S3C2410处理器

作者:forkchen

Email:chenjun@bupt-bcnl.com

QQ:56155986

欢迎广大嵌入式之友多多交流

目 录

1.1 Vivi简介... 2

1.2 vivi的配置与编译... 3

1.2.1 建立交叉开发环境... 3

1.2.2 配置和编译vivi 4

1.3 vivi代码分析... 4

1.4 vivi的运行... 5

1.4.1 vivi的第一阶段... 5

1.4.2 vivi的第二阶段... 15

1.5 启动代码执行流程图... 17

1.6 vivi的配置文件... 19

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。

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.2 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())。 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文件中) (7) 初始化内置命令,init_builtin_cmds()。 通过add_command函数,加载vivi内置的几个命令。 (8) 启动boot_or_vivi()。 启动成功后,将通过vivi_shell()启动一个shell(如果配置了CONFIG_SERIAL_TERM),此时vivi的任务完成。 [code]

1.5 启动代码执行流程图

(1)head.s代码执行流程 (2)main.c代码执行流程

1.6 vivi的配置文件

Vivi的初始配置文件位置:/vivi/arch/def-configs/smkd2410, 通过make menuconfig 修改后的配置保存在这个文件中,我们也可以载入一个自己的配置文件来进行编译。


[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: