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

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_IRQsub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)#endifsub 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.cEnv_eeprom.cEnv_flash.cEnv_nand.cEnv_nowhere.cEnv_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.cflash.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;

staticvoid 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 ();//#endifdrv_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_CONSOLEif (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_CS8900cs8900_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:

你可能感兴趣的:(boot_jump_linux函数,uboot 启动过程中各函数功能详细分析)