Chinaunix首页 | 论坛 | 博客
  • 博客访问: 156933
  • 博文数量: 152
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-08 15:46
文章分类

全部博文(152)

文章存档

2016年(152)

我的朋友

分类: LINUX

2016-06-05 15:17:30

uboot启动过程中各函数功能详细分析

本文主要分析流程中,各函数的功能。按启动顺序罗列一下启动函数执行细节。我们首先从函数start_armboot流程进行分析:

1)DECLARE_GLOBAL_DATA_PTR; 这个宏在include/global_data.h中

#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")   
/* 声明一个寄存器变量gd占用r8。这个宏在所有需要引用全局数据指针gd_t *gd的源码中都有声明 
 * 这个声明也避免编译器把r8分配给其他的变量,所以gd就是r8,这个指针变量不占用内存。
 */
/* 定义gd为gd_t类型指针,存储在寄存器r8中 */
/* register:表示变量对于执行速度非常重要,因此应该放在机器的寄存器中(寄存器独立于内存,通常在处理器芯片上) */
/* volatile:用于指定变量的值可以由外部过程异步修改,例如中断例程 */

2)gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); 位于lib_arm/board.c
/* 对全局数据区进行地址分配,_armboot_start为0x33f80000,CFG_MALLOC_LEN是:堆的大小+环境数据区大小。
 * 可以在/config/smdk2410.h中找到CFG_MALLOC_LEN,其大小定义为192K
*/

3)gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); 位于lib_arm/board.c
/*
 * 分配板子数据区bd首地址
 * 这里,我们可以结合start.s中栈的分配来理解:
 * 
  stack_setup:
 ldr r0, _TEXT_BASE  /* upper 128 KiB: relocated uboot   */
//设置SDRAM的基地址_TEXT_BASE = 0x33F80000
  //下面这两行代码可以参考工作日志上的图来理解
  sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */
 sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */
 
#ifdef CONFIG_USE_IRQ
 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
 sub sp, r0, #12  /* leave 3 words for abort-stack    */

由这些汇编代码,我们可以得出如下内存分配结构:

显示缓冲区                (.bss_end~0x34000000)
uboot(bss,data,text)  (0x33f00000~.bss_end)
heap(for malloc)
gd(global data)
bd(board data)
stack
... ...
nor flash                  (0~2M)

有网友这样分析:
gd和bd共占128个字节的大小。
在给malloc区和bd数据结构分配后,如果定义了IRQ,则给栈分配IRQ+FIQ占8K大小的区。CONFIG_STACKSIZE_IRQ=4K。
如果没有定义,则至少保存12个字节。在smdk2410.h有定义。
从0x33f00000到0x34000000的1M地址,是留给uboot使用的, 即uboot占用高端内存区。
下面是从网络上获得的信息(作者说是从代码中得出的结论,需要进一步验证)。DW_STACK_START,建立堆栈,
栈起点0x33f00000,大小为0x80000 32K大小的栈。UBOOT_RAM_BASE 与_TEXT_BASE(0x33f80000)相同,
_arm_boot_start也为0x33f80000。
*/

4)下面,我们来分析初始化列表init_sequence[]中的函数:

A. cpu_init(),定义于cpu/arm920t/cpu.c

   分配IRQ、FIQ栈底地址,由于没有定义CONFIG_USE_IRQ,所以相当于空实现
   
B. board_init:板级初始化,定义于/board/smdk2410/smdk2410.c
   
   设置PLL时钟,GPIO,使能I/D cache
   设置bd信息: 
   gd->bd->bi_arch_number = MACH_TYPE_SMDK2410; /* arch number of SMDK2410-Board 板子的ID*/
  gd->bd->bi_boot_params = 0x30000100;    /* adress of boot parameters 内核启动参数存放地址 */
C. interrupt_init;定义于cpu/arm920t/s3c24x0/interrupt.c
   
   用于初始化smdk2410的PWM timer 4,使其能够自动装载计数值,恒定地产生时间中断信号,但是中断被屏蔽了,用不上。
D. env_init;定义于以下几个文件中(为什么):
Env_dataflash.c 
Env_eeprom.c 
Env_flash.c 
Env_nand.c 
Env_nowhere.c 
Env_nvram.c 
功能:指定环境区的地址。
default_enviroment是默认的环境参数设置。
gd->env_addr = (ulong)&default_enviroment[0];
gd->env_valid = 0;

E. init_baudrate: 初始化全局数据区中波特率的值

static int init_baudrate (void)
{
 char tmp[64]; /* long enough for environment variables */
 int i = getenv_r ("baudrate", tmp, sizeof (tmp));
 gd->bd->bi_baudrate = gd->baudrate = (i > 0)
   ? (int) simple_strtoul (tmp, NULL, 10)
   : CONFIG_BAUDRATE;

 return (0);
}

F.serial_init;串口通讯设置,定义于:

Serial.c定义于:

 u-boot-1.1.6\cpu\arm920t\s3c24x0):int serial_init (void)

功能:根据bd中波特率值和pclk,设置串口寄存器。

G. console_init_f;控制台前期初始化common/console.c
由于标准设备还没有初始化(gd->flags&GD_FLG_DEVINIT=0), 这时控制台使用串口作为控制台。函数只有一句:gd->have_console=1;

H. dram_init;初始化内存RAM的信息。位于board/smdk2410/smdk2410.c
   其实就是给gd->bd中的内存信息表赋值。

   int dram_init (void)
{
 gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
 gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

 return 0;
}

5)下面,我们来看flash_init;本函数定义在board/smdk2410/flash.c
   flash.c这个文件与具体平台密切相关,smdk2410使用的flash与s3c2410不一样(一样),所以移植的时候必须重写这个程序。
   那么,flash_init()到底要做什么操作呢?
   首先有一个变量,flash_info_t flash_info[CFG_MAX_FLASH_BANKS];这个变量用来记录flash的信息。
   flash_info_t结构体的定义:
   未找到?????????
   
   把视频帧缓冲区设置在bss_end后面。
   未找到代码????????????
   
    /* armboot_start is defined in the board-specific linker script */
  mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //位于board.c
   设置heap区,供malloc使用,下面的变量和函数定义在lib_arm/board.c。
   malloc可用内存由mem_malloc_start,mem_malloc_end指定,而当前分配的位置则是mem_malloc_brk。
   mem_malloc_init负责初始化这三个变量。malloc则通过sbrk函数来使用和管理这片内存。

   
   /*
 * Begin and End of memory area for malloc(), and current "brk"
 */
static ulong mem_malloc_start = 0;
static ulong mem_malloc_end = 0;
static ulong mem_malloc_brk = 0;

static
void mem_malloc_init (ulong dest_addr)
{
 mem_malloc_start = dest_addr;
 mem_malloc_end = dest_addr + CFG_MALLOC_LEN;
 mem_malloc_brk = mem_malloc_start;

 memset ((void *) mem_malloc_start, 0,
   mem_malloc_end - mem_malloc_start);
}

void *sbrk (ptrdiff_t increment)
{
 ulong old = mem_malloc_brk;
 ulong new = old + increment;

 if ((new < mem_malloc_start) || (new > mem_malloc_end)) {
  return (NULL);
 }
 mem_malloc_brk = new;

 return ((void *) old);
}
   
这是flash_init的定义:
ulong flash_init (void)
{
 int i, j;
 ulong size = 0;

 for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
  ulong flashbase = 0;

  flash_info[i].flash_id =
#if defined(CONFIG_AMD_LV400)
   (AMD_MANUFACT & FLASH_VENDMASK) |
   (AMD_ID_LV400B & FLASH_TYPEMASK);
#elif defined(CONFIG_AMD_LV800)
   (AMD_MANUFACT & FLASH_VENDMASK) |
   (AMD_ID_LV800B & FLASH_TYPEMASK);
#else
#error "Unknown flash configured"
#endif
   flash_info[i].size = FLASH_BANK_SIZE;
  flash_info[i].sector_count = CFG_MAX_FLASH_SECT;
  memset (flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
  if (i == 0)
   flashbase = PHYS_FLASH_1;
  else
   panic ("configured too many flash banks!\n");
  for (j = 0; j < flash_info[i].sector_count; j++) {
   if (j <= 3) {
    /* 1st one is 16 KB */
    if (j == 0) {
     flash_info[i].start[j] =
      flashbase + 0;
    }

    /* 2nd and 3rd are both 8 KB */
    if ((j == 1) || (j == 2)) {
     flash_info[i].start[j] =
      flashbase + 0x4000 + (j -
              1) *
      0x2000;
    }

    /* 4th 32 KB */
    if (j == 3) {
     flash_info[i].start[j] =
      flashbase + 0x8000;
    }
   } else {
    flash_info[i].start[j] =
     flashbase + (j - 3) * MAIN_SECT_SIZE;
   }
  }
  size += flash_info[i].size;
 }

 flash_protect (FLAG_PROTECT_SET,
         CFG_FLASH_BASE,
         CFG_FLASH_BASE + monitor_flash_len - 1,
         &flash_info[0]);

 flash_protect (FLAG_PROTECT_SET,
         CFG_ENV_ADDR,
         CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]);

 return size;
}


6)env_relocate()环境参数区重定位
由于初始化了heap区,所以可以通过malloc重新分配一块环境参数区,但是没有必要,因为默认的环境参数已经重新定位到RAM中去了。
......................

7)IP,MAC地址的初始化,主要是从环境中读,然后赋给gd->bd对应域就OK。
8)devices_init();定义于common/devices.c

int devices_init (void) //下面注释掉的,都是因为对应的编译选项没有意义。
{
#ifndef CONFIG_ARM     /* already relocated for current ARM implementation */
 ulong relocation_offset = gd->reloc_off;
 int i;

 /* relocate device name pointers */
 for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {
  stdio_names[i] = (char *) (((ulong) stdio_names[i]) +
      relocation_offset);
 }
#endif

 /* Initialize the list */
 devlist = ListCreate (sizeof (device_t)); //创建设备列表

 if (devlist == NULL) {
  eputs ("Cannot initialize the list of devices!\n");
  return -1;
 }
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
 i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE); //初始化i2c接口,i2c接口没有注册到devlist中去
#endif
//#ifdef CONFIG_LCD
 //drv_lcd_init ();
//#endif
//#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)
 //drv_video_init ();
//#endif
//#ifdef CONFIG_KEYBOARD
// drv_keyboard_init ();
//#endif
//#ifdef CONFIG_LOGBUFFER
// drv_logbuff_init ();
//#endif
 drv_system_init ();v //这里其实是定义了一个串口设备,并且注册到devlist中去
//#ifdef CONFIG_SERIAL_MULTI
 //serial_devices_init ();
//#endif
//#ifdef CONFIG_USB_TTY
// drv_usbtty_init ();
//#endif
//#ifdef CONFIG_NETCONSOLE
 //drv_nc_init ();
//#endif

 return (0);
}
 devices_init()这个函数执行完之后,创建了devlist,但是只有一个串口设备注册在内。显然devlist中的设备都是可以作为console的
 
9)jumptable_init();用于初始化gd->jt,这里请注意:u-boot-1.1.6 的jumptable只起到登记函数地址的作用,并没有其他的作用。
10)console_init_r();位于/common/console.c这是后期控制台初始化
   主要过程:查看环境参数stdin,stdout,stderr中对标准IO的指定的设备名称,再按照环境指定的名称呢个搜索devlist,将搜索到的设备指针赋给标准IO数组stdio_devices[]。
   置gd->flag标志GD_FLG_DEVINIT。这个标志影响putc,getc函数的实现

//未定义标志时直接由串口serial_getc和serial_putc实现,定义以后通过标准设备数组stdio_devices[]
   中的putc和getc来实现。

   void putc (const char c)
{
#ifdef CONFIG_SILENT_CONSOLE
 if (gd->flags & GD_FLG_SILENT)  //GD_FLG_SILENT无输出标志
  return;
#endif

 if (gd->flags & GD_FLG_DEVINIT) {
  /* Send to the standard output */
  fputc (stdout, c);
 } else {
  /* Send directly to the handler */
  serial_putc (c);   //未初始化时直接从串口输出
 }
}

void fputs (int file, const char *s)
{
 if (file < MAX_FILES)
  stdio_devices[file]->puts (s);
}

使用devlist,std_device[]的原因:
为了更灵活地实现标准IO重定向,任何可以作为标准IO的设备,如USB键盘,LCD屏,串口等都可以对应一个device_t的结构体变量,
只需要实现getc和putc等函数,就能加入到devlist列表中去,也就可以被assign为标准IO设备std_device中去
。如函数 

/////////
int console_assign (int file, char *devname); /* Assign the console 重定向标准输入输出*/
这个函数功能就是把名为devname的设备重定向为标准IO文件file(stdin,stdout,stderr)。其执行过程是在devlist中查找devname的设备,
返回这个设备的device_t指针,并把指针值赋给std_device[file]。

////////

11)enable_interrupts(),/cpu/arm920t/interrupt.c 使能中断。由于CONFIG_USE_IRQ没有定义,空实现。

#ifdef CONFIG_USE_IRQ
/* enable IRQ interrupts */
void enable_interrupts (void)
{
 unsigned long temp;
 __asm__ __volatile__("mrs %0, cpsr\n"
        "bic %0, %0, #0x80\n"
        "msr cpsr_c, %0"
        : "=r" (temp)
        :
        : "memory");
}

void enable_interrupts (void)
{
 return;
}

12)设置CS8900的MAC地址 (board.c)
 /* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_CS8900
 cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif

13)初始化以太网(board.c)
eth_initialize(gd->bd); //bd中IP,MAC已经初始化

14)main_loop();定义于/common/main.c
至此,所有初始化工作已经工作完毕。main_loop在标准转入设备中接收命令行,然后分析,查找,执行

from:

http://zqwt.012.blog.163.com/blog/static/1204468420103139147872/

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