在分析start.S文件过程中提到过,最后从汇编跳到C函数执行的是start_armboot函数,位于lib_arm\board.c文件下,它的执行流程图如下,截图来源于《嵌入式LINUX应用开发完全手册》。根据流程图,以下内容大致分几步写:

1、gd全局变量初始化

2、调用init_sequence函数指针数组里的初始化函数、nand初始化、环境变量初始化、USB初始化

3、死循环main_loop()分析

1、gd全局变量初始化

gd是全局引用的变量,它的定义在Global_data.h (include\asm-arm)中,它利用的是CPU的寄存器r8。只有在文件中引用DECLARE_GLOBAL_DATA_PTR ,就可以使用gd这个变量

  1. #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

它是一个指向gd_t结构体的指针,gd_t结构体如下所示

  1. typedef struct global_data {
  2. bd_t *bd; //bd结构体
  3. unsigned long flags; //标志
  4. unsigned long baudrate; //使用的波特率
  5. unsigned long have_console; /* serial_init() was called */ //是否有控制台的标志
  6. unsigned long reloc_off; /* Relocation Offset */ //重定位地址
  7. unsigned long env_addr; /* Address of Environment struct *///环境变量存放的地址
  8. unsigned long env_valid; /* Checksum of Environment valid? *///检查环境变量是否有效
  9. unsigned long fb_base; /* base address of frame buffer */ //lcd的缓存地址
  10. #ifdef CONFIG_VFD
  11. unsigned char vfd_type; /* display type */
  12. #endif
  13. #if 0
  14. unsigned long cpu_clk; /* CPU clock in Hz! */
  15. unsigned long bus_clk;
  16. unsigned long ram_size; /* RAM size */
  17. unsigned long reset_status; /* reset status register at boot */
  18. #endif
  19. void **jt; /* jump table */
  20. } gd_t;

其中env_addr、baudrate、bd等参数比较重要,bd也是一个结构体,在U-boot.h (include\asm-arm)里定义,定义如下所示,这里面bi_arch_number与bi_boot_params这两个参数是传给内核的,很重要。

  1. typedef struct bd_info {
  2. int bi_baudrate; /* serial console baudrate *///串口作为控制台时的波特率
  3. unsigned long bi_ip_addr; /* IP Address */ //ip地址,可配置
  4. unsigned char bi_enetaddr[]; /* Ethernet adress */ //物理网络地址,即MAC Address,网卡决定,不可配置
  5. struct environment_s *bi_env; //指向环境变量的指针
  6. ulong bi_arch_number; /* unique id for this board *///CPU架构号码,传给内核
  7. ulong bi_boot_params; /* where this board expects params *///标记列表的开始地址,传给内核,告诉内核从这个地方取参数
  8. struct /* RAM configuration */
  9. {
  10. ulong start;
  11. ulong size;
  12. } bi_dram[CONFIG_NR_DRAM_BANKS];//sdram的起始地址与大小
  13. #ifdef CONFIG_HAS_ETH1
  14. /* second onboard ethernet port */
  15. unsigned char bi_enet1addr[];
  16. #endif
  17. } bd_t;

environment_s结构体定义在Environment.h (include)中,如下所示。环境变量就是以这个格式存储在nand中的

  1. #define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE)//0x20000-5,减去的5为crc校验与flags占用的
  2.  
  3. typedef struct environment_s {
  4. unsigned long crc; /* CRC32 over data bytes *///crc校验
  5. #ifdef CFG_REDUNDAND_ENVIRONMENT
  6. unsigned char flags; /* active/obsolete flags *///环境变量标志
  7. #endif
  8. unsigned char data[ENV_SIZE]; /* Environment data *///环境变量具体的数据。最大
  9. } env_t;

start_armboot函数一开始先初始化gd变量。gd变量所指向的内容占用128字节存放在堆区后面,栈区前面。

  1. /* Pointer is writable since we allocated a register for it */
  2. gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));//gd地址向上增长
  3. /* compiler optimization barrier needed for GCC >= 3.4 */
  4. __asm__ __volatile__("": : :"memory");
  5.  
  6. memset ((void*)gd, , sizeof (gd_t));//清0 gd段
  7. gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));//求出bd段地址
  8. memset (gd->bd, , sizeof (bd_t));//清0gd->bd段
  9.  
  10. monitor_flash_len = _bss_start - _armboot_start;//显示需要的flash长度

2、调用init_sequence函数指针数组里的初始化函数、nand初始化、环境变量初始化、USB初始化

start_armboot函数继续往下执行,执行初始化函数数组里的函数

  1. for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {//初始化各个函数 by andy
  2. if ((*init_fnc_ptr)() != ) {//函数的返回值不为0,认为出错,打印出错信息 by andy
  3. hang ();//打印出错信息 by andy
  4. }
  5. }

init_sequence数组的内容如下所示

  1. init_fnc_t *init_sequence[] = {
  2. cpu_init, /* basic cpu dependent setup *///CPU的一些堆栈大小设置 by andy
  3. board_init, /* basic board dependent setup *///设置芯片代码、设置与内核交互的地址 by andy
  4. interrupt_init, /* set up exceptions *///10ms时钟定时器设置 by andy
  5. env_init, /* initialize environment *///初始化环境变量,采用默认环境变量 by andy
  6. init_baudrate, /* initialze baudrate settings *///初始化串口波特率为115200 by andy
  7. serial_init, /* serial communications setup *///初始化串口在cpu/arm920t/s3c24x0中实现 by andy
  8. console_init_f, /* stage 1 init of console *///设置控制台初始化标志 by andy
  9. display_banner, /* say that we are here *///打印UBOOT版本信息 by andy
  10. #if defined(CONFIG_DISPLAY_CPUINFO)
  11. print_cpuinfo, /* display cpu info (and speed) */
  12. #endif
  13. #if defined(CONFIG_DISPLAY_BOARDINFO)
  14. checkboard, /* display board info */
  15. #endif
  16. dram_init, /* configure available RAM banks *///内存起始地址以及大小设置 by andy
  17. display_dram_config,//打印出DRAM的大小 by andy
  18. NULL,
  19. };

start_armboot函数继续往下执行,清0分配的堆区的内容

  1. /* armboot_start is defined in the board-specific linker script */
  2. mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);//清0堆区的内容
  1. void mem_malloc_init (ulong dest_addr)
  2. {
  3. mem_malloc_start = dest_addr; //堆区的首地址
  4. mem_malloc_end = dest_addr + CFG_MALLOC_LEN; //堆区的结束地址
  5. mem_malloc_brk = mem_malloc_start;
  6.  
  7. memset ((void *) mem_malloc_start, ,
  8. mem_malloc_end - mem_malloc_start);//清0堆区的内容
  9. }

start_armboot函数继续往下执行,初始化nand,环境变量的内容存储在nand中

  1. puts ("NAND: ");//打印出nand的大小
  2. nand_init(); /* go init the NAND *///初始化nand flash

start_armboot函数继续往下执行,重新检测环境变量,环境变量在初始化函数数组中已经初始化过,因为nand初始化后,所以再检测一般环境变量是否需要重新加载。env_relocate 函数大致的意思是检查nand中的存放环境变量位置的crc校验是否有效,如果无效则采用默认的环境变量,如果有效则采用nand中的环境变量

  1. /* initialize environment */
  2. env_relocate ();//初始化环境变量,crc有效的话从nand中读取存储的环境变量,否则采用默认的环境变量 by andy

默认的环境变量的值如下

  1. uchar default_environment[] = {
  2. #ifdef CONFIG_BOOTARGS
  3. "bootargs=" CONFIG_BOOTARGS "\0"//"noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0",传给内核的参数
  4. #endif
  5. #ifdef CONFIG_BOOTCOMMAND
  6. "bootcmd=" CONFIG_BOOTCOMMAND "\0"//"nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0"。启动内核的命令
  7. #endif
  8. #ifdef CONFIG_RAMBOOTCOMMAND
  9. "ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
  10. #endif
  11. #ifdef CONFIG_NFSBOOTCOMMAND
  12. "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
  13. #endif
  14. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
  15. "bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0"//延时参数2S
  16. #endif
  17. #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
  18. "baudrate=" MK_STR(CONFIG_BAUDRATE) "\0"//波特率115200
  19. #endif
  20. #ifdef CONFIG_LOADS_ECHO
  21. "loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0"
  22. #endif
  23. #ifdef CONFIG_ETHADDR
  24. "ethaddr=" MK_STR(CONFIG_ETHADDR) "\0"//08:00:3e:26:0a:5b;MAC地址
  25. #endif
  26. #ifdef CONFIG_ETH1ADDR
  27. "eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0"
  28. #endif
  29. #ifdef CONFIG_ETH2ADDR
  30. "eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0"
  31. #endif
  32. #ifdef CONFIG_ETH3ADDR
  33. "eth3addr=" MK_STR(CONFIG_ETH3ADDR) "\0"
  34. #endif
  35. #ifdef CONFIG_IPADDR
  36. "ipaddr=" MK_STR(CONFIG_IPADDR) "\0"//板子IP192.168.7.17
  37. #endif
  38. #ifdef CONFIG_SERVERIP
  39. "serverip=" MK_STR(CONFIG_SERVERIP) "\0"//服务器IP192.168.7.11
  40. #endif
  41. #

start_armboot函数继续往下执行,从环境变量中获取IP地址以及MAC地址

  1. gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");//从环境变量获得IP地址 by andy
  2. i = getenv_r ("ethaddr", tmp, sizeof (tmp));//从环境变量获得MAC地址
  3. s = (i > ) ? tmp : NULL;
  4.  
  5. for (reg = ; reg < ; ++reg) {//判断获得的MAC地址有效后存储在gd->bd->bi_enetaddr中
  6. gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, ) : ;
  7. if (s)
  8. s = (*e) ? e + : e;
  9. }

start_armboot函数继续往下执行,控制台初始化,主要为设置输入输出与错误设备都为串口

  1. console_init_r (); /* fully init console as a device *///控制台初始化

start_armboot函数继续往下执行,USB与网络初始化,主要用来文件传输用的

  1. Port_Init();
  2. if (!PreLoadedONRAM) {
  3. /* enable exceptions */
  4. enable_interrupts ();
  5. /* add by www.100ask.net */
  6. usb_init();//USB初始化by andy
  7. }
  8.  
  9. eth_initialize(gd->bd);//网络初始化

3、死循环main_loop()分析

  1. for (;;) {//大循环
  2. main_loop ();//大循环 by andy
  3. }

下面分析main_loop()函数,它位于Main.c (common)中。main_loop函数首先为nand flash实现分区

  1. extern int mtdparts_init(void);
  2. if (!getenv("mtdparts"))//如果mtdparts参数不存在 by andy
  3. {
  4. run_command("mtdparts default", );//执行分区命令 设置分区 by andy
  5. }
  6. else
  7. {
  8. mtdparts_init();
  9. }

执行完run_command("mtdparts default", 0)后分区内容为:256k@0(bootloader),128k(params),2m(kernel),-(root)。意思是以bootloader占用256k为开头,初始地址为0。接着128k为参数,接着2m为内核,剩余的为文件。

继续执行main_loop函数,从环境变量中取得一些必要的参数。在后面要用到

  1. s = getenv ("c");//从环境变量中取得boordelay参数
  2. bootdelay = s ? (int)simple_strtol(s, NULL, ) : CONFIG_BOOTDELAY;//将字符串转换成整形
  3. s = getenv ("bootcmd");//取得bootcmd环境变量为"nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0"

继续执行main_loop函数,如果禁用bootdelay功能,则直接运行bootcmd环境变量。功能为从nand中取得内核到sdram的0x30007FC0 处。然后启动内核

  1. if (bootdelay >= && s && !abortboot (bootdelay)) {//等待延时数倒计时,如果时间到了则运行bootcmd参数。如果在时间到之前按下了空格键,则继续网线执行
  2. # ifdef CONFIG_AUTOBOOT_KEYED
  3. int prev = disable_ctrlc(); /* disable Control C checking */
  4. # endif
  5.  
  6. # ifndef CFG_HUSH_PARSER
  7. {
  8. printf("Booting Linux ...\n");
  9. run_command (s, );//执行bootcmd命令
  10. }
  11. # else
  12. parse_string_outer(s, FLAG_PARSE_SEMICOLON |
  13. FLAG_EXIT_FROM_LOOP);
  14. # endif
  15.  
  16. # ifdef CONFIG_AUTOBOOT_KEYED
  17. disable_ctrlc(prev); /* restore Control C checking */
  18. # endif
  19. }

继续执行main_loop函数,再来一个循环。若前面没有启动内核,那么进入死循环,等待控制台输入命令然后运行

  1. for (;;) {//若倒计时没到之前
  2. #ifdef CONFIG_BOOT_RETRY_TIME
  3. if (rc >= ) {
  4. /* Saw enough of a valid command to
  5. * restart the timeout.
  6. */
  7. reset_cmd_timeout();
  8. }
  9. #endif
  10. len = readline (CFG_PROMPT);
  11.  
  12. flag = ; /* assume no special flags for now */
  13. if (len > )
  14. strcpy (lastcommand, console_buffer);//取得控制台输入设备的数据
  15. else if (len == )
  16. flag |= CMD_FLAG_REPEAT;
  17. #ifdef CONFIG_BOOT_RETRY_TIME
  18. else if (len == -) {
  19. /* -2 means timed out, retry autoboot
  20. */
  21. puts ("\nTimed out waiting for command\n");
  22. # ifdef CONFIG_RESET_TO_RETRY
  23. /* Reinit board to run initialization code again */
  24. do_reset (NULL, , , NULL);
  25. # else
  26. return; /* retry autoboot */
  27. # endif
  28. }
  29. #endif
  30.  
  31. if (len == -)
  32. puts ("<INTERRUPT>\n");
  33. else
  34. rc = run_command (lastcommand, flag);//运行从输入设备取得的数据
  35.  
  36. if (rc <= ) {
  37. /* invalid command or not repeatable, forget it */
  38. lastcommand[] = ;
  39. }
  40. }

u-boot之start_armboot函数分析的更多相关文章

  1. 第1阶段——uboot分析之硬件初始化start_armboot函数(5)

    start_armboot()分析:在start.S初始化后跳转到start_armboot实现第2阶段硬件相关的初始化(烧写擦除flash,网卡驱动,usb驱动,串口驱动,从FLASH读内核,启动内 ...

  2. start_amboot()函数分析

    一.整体流程 start_amboot()函数是执行完start.S汇编文件后第一个C语言函数,完成的功能自然还是初始化的工作 . 1.全局变量指针r8设定,以及全局变量区清零 2.执行一些类初始化函 ...

  3. split(),preg_split()与explode()函数分析与介

    split(),preg_split()与explode()函数分析与介 发布时间:2013-06-01 18:32:45   来源:尔玉毕业设计   评论:0 点击:965 split()函数可以实 ...

  4. string函数分析

    string函数分析string函数包含在string.c文件中,经常被C文件使用.1. strcpy函数原型: char* strcpy(char* str1,char* str2);函数功能: 把 ...

  5. uboot的jumptable_init函数分析

    一.函数说明 函数功能:安装系统函数指针 函数位置:common/exports.c 二.函数分析 void jumptable_init (void) { int i; gd->jt = (v ...

  6. Linux-0.11内核源代码分析系列:内存管理get_free_page()函数分析

    Linux-0.11内存管理模块是源码中比較难以理解的部分,如今把笔者个人的理解发表 先发Linux-0.11内核内存管理get_free_page()函数分析 有时间再写其它函数或者文件的:) /* ...

  7. 31.QPainter-rotate()函数分析-文字旋转不倾斜,图片旋转实现等待

    在上章和上上上章: 28.QT-QPainter介绍 30.QT-渐变之QLinearGradient. QConicalGradient.QRadialGradient 学习了QPainter基础绘 ...

  8. Spring Boot 实战与原理分析视频课程

    Spring Boot 实战与原理分析视频课程 链接:https://pan.baidu.com/share/init?surl=PeykcoeqZtd1d9lN9V_F-A 提取码: 关注公众号[G ...

  9. 如何验证一个地址可否使用—— MmIsAddressValid函数分析

    又是一篇内核函数分析的博文,我个人觉得Windows的内核是最好的老师,当你想实现一个功能之前可以看看Windows内核是怎么做的,说不定就有灵感呢:) 首先看下官方的注释说明: /*++ Routi ...

随机推荐

  1. 简单实现"回车!=提交"(去除表单的回车即提交)

    -------------------------------------------------------------------------------------------------- 实 ...

  2. 13.Java国际化.md

    一.国际化开发概述 软件的国际化:软件开发时,要使它能同时应对世界不同地区和国家的访问,并针对不同地区和国家的访问,提供相应的.符合来访者阅读习惯的页面或数据. 国际化(internationaliz ...

  3. week06 08 postman 测试jsonrpc

    用postman来测试rpc需要添加特别的字段 ’ { "jsonrpc":"2.0", "id":"123", &qu ...

  4. js实现刷新页面出现随机背景图

    直接上代码: <script>         var bodyBgs = [];         bodyBgs[0] = "IMG/01.jpg";         ...

  5. C++ 关于 CMFCPropertyGridCtrl 的使用方法 之一 (原创)

    题外话: 最近在写一个重要的程序,想做的更灵活一些,于是想采用属于对话框的形式,如图所示 但查了好几本大部门的C++及MFC的书,还有很多的网上的资料,这方面的介绍实在是少之又少.不过,好在VS201 ...

  6. 代码规范【经理培训内容记录】[有参考:http://kb.cnblogs.com/page/179593/]

    一.命名规范 方法:所有首字母大写,如BloodControl; 类:所有首字母大写: 变量:第一个首字母小写,其他首字母大写:如bloodControl; 常量:全部字母大写,可用下划线分隔:如BL ...

  7. 处女座和小姐姐(三)-数位dp1.0

    链接:https://ac.nowcoder.com/acm/contest/329/G来源:牛客网 题目描述 经过了选号和漫长的等待,处女座终于拿到了给小姐姐定制的手环,小姐姐看到以后直呼666! ...

  8. 使用RestTemplate调用接口上传文件

    场景 接口接受一个文件,缓存在本地,验证文件的完整性及内容,然后将文件上传至云服务器: 下面只写利用RestTemplate将文件上传至云服务器,至于文件上传以及缓存在本地可以参考:JAVA文件上传: ...

  9. for嵌套

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...

  10. HTTP是用来做什么的

    (一)HTTP协议介绍 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议.所有的WWW文件都必须遵守这个标准.设计HTTP最初的目 ...