Chinaunix首页 | 论坛 | 博客
  • 博客访问: 242136
  • 博文数量: 29
  • 博客积分: 1598
  • 博客等级: 上尉
  • 技术积分: 388
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-09 13:35
文章分类
文章存档

2012年(3)

2011年(4)

2010年(1)

2009年(21)

我的朋友

分类: LINUX

2009-04-19 19:37:56



UBOOT1.2.0分析及移植

格式弄的太乱了,不过还是希望对大家有所帮助 :D,下面是参考资料,这些都是移植uboot必看的资料。


参考资料:

(1)http://blog.chinaunix.net/u1/34474/showart.php?id=363269

这位高手的blog是每位想进去嵌入式行业的同学必看的。

(2)<>pdf文档,百度上可以搜到的,非常感谢原作者的分享

(3) <>PDF,其次也参考了很多其他的资料,非常敬佩大家的钻研与奉献精神!

工作环境:

uboot1.2.0

Ubuntu8.10

cross_compile3.3.2


1 绪论

描述当前嵌入式产品的发展现状;

描述sangsun公司的产品,简单介绍s3c2410,以及板载配置。

2 bootloader的介绍,不同种类优劣性比较


3 论文出发点:目前的论文主要是针对Uboot的编译与移植,侧重点在如何配置适合自己开发板的参数,并将它移植到开发板。但并没有对uboot的工作流程进行清晰的分析,如果能够从本质上掌握整个uboot的工作原理,开发嵌入式产品时的周期也将缩短。因此对uboot的分析是必要的。


4 uboot目录结构。u-boot的源码顶层目录说明

目    录 特    性                解 释 说 明
board                 
平台依赖          存放电路板相关的目录文件,
                                       
例如:RPXlite(mpc8xx)
                                        smdk2410(arm920t)

                                        sc520_cdp(x86)
等目录

cpu                   
平台依赖          存放CPU相关的目录文件
                                       
例如:mpc8xxppc4xx
                                        arm720t
arm920txscalei386等目录

lib_ppc               
平台依赖          存放对PowerPC体系结构通用的文件,
                                       
主要用于实现PowerPC平台通用的函数

lib_arm               
平台依赖           存放对ARM体系结构通用的文件,
                                        
主要用于实现ARM平台通用的函数

lib_i386              
平台依赖           存放对X86体系结构通用的文件,
                                        
主要用于实现X86平台通用的函数

include               
通用                头文件和开发板配置文件,
                                         
所有开发板的配置文件都在configs目录下

common              
通用                通用的多功能函数实现
lib_generic           
通用                通用库函数的实现
net                   
 通用                存放网络的程序
fs                    
 通用                存放文件系统的程序
post                  
 通用                存放上电自检程序
drivers               
  通用                通用的设备驱动程序,主要有以太网接口的驱动
disk                  
  通用                硬盘接口程序
rtc                   
  通用                RTC的驱动程序
dtt                   
  通用                数字温度测量器或者传感器的驱动
examples              
应用例程             一些独立运行的应用程序的例子,例如helloworld
tools                 
  工具                存放制作S-Record或者u-boot格式的映像等工具,
                                         
例如mkimage

doc                   
  文档                开发使用文档


5 简单介绍一下makefile,开始着手于源码的Makefile分析。在整个uboot目录中,makefile可以分为三类,

1)顶层makefile

2)结构makefile

3)子目录makefile

正式分析Makefilemakefile内容比较容易理解。

Ubootmakefile总共有50几页,但其中重要的内容折合起来也就十二三页。


6正式开始分析uboot源码。

根据/board/smdk2410目录下的u-boot.lds可知,函数入口处在/cpu/arm920t/start.S中的_start处,且从0x00000000地址处开始。

因此首先应该分析start.S文件,这是第一个执行的汇编文件,非常的重要。

(1) cpu/arm920t/start.S

uboot启动也分stage1stage2两个阶段。Uboot加载流程。2410上电,nandflash4kb的内容以硬件的方式被拷贝到SRAM中,SRAMcpu核中与nandflash配合的芯片从而实现nandflash的自启动系统。在SRAM中的部分uboot代码执行并在第2阶段执行之前,将nandflash中的uboot的全部代码拷贝到SDRAM中运行。然后引导内核与文件系统。

由上可以看出,这4kb以内代码必须要能够完成一些初始化工作。其实这就是我们平时所说的Uboot运行两阶段中的第一阶段。这部分短小的代码用汇编语言编写而成,也就是我们的start.S了。下面是总结的一张流程图

.globl _start

1 _start: b reset 运行以后,先跳转到reset标签处

。。。。。。

  1. reset:

reset主要完成以下的功能。

  1. 首先切换到svc超级用户模式。在系统复位和软件中断时可以进入这种模式。

  2. 关闭看门狗。看门狗可以用作16位的定时器来请求中断服务。并在固定时钟内产生复位信号所以要关闭。

  3. 屏蔽所有的中断。通过设置INTMSKINTSUBMSK寄存器来实现。

  4. 设置时钟。2410的时钟一般采用默认的,不用另外设置。

3 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 跳转到cpu_init_crit

bl cpu_init_crit

#endif 接下来跳转到cpu_init_crit处。Bl指令是带返回的。PC值将指向下一条指令。

cpu_init_crit:从注释中可以了解到。它主要来设置一些重要的寄存器和内存时钟的设置。

MCR.MRC指令统称为协处理器寄存器传送指令。

MCR{cond} coproc,opcode1,Rd,CRn,CRm,{,opcode2}

Coproc:指令操作的协处理器名。标准名为pn,n0~15

mcr p15, 0, r0, c7, c7, 0 例如这条指令就是将寄存器r0中的值送到协处理器p15c7中去。

mrc即是将协处理中的寄存器的内容送到外部的寄存器中。


在重定位之前,我们必须设置内存时序,因为内存时钟是依赖于开发版的。在lowlevel_init.S文件里可以得到解释。lowlevel_init.S主要定义了各Bank的内钟等等

之后mov ip, lr //保存当前的lr。即是当前的pc值,因为再下一条指令又要进行跳转了。。

bl lowlevel_init//同样,这是条带返回的跳转。指向下一条指令。

mov lr, ip

mov pc, lr

因为lowevel_init被声明为.globl,可以全局使用,虽然不在start.S文件中定义。因此可以直接跳转到/board/smdk2410/lowlevel.S中来执行这个命令了。


它主要完成内存控制的配置,设置当前的PCnandflash的起始位置。

下面是Lowlevel_init中的部分代码

BWSCONBUS WIDTH&WAIT CONTROL REGISTER 0x48000000

可以来选择哪个BANK

ldr r0, =SMRDATA //SMRDATA的地址传到r0中。

ldr r1, _TEXT_BASE //通过将_TEXT_BASE中的值加载到r1

sub r0, r0, r1 //r0=r0-r1??

ldr r1, =BWSCON /* Bus Width Status Controller */

add r2, r0, #13*4//SMRDATA13条指令。

0:

ldr r3, [r0], #4//SMDDATA中的值循环。主要是涉及了BAND0~7的时钟啊,内存刷新什么的

str r3, [r1], #4

cmp r2, r0

bne 0b


/* everything is fine now */

mov pc, lr//返回到cpu_init_crit

这样返回到了cup_init_crit中,

.....

mov ip,lr //ip中保存了reset中调用cpu_init_crit的下一条指令地址

bl lowlevel_init

mov lr, ip //Lowlevel_init返回后,执行这条指令。将ip传给lr

mov pc, l //再传给pc,这样就回到了reset的下一条指令处

#endif /* CONFIG_SKIP_LOWLEVEL_INIT */


返回到reset处吧~~#endif等着我们,到这里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

定义各种异常模式。每种异常模式都有对应的pc来存储异常指令地址。接着继续执行这条指令以后的汇编代码,具体还是要结合start.S源码看的。


另外在/board/smdk2410/config,mk目录下存储着这样的信息。

# SMDK2410 has 1 bank of 64 MB DRAM

#

# 3000'0000 to 3400'0000

#

# Linux-Kernel is expected to be at 3000'8000, entry 3000'8000

# optionally with a ramdisk at 3080'0000

#

# we load ourself to 33F8'0000

#

# download area is 3300'0000

#

TEXT_BASE = 0x33F80000

定义了TEXT_BASE的地址。另外UBOOT_RAM_BASE _armboot_start 都是这个地址


Start.S继续向下执行,到达relocate代码段

relocate那段代码只针对从norflash中启动的设备有效,可要可不要,因此要是设备支持从nandflash启动,需要添加能将uboot拷贝到RAM中的代码。start.S本身是在SRAM中运行的,添加的这部分拷贝代码就是用来将剩余的内容全部拷贝到RAM中去了。

我们这里只是分析阶段。具体怎么在这里添加拷贝代码,参考第二部分 正式移植


在添加的拷贝代码中,执行了一条跳转到nand_read_ll的指令。此函数在board/smdk2410/nand_read,c中定义了。但是uboot的源码包中没有这个函数,我们可以从vivi中获取这个函数~通过这个函数来将uboot读取到RAM中。

执行完这个函数以后,接着从copy_myself返回!!!检验一下是否将uboot已全部读取到TEXT_BASE中,如果成功,则继续执行下一条关键指令!!

ldr pc,_start_armboot 将这个函数的地址值赋给PC,直接实现跳转!

_start_armboot: .word start_armboot

开始跳转到/lib_arm/board.c中定义的start_armboot函数中执行了。到现在为止就是第2阶段正式开始了。Start.S文件分析也到此结束!!


2board.c也非常重要,并且start_armboot()函数定义lib_arm/board.c文件中。

board.c是个非常重要的文件。包括了CS8900网卡驱动函数的声明、内存函数初始化的函数mem_malloc_initnandflash初始化、波特率初始化函数、输出一些硬件信息....start_armboot本身主要完成网络的设置等,并最终进入到main_loop()函数。即循环等待。下面先介绍几个重要的数据结构。


1初始化函数序列init_sequence[]
  init_sequence[]
数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。

  init_fnc_t *init_sequence[] = {
         cpu_init,             /*
基本的处理器相关配置 -- cpu/arm920t/cpu.c */
         board_init,           /*
基本的板级相关配置 -- board/smdk2410/smdk2410.c */
         interrupt_init,       /*
初始化例外处理 -- cpu/arm920t/s3c24x0/interrupt.c */
         env_init,             /*
初始化环境变量 -- common/env_flash.c */
         init_baudrate,        /*
初始化波特率设置 -- lib_arm/board.c */
         serial_init,          /*
串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */
         console_init_f,       /*
控制台初始化阶段1 -- common/console.c */
         display_banner,       /*
打印u-boot信息 -- lib_arm/board.c */
         dram_init,            /*
配置可用的RAM -- board/smdk2410/smdk2410.c */
         display_dram_config,  /*
显示RAM的配置大小 -- lib_arm/board.c */
         NULL,
  };


2 全局数据结构

typedef struct global_data {全局数据

bd_t *bd; //板子数据指针

unsigned long flags;//指示标志,如设备已经初始化等。

unsigned long baudrate;//波特率定义

unsigned long have_console; /* serial_init() was called 串口初始化*/

unsigned long reloc_off; /* Relocation Offset */

unsigned long env_addr; /* Address of Environment struct 环境参数地址*/

unsigned long env_valid; /* Checksum of Environment valid? 环境参数CRC检验有效标志*/

unsigned long fb_base; /* base address of frame buffer 帧地址*/

#ifdef CONFIG_VFD

unsigned char vfd_type; /* display type */

#endif

#if 0

unsigned long cpu_clk; /* CPU clock in Hz! */

unsigned long bus_clk;

unsigned long ram_size; /* RAM size */

unsigned long reset_status; /* reset status register at boot */

#endif

void **jt; /* jump table */

} gd_t; include/asm-arm/Global_data.h中定义。全局数据变量指针,它保存了U-boot运行时需要的全局数据。


3 开发板数据结构

typedef struct bd_info {

int bi_baudrate; /* serial console baudrate */

unsigned long bi_ip_addr; /* IP Address */

unsigned char bi_enetaddr[6]; /* Ethernet adress即网卡地址 */

struct environment_s *bi_env;

ulong bi_arch_number; /* unique id for this board */

ulong bi_boot_params; /* where this board expects params启动参数 */

struct /* RAM configuration */

{

ulong start;

ulong size;

} bi_dram[CONFIG_NR_DRAM_BANKS];

#ifdef CONFIG_HAS_ETH1

/* second onboard ethernet port */

unsigned char bi_enet1addr[6];

#endif

} bd_t; include/asm-arm/u-boot.h中定义。描述开发板的数据结构。


4 voidstart_armboot (void)函数分析
   
{
       //
全局数据变量指针gd占用r8
          DECLARE_GLOBAL_DATA_PTR;
         
          /*
给全局数据变量gd安排空间*/
          gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
          memset ((void*)gd, 0, sizeof (gd_t));
         
          /*
给板子数据变量gd->bd安排空间*/
          gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
          memset (gd->bd, 0, sizeof (bd_t));
          monitor_flash_len = _bss_start - _armboot_start;//
u-boot的长度。
         
          /*
顺序执行init_sequence数组中的初始化函数 */
          for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
                 if ((*init_fnc_ptr)() != 0) {
                         hang ();
                 }
          }
         
          /*
配置可用的Flash */
          size = flash_init ();
      
 ……
          /*
初始化堆空间 */
          mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
          /*
重新定位环境变量, */
          env_relocate ();
          /*
从环境变量中获取IP地址 */
          gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
          /*
以太网接口MAC 地址 */
          ……
          devices_init ();      /* 
设备初始化 */
          jumptable_init ();  //
跳转表初始化
          console_init_r ();    /*
完整地初始化控制台设备 */
          enable_interrupts (); /*
使能中断处理 */
          /*
通过环境变量初始化 */
          if ((s = getenv ("loadaddr")) != NULL) {
                  load_addr = simple_strtoul (s, NULL, 16);
          }
          /* main_loop()
循环不断执行 */
          for (;;) {
                  main_loop ();      /*
主循环函数处理执行用户命令 -- common/main.c */
          }
   }


U-boot-arm.h中定义了这个变量。

extern ulong _armboot_start; /* code start */

extern ulong _bss_start; /* code + data end == BSS start */

extern ulong _bss_end; /* BSS end */

extern ulong IRQ_STACK_START; /* top of IRQ stack */

extern ulong FIQ_STACK_START; /* top of FIQ stack */



分析完start_armboot函数后,下面再分析几个比较重要的文件。

1include/configs/smdk2410.h

主要是定义了一些宏。用来选择处理器,设置时钟,定义内存池的大小。设置网卡CS8900,还有串口与时钟的设置。

一些命令的定义。主要是CONFIG_COMMANDS宏中。看名称就能猜到是定义命令的意思了。包括CFG_CMD_CACHE命令等等,如果要设置nandflash启动,那么必须要使CFG_CMD_NANDCFG_CMD_PINGCFG_CMD_NET三个选项有效。接下去的程序就定义了一些关于IP地址,网关的设置。以及关于内存起始地址CFG_MEMTEST_STARTCFG_MEMTEST_END的定义。还有一些栈的设置。包含了许多宏地址的定义。因此这个文件很重要!!最后一些就包括了FLASH的环境设置。要进行修改。将SDRAM的起始地址0x30000000 flashbank1 0x00000000 联系起来。下表是一些比较重要的宏定义。

1

PHY_FLASH_1

0X00000000

CFG_FLASH_BASE

PHY_FLASH_1

CFG_PROMPT

SMDK2410#” 开发板的用户名称

CFG_MEMTEST_START

0X30000000

CFG_MEMTEST_END

0X33F00000

CFG_LOAD_ADDR

0X33000000默认的加载地址

PHYS_SDRAM_1

OX30000000 SDRAM bank1

PHYS_SDRAM_1_SIZE

0X04000000

CFG_ENV_IS_IN_FLASH

1

CFG_ENV_SIZE

0X10000 64K

这个文件的主要功能就分析完了。我对源码做了比较好的分析,这里就不一一列举了。利用source insight进行查看。


2/include/Cmd_confdefs.h 这个文件包含了全部命令的宏定义,好像是定义宏的地址~~~~smdk2410.h定义部分命令后,则必须将这个头文件include 进来。


3/include/s3c2410.h/include/s3c24x0.h

首先先分析s3c24x0.h文件。这个文件中定义了所有的硬件资源寄存器。包括内存控制,USB控制,中断、DMA等等。主要是通过这些结构体定义的对象与s3c2410.h中定义的SFR基地址结合,定义这个某个寄存器有哪些状态寄存器。

该文件中。主要定义了特殊寄存的基地址。SFR的范围0x48000000~0x5A000000

s3c24x0.h中的部分内容

typedef struct {

S3C24X0_REG32 BWSCON;

S3C24X0_REG32 BANKCON[8];

S3C24X0_REG32 REFRESH;

S3C24X0_REG32 BANKSIZE;

S3C24X0_REG32 MRSRB6;

S3C24X0_REG32 MRSRB7;

} /*__attribute__((__packed__))*/ S3C24X0_MEMCTL

定义一个s3c24x0_MEMCTL对象,这个结构体中都是内存的相关寄存器。

下面是s3c2410.h中的部分代码。利用24x0中的对象来获取基地址。两者结合~

static inline S3C24X0_MEMCTL * const S3C24X0_GetBase_MEMCTL(void)

{

return (S3C24X0_MEMCTL * const)S3C24X0_MEMCTL_BASE;

先定义S3C24X0_MEMCTL结构体对象, 再在s3c2410.h中定义一个const函数,表示这个函数的返回值是不变的。


(4)/include/common.h/common/cmd_nand.c/common/command.c common目录下包括一些通用的函数、如网络、硬盘、串口、nandflash等等.

这个文件主要定义了uboot的所有命令的形式。

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}每个命令就是一个cmd_tbl_t的对象。即每个命令都有一个结构体来描述。包括命令名字、参数的最大个数、重复数、命令执行函数、用法、帮助。从控制台中输入的命令是又command.c完成的,而find_cmd函数用来匹配具体的某个命令。


7内存分布情况从高到低分配。

对于smdk2410,RAM范围从0x30000000~0x34000000. u-boot占用高端内存区的1M大小。从前面的分析可知内存分配大致如下:



内存图的分析:在start.S

gdbd共占128个字节的大小。在给malloc区和bd数据结构分配后,如果定义了IRQ,则给栈分配IRQ+FIQ8K大小的区。CONFIG_STACKSIZE_IRQ=4K如果没有定义,则至少保存12个字节。在smdk2410.h有定义。从0x33f00000x340000001M地址,是留给uboot使用的。下面是从代码中总结出的信息,不知道对不对。需要进一步验证。DW_STACK_START,建立堆栈,栈起点0x33f00000,大小为0x8000 32K大小的栈?UBOOT_RAM_BASE _TEXT_BASE相同0x33f80000 _arm_boot_start也为0x33f80000.uboot源码分析暂到此结束。其实还是有许多不明白的地方。以后发现什么新的了再添加吧

未完。。。。。。。。。。见下一篇





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

28796182012-10-30 19:53:10

THANKS,