U-Boot启动第二阶段代码分析.doc_第1页
U-Boot启动第二阶段代码分析.doc_第2页
U-Boot启动第二阶段代码分析.doc_第3页
U-Boot启动第二阶段代码分析.doc_第4页
U-Boot启动第二阶段代码分析.doc_第5页
已阅读5页,还剩54页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

U-Boot启动第二阶段代码分析 (2012-03-23 15:10)一键转载标签: 二 分类: ubootU-Boot第一阶段的启动流程。(nandflash启动,把nand的4k代码考到sram中,因为nand没址线,不能映射到内存,所以通过sram进行过度,sram中4k代码把整个uboot拷贝到sdram上,初始化好堆栈,为c语言提供条件,进入uboot的第二阶段! )这个阶段主要是初始化硬件设备,为加载U-Boot的第二阶段代码准备RAM空间最后跳转到lib_arm/board.c中start_armboot函数,这是第二阶段的入口点。 在上一篇文章中,我们介绍了u-boot启动的时候汇编语言的部分,当时我们进行了一些简单的初始化,并且为C语言的执行建立的环境(堆栈),现在我们看看当从汇编语言转到C语言的时候执行的第一个函数( start_armboot (),在lib_armboard.c中),该函数进行了一系列的外设初始化,然后调用main_loop (),根据配置来选择是直接加载Linux内核还是进入等待命令模式。1、在介绍该函数之前,我们需要看一看几个数据结构,这些是u-boot中几个重要的数据结构:(1)gd_t结构体U-Boot使用了一个结构体gd_t来存储全局数据区的数据,这个结构体在include/asm-arm/global_data.h中定义如下: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? */ unsigned long fb_base; /* base address of frame buffer */#ifdef CONFIG_VFD /我们一般没有配置这个,这个是frame buffer的首地址 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; /* * Global Data Flags */#define GD_FLG_RELOC 0x00001 /* Code was relocated to RAM */#define GD_FLG_DEVINIT 0x00002 /* Devices have been initialized */#define GD_FLG_SILENT 0x00004 /* Silent mode */ #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (r8) 在global_data.h中U-Boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址:#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (r8) DECLARE_GLOBAL_DATA_PTR定义一个gd_t全局数据结构的指针,这个指针存放在指定的寄存器r8中。这个声明也避免编译器把r8分配给其它的变量。任何想要访问全局数据区的代码,只要代码开头加入“DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。 根据U-Boot内存使用图中可以计算gd的值:gd = TEXT_BASE CONFIG_SYS_MALLOC_LEN sizeof(gd_t) 2)bd_t 保存与板子相关的配置参数bd_t在include/asm-arm/u-boot.h中定义如下:typedef struct bd_info int bi_baudrate; /* 串口通讯波特率 */ unsigned long bi_ip_addr; /* IP地址 */ unsigned char bi_enetaddr6; /* 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配置信息 */ ulong start; ulong size; bi_dramCONFIG_NR_DRAM_BANKS; /在我的板子上DRAM配置是1个#ifdef CONFIG_HAS_ETH1 /* second onboard ethernet port */ unsigned char bi_enet1addr6;#endif bd_t; #define bi_env_data bi_env-data#define bi_env_crc bi_env-crcU-Boot启动内核时要给内核传递参数,这时就要使用gd_t,bd_t结构体中的信息来设置标记列表。3). 初始化函数列表(以数组的形式)相关代码在lib-arm/board.c中定义typedef int (init_fnc_t) (void); /*这是使用typedef定义一个init_fnc_t为函数类型,该函数返回int型,无参数*/ int print_cpuinfo (void); /* test-only */ init_fnc_t *init_sequence定义一个init_fnc_t指针类型的数组。简单的说就是定义了个函数指针数组,指向一系列cpu初始化函数。init_fnc_t *init_sequence = /*全局变量init_sequence的定义,*/ cpu_init, /* basic cpu dependent setup CPU的相关配置,如初始化IRQ/FIQ模式的栈cpu/arm920t/cpu.c*/ board_init, /* basic board dependent setup开发板相关配置,是对板子的初始化,设置MPLL,改变系统时钟,以及一些GPIO寄存器的值,还设置了U-Boot机器码和内核启动参数地址,它是开发板相关的函数,比如2410是在:board/smdk2410/smdk2410.c中实现*/ interrupt_init, /* set up exceptions 初始化中断,在cpu/arm920t/s3c24x0/interrupts.c实现*/ env_init, /* initialize environment 初始化环境变量,检查flash上的环境变量是否有效common/env_flash.c 或common/env_nand.c */ init_baudrate, /* initialze baudrate settings 初始化波特率lib_arm/board.c */ serial_init, /* serial communications setup串口初始化串口初始化后我们就可以打印信息了cpu/arm920t/s3c24x0/serial.c */ console_init_f, /* stage 1 init of console 控制台初始化第一阶段common/console.c */ display_banner, /* say that we are here通知代码已经运行到该处,打印U-Boot版本、编译的时间- lib_arm/board.c */#if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, /* display cpu info (and speed) */#endif#if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* display board info */#endif dram_init, /* configure available RAM banks 配置可用的内存区,检测系统内存映射,设置内存的起始地址和大小board/smdk2410/smdk2410.c*/ display_dram_config, NULL,;可以看出这里定义了一个指针数组,它的每一个元素都是指针变量,这些指针变量指向的类型为init_fnc_t,在C语言中函数的入口地址就是函数名,所以这里使用一系列函数名来初始化这个数组。现在来看看到底做了哪些初始化工作int cpu_init (void) /* * setup up stacks if necessary */#ifdef CONFIG_USE_IRQ IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4; FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;#endif return 0;这个函数cpu/arm920t/cpu.c在中实现,其实这个函数没有做任何工作,因为CONFIG_USE_IRQ这个宏没有定义,那么怎么知道这个宏是否被定义了呢,在使用SourceInsight的搜索功能时,发现有些宏会在很多头文件被定义,而我们又很难判断这些头文件是否被当前的c文件包含了,我使用一个简便的方法,利用编译器的预处理功能,#ifdef CONFIG_USE_IRQ#error CONFIG_USE_IRQ_xxxxxx DECLARE_GLOBAL_DATA_PTR; IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4; FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;#endif 这样如果这个宏被定义了,那么编译时就会报错输出#error CONFIG_USE_IRQ_xxxxxx,并终止编译.对于smdk2410来说这个宏CONFIG_USE_IRQ没定义,实际上就是把IRQ_STACK_START, FIQ_STACK_START指到RAM中的IRQ stuff区域。int board_init (void) 这个函数board/smdk2410/smdk2410.c在中实现/获取power和clock及GPIO方面的寄存器地址,稍后的操作会对这些寄存器操作,需要看到的是,象S3C24X0_CLOCK_POWER里面的field对象都是按照实际寄存器的地址来安排的 S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO(); /* to reduce PLL lock time, adjust the LOCKTIME register */ /降低PLL的lock time的值,具体含义可参考data sheet clk_power-LOCKTIME = 0xFFFFFF; /* configure MPLL */ clk_power-MPLLCON = (M_MDIV 12) + (M_PDIV UPLLCON = (U_M_MDIV 12) + (U_M_PDIV GPACON = 0x007FFFFF; gpio-GPBCON = 0x00044555; gpio-GPBUP = 0x000007FF; gpio-GPCCON = 0xAAAAAAAA; gpio-GPCUP = 0x0000FFFF; gpio-GPDCON = 0xAAAAAAAA; gpio-GPDUP = 0x0000FFFF; gpio-GPECON = 0xAAAAAAAA; gpio-GPEUP = 0x0000FFFF; gpio-GPFCON = 0x000055AA; gpio-GPFUP = 0x000000FF; gpio-GPGCON = 0xFF95FFBA; gpio-GPGUP = 0x0000FFFF; gpio-GPHCON = 0x002AFAAA; gpio-GPHUP = 0x000007FF; /* SMDK2410开发板的机器码 */ */ gd-bd-bi_arch_number = MACH_TYPE_SMDK2410; /* adress of boot parameters内核启动参数地址,运行时在linux内核之下/ gd-bd-bi_boot_params = 0x30000100; /使能指令cache和数据cache icache_enable(); dcache_enable(); return 0;现在说一下icache_enable、dcache_enable函数,它定义在cpu/arm920t/cpu.c中,这两个函数是通过修改CP15的c1寄存器来实现的,使能cache很简单,只要把协处理器15的相关位打开就行了,这里来是将c1的I、C位置1,来开启Icache、DCaches。我这里只分析icache_enable,dcache_enable类似。icache_enable具体实现如下:void icache_enable (void) ulong reg; reg = read_p15_c1 (); /* get control reg. 获取CP15的c1寄存器值存到reg中*/ cp_delay (); write_p15_c1 (reg | C1_IC);/*这里将C1寄存器的I、C位置1,来开启Icache、Dcaches,关于CP15的c1寄存器的格式可参看前面u-boot启动第一阶段分析的cpu_init_crit函数部分。这里须要理解的是read_p15_c1与write_p15_c1函数,它们分别在cpu/arm920t/cpu.c中定义如下:static unsigned long read_p15_c1 (void) unsigned long value; _asm_ _volatile_( mrc p15, 0, %0, c1, c0, 0 read control regn,%0是参数传递时R0寄存器,其功能是读取CP15的c1寄存器值放到r0中。 : =r (value) : : memory); #ifdef MMU_DEBUG printf (p15/c1 is = %08lxn, value);#endif return value; 返回读取CP15的c1寄存器的值/* write to co-processor 15, register #1 (control register) */static void write_p15_c1 (unsigned long value)#ifdef MMU_DEBUG printf (write %08lx to p15/c1n, value);#endif _asm_ _volatile_( mcr p15, 0, %0, c1, c0, 0 write it backn 保存r0的值到控制寄存器CP15的c1寄存器中,因为函数参数传递时,第一个参数都是放在r0寄存器中的。 : : r (value) : memory); read_p15_c1 ();接下来该看初始化函数: interrupt_init,在cpu/arm920t/s3c24x0/interrupts.c实现interrupt_init代码如下:int timer_load_val = 0;static ulong timestamp;static ulong lastdec;int interrupt_init (void) 初始化timer4相关寄存器,用于产生10ms定时中断信号。 S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();/返回定时器配置寄存器地址0x51000000,即TCFG0寄存器地址。 /*这里使用定时器4,定时器4有只有一个内部定器没有输出管脚*/ /* prescaler for Timer 4 is 16 */ timers-TCFG0 = 0x0f00; if (timer_load_val = 0) /* * for 10 ms clock period PCLK with 4 bit divider = 1/2 * (default) and prescaler = 16. Should be 10390 * 33.25MHz and 15625 50 MHz */ timer_load_val = get_PCLK()/(2 * 16 * 100); /PCLK(返回PCLK频率 /* load value for 10 ms timeout */ lastdec = timers-TCNTB4 = timer_load_val;/设置计数缓存寄存器初始值。 /*设置定时器4手动更新,自动加载模式,并关闭定时器4*/ timers-TCON = (timers-TCON & 0x0700000) | 0x600000; /* auto load, 启动Timer 4 */ timers-TCON = (timers-TCON & 0x0700000) | 0x500000; timestamp = 0; return (0);对着datasheet来看这个函数, 实际上这个函数使用timer 4来作为系统clock, 即时钟滴答, 10ms一次,到点就产生一个中断,但由于此时中断还没打开所以这个中断不会响应。接着看env_init: 由于我们在inculde/configs/smdk2410.h下定义了CFG_ENV_IS_IN_FLASH,因此该函数位于common/env_flash.c下int env_init(void)#ifdef CONFIG_OMAP2420H4 int flash_probe(void); if(flash_probe() = 0) goto bad_flash;#endif if (crc32(0, env_ptr-data, ENV_SIZE) = env_ptr-crc) gd-env_addr = (ulong)&(env_ptr-data); gd-env_valid = 1;使用include/configs/smdk2410.h配置的环境变量则设置环境变量可用标志 return(0); /* env_ptr在前面定义为env_t *env_ptr = (env_t *)CFG_ENV_ADDR;而CFG_ENV_ADDR被定义在include/configs/smdk2410.h中了,这里判断如果校验正确(即CFG_ENV_ADDR被定义了)则环境变量的存放地址使用smdk2410.h中定义的,否则使用后面的默认的环境变量值default_environment数组*/#ifdef CONFIG_OMAP2420H4bad_flash:#endif gd-env_addr = (ulong)&default_environment0; gd-env_valid = 0; 使用默认的环境配置变量则设置环境变量不可用标志 return (0);这个函数主要是在gd里保存环境变量的存放地址。一般使用默认的环境变量值即default_environment数组,它在common/env_commom.c中定义(关于u-boot环境变量更详细的说明请看U-BOOT环境变量实现一文档。地址:/hxliu_leo/article/details/5315011)uchar default_environment = #ifdef CONFIG_BOOTARGS bootargs= CONFIG_BOOTARGS 0#endif#ifdef CONFIG_BOOTCOMMAND bootcmd= CONFIG_BOOTCOMMAND 0#endif#if defined(CONFIG_BOOTDELAY) & (CONFIG_BOOTDELAY = 0) bootdelay= MK_STR(CONFIG_BOOTDELAY) 0#endif。#ifdef CONFIG_EXTRA_ENV_SETTINGS CONFIG_EXTRA_ENV_SETTINGS#endif 0;可见环境变量以如下的方式存放在数组中 Name=value并且以”/0”结束, 而类似CONFIG_BOOTARGS的宏都定义在板子自己的配置文件中即smdk2410.h里。接下来看init_baudrate,定义在lib_arm/board.cstatic int init_baudrate (void) char tmp64; /* 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);该函数从上面刚初始化好的环境变量列表里找波特率值,如果没有就赋初始值为CONFIG_BAUDRATE。继续往下看serial_init,cpu/arm920t/s3c24x0/serial.cint serial_init (void) serial_setbrg ();/设置波特率,停止位等 return (0);void serial_setbrg (void) cpu/arm920t/s3c24x0/serial.c S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR); int i; unsigned int reg = 0; /* value is calculated so : (int)(PCLK/16./baudrate) -1 */ reg = get_PCLK() / (16 * gd-baudrate) - 1; /* FIFO enable, Tx/Rx FIFO clear */ uart-UFCON = 0x07; uart-UMCON = 0x0; /* Normal,No parity,1 stop,8 bit */ uart-ULCON = 0x3; /* * tx=level,rx=edge,disable timeout int.,enable rx error int., * normal,interrupt or polling */ uart-UCON = 0x245; uart-UBRDIV = reg; #ifdef CONFIG_HWFLOW uart-UMCON = 0x1; /* RTS up */#endif for (i = 0; i have_console = 1; #ifdef CONFIG_SILENT_CONSOLE if (getenv(silent) != NULL) gd-flags |= GD_FLG_SILENT; /设置控制台模式#endif return (0);该函数初始化了几个控制台相关的标记。接着看display_banner: lib_arm/board.cstatic int display_banner (void) printf (nn%snn, version_string); /打印U-BOOT的版本信息 debug (U-Boot code: %08lX - %08lX BSS: - %08lXn, _armboot_start, _bss_start, _bss_end); /打印U-BOOT代码位置#ifdef CONFIG_MODEM_SUPPORT debug (Modem Support enabledn);#endif#ifdef CONFIG_USE_IRQ debug (IRQ Stack: %08lxn, IRQ_STACK_START); debug (FIQ Stack: %08lxn, FIQ_STACK_START);#endif return (0);这个函数就是在控制台上打印一些系统信息。接着看dram_init:int dram_init (void) 这个函数board/smdk2410/smdk2410.c在中实现 gd-bd-bi_dram0.start = PHYS_SDRAM_1; /RAM起始地址 gd-bd-bi_dram0.size = PHYS_SDRAM_1_SIZE; /RAM大小 return 0;2410使用2片32MB的SDRAM组成了64MB的内存,接在存储控制器的BANK6,地址空间是0x300000000x34000000。在include/configs/smdk2410.h中PHYS_SDRAM_1和PHYS_SDRAM_1_SIZE 分别被定义为0x30000000和0x04000000(64M)。再看display_dram_config lib_arm/board.cstatic int display_dram_config (void) int i; #ifdef DEBUG puts (RAM Configuration:n); for(i=0; ibd-bi_drami.start); print_size (gd-bd-bi_drami.size, n); #else ulong size = 0; for (i=0; ibd-bi_drami.size; puts(DRAM: ); print_size(size, n);#endif return (0);呵呵仅仅是打印系统RAM的信息。这样整个初始化函数表的函数都看完了,总结一下主要做了如下过程:1 cpu, borad, interrupt的初始化,包括cache等,这些都于特定板子的配置有关。2 环境变量的初始化,3 串口,控制台,RAM的初始化,4 在控制台上实时的显示系统配置等相关参数。 最后需要说明的是,大部分的配置参数都是预先在include/configs/board_name.h下定义的,因此如果我们要移植我们自己的板子的话,这个文件必不可少,它描述了我们板子的配置情况如CPU型号,RAM大小等。2.start_armboot()源码分析分析完上述的数据结构,下面来分析start_armboot函数,现在我贴出start_armboot ()的源代码,然后具体的在其中解释一些代码的作用。void start_armboot (void) init_fnc_t *init_fnc_ptr; /*它是指向指针的指针变量,变量的类型为init_fnc_t,这是一个使用typedef定义的函数类型, 其中init_fnc_ptr将在后面指向一个数组指针*/ char *s;#ifndef CFG_NO_FLASH ulong size;#endif#if defined(CONFIG_VFD) | defined(CONFIG_LCD) unsigned long addr;#endif /* Pointer is writable since we allocated a register for it */ gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t);/计算全局数据结构的地址gd /* compiler optimization barrier needed for GCC = 3.4 */ _asm_ _volatile_(: : :memory); memset (void*)gd, 0, sizeof (gd_t); /初始化全局数据区为0; gd-bd = (bd_t*)(char*)gd - sizeof(bd_t); /为bd_t分配空间,并赋值到gd memset (gd-bd, 0, sizeof (bd_t); monitor_flash_len = _bss_start - _armboot_start; /代码段的大小 /* 逐个调用init_sequence数组中的初始化函数 */ for (init_fnc_ptr = init_sequence; *init_fnc_ptr; +init_fnc_ptr) if (*init_fnc_ptr)() != 0) hang (); /* CFG_NO_FLASH 表示没有flash,如果没定义该常量则表示板子上有flash,此时调用flash_init()对其进行初始化.这里是norflash初始化 */#ifndef CFG_NO_FLASH /* configure available FLASH banks */ size = flash_init (); 详见后分析 display_flash_config (size); /打印flash的信息,仅是其大小;#endif /* CFG_NO_FLASH */ #ifdef CONFIG_VFD /smdk2410没定义# ifndef PAGE_SIZE# define PAGE_SIZE 4096# endif /* * reserve memory for VFD display (always full pages) */ /* bss_end is defined in the board-specific linker script */ addr = (_bss_end + (PAGE_SIZE - 1) & (PAGE_SIZE - 1); size = vfd_setmem (addr); gd-fb_base = addr;#endif /* CONFIG_VFD */ #ifdef CONFIG_LCD# ifndef PAGE_SIZE# define PAGE_SIZE 4096# endif /* * reserve memory for LCD display (always full pages) */ /* bss_end is defined in the board-specific linker script */ addr = (_bss_end + (PAGE_SIZE - 1) & (PAGE_SIZE - 1); size = lcd_setmem (addr); gd-fb_base = addr;#endif /* CONFIG_LCD */ /* armboot_start is defined in the board-specific linker script */ mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); /初始化malloc区域,_armboot_start=0x33f80000,CFG_MALLOC_LEN=0x30000,mem_malloc_init对 (0x33f80000-0x30000)(0x33f80000)这段存储区清零/如果定义了命令和NAND命令,初始化nand#if (CONFIG_COMMANDS & CFG_CMD_NAND) puts (NAND: ); nand_init(); /*探测NAND flash并根据NAND flash的类型填充相应的数据结构,全局结构体变量nand_dev_desc0(类型struct nand_chip) */#endif #ifdef CONFIG_HAS_DATA

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论