start_armboot解析
start_armboot函数在lib_arm/board.c文件里,除了函数本身,还有一些用到了的定义和宏。S5PV210-uboot解析-start.S解析(二)-gd_t和init_sequence函数.note
typedef int (init_fnc_t) (void);
这里定义的是一个函数类型,不是函数指针。
下面是正式代码,我将没有用到的地方都去掉了:
void start_armboot (void)
{
    init_fnc_t **init_fnc_ptr;
这里定义的是函数类型的二重指针,也可以看成是函数类型的指针数组。
    char *s;
    int mmc_exist = 0;
 
    /* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
    ulong gd_base;
 
    gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
gd_base的地址就是 0x33e0_0000 + 2*1024*1024 - (0x4000 + 896*1024) - (512*1024) - (36 + 46 )
就是 基地址 + 2MB -(16KB + 896KB) - (512KB) - (82B)
也就是uboot的基地址+2MB-1424KB,相当于uboot基地址后面623KB多点的位置。
这之间的内存排布就是
CFG_UBOOT_SIZE 
宏定义的uboot大小(2MB)
实际上只有200多KB
gd_base
全局变量的区域
 
内存间隔
CFG_STACK_SIZE 
栈区
CFG_MALLOC_LEN 
堆区
CFG_UBOOT_BASE 
uboot基地址
    gd = (gd_t*)gd_base;
#else
    gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
#endif
 
    /* compiler optimization barrier needed for GCC >= 3.4 */
    __asm__ __volatile__("": : :"memory");
这句话是调用汇编的指令,为了防止高版本的GCC的优化造成错误。__asm__ 表示后面的代码为内嵌汇编,__volatile__防止编译器优化代码,括号内的是汇编指令。内嵌汇编语法如下:
__asm__(汇编语句模板: 输出部分: 输入部分: 破坏描述部分)
 
    memset ((void*)gd, 0, sizeof (gd_t));
    gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
    memset (gd->bd, 0, sizeof (bd_t));
memset初始化指针。
 
    monitor_flash_len = _bss_start - _armboot_start;
 
    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) {
            hang ();
        }
    }
 
这个for循环用到了之前定义的函数类型的二重指针(相当于指针数组),遍历init_sequence数组内的元素,即运行每一个函数进行相关的初始化操作,这里面的函数都是初始化相关的。这里操作的时候先赋值init_fnc_ptr = init_sequence 循环的时候操作init_fnc_ptr 而不对init_sequence 进行操作。如果某一个函数初始化失败了,没有返回0,那么就进入hang函数挂起。hang函数内部就是一个死循环。如果uboot进入到hang函数了,只能重启。
#ifndef CFG_NO_FLASH
    /* configure available FLASH banks */
    size = flash_init ();
    display_flash_config (size);
#endif /* CFG_NO_FLASH */
这里是属于代码移植的遗留问题,在210板子中是没有norFlash的,但是这里还是保留了这段代码,而进行实际测试发现如果定义CFG_NO_FLASH的话会产生很多错误,所以没有定义CFG_NO_FLASH。这段代码只是在串口输出了Flash: 8 MB,没有其他的作用。
 
#ifdef CONFIG_VFD
#   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
    /* board init may have inited fb_base */
    if (!gd->fb_base) {
#       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 */
CONFIG_VFD和CONFIG_LCD都是和显示相关的,是uboot中自带的LCD显示的软件架构,但这里这两个宏都没有被定义,因为在后面我们使用了自己的LCD显示函数。
 
    /* armboot_start is defined in the board-specific linker script */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
    mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
#else
    mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
#endif
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);
}
这个函数是用来初始化堆管理器的。CFG_MALLOC_LEN就是堆的大小,有896KB。
//******************************//
// Board Specific
// #if defined(CONFIG_SMDKXXXX)
//******************************//
 
接下来的一些代码都是和指定的板子相关的,通过定义宏来选择。
#if defined(CONFIG_X210)
 
    #if defined(CONFIG_GENERIC_MMC)
        puts ("SD/MMC:  ");
        mmc_exist = mmc_initialize(gd->bd);
        if (mmc_exist != 0)
        {
            puts ("0 MB\n");
初始化MMC,其实是初始化SoC内部的MMC控制器。调用mmc_initialize函数。
drivers/mmc/mmc.c
int mmc_initialize(bd_t *bis)
{
    struct mmc *mmc;
    int err;
 
    INIT_LIST_HEAD(&mmc_devices);
    cur_dev_num = 0;
 
    if (board_mmc_init(bis) < 0)
        cpu_mmc_init(bis);
 
#if defined(DEBUG_S3C_HSMMC)
    print_mmc_devices(',');
#endif
 
#ifdef CONFIG_CHECK_X210CV3
    mmc = find_mmc_device(1);//lqm
#else
    mmc = find_mmc_device(0);
#endif
    if (mmc) {
        err = mmc_init(mmc);
        if (err)
            err = mmc_init(mmc);
        if (err) {
            printf("Card init fail!\n");
            return err;
        }
    }
    printf("%ldMB\n", (mmc->capacity/(1024*1024/(1<<9))));
    return 0;
}
drivers/mmc/mmc.c
这段代码主要初始化的操作是这一段:
 if (board_mmc_init(bis) < 0)
        cpu_mmc_init(bis);
跳转到board_mmc_init后
static int __def_mmc_init(bd_t *bis)
{
    return -1;
}
 
int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init")));
board_mmc_init 后面的__attribute__((weak, alias("__def_mmc_init"))); 表示
board_mmc_init 是 __def_mmc_init 的一个别名。而__def_mmc_init 返回-1,小于0,所以
初始化的时候实际执行的是cpu_mmc_init。
cpu/s5pc11x/cpu.c
int cpu_mmc_init(bd_t *bis)
{
#ifdef CONFIG_S3C_HSMMC
    setup_hsmmc_clock(); //设置时钟
    setup_hsmmc_cfg_gpio(); //设置GPIO
    return smdk_s3c_hsmmc_init();
#else
    return 0;
#endif
}
__attribute__ 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
weak 和 alias 分别是两个属性。weak 使得 board_mmc_init 这个符号在目标文件中作为 weak symbol 而不是 global symbol。
而 alias 则使 board_mmc_init 是 __def_mmc_init 的一个别名,__def_mmc_init 和 board_mmc_init 必须在同一个编译单元中定义,否则会编译出错。
给函数加上weak属性时,即使函数没定义,函数被调用也可以编译成功。
当有两个函数同名时,则使用强符号(也叫全局符号,即没有加weak的函数)来代替弱符号(加weak的函数)。
#ifdef CONFIG_CHECK_X210CV3
            check_flash_flag=0;//check inand error!
#endif
        }
#ifdef CONFIG_CHECK_X210CV3
        else
        {
            check_flash_flag=1;//check inand ok! 
        }
#endif
    #endif
 
    #if defined(CONFIG_MTD_ONENAND)
        puts("OneNAND: ");
        onenand_init();
        /*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/
    #else
        //puts("OneNAND: (FSR layer enabled)\n");
    #endif
 
    #if defined(CONFIG_CMD_NAND)
        puts("NAND:    ");
        nand_init();
    #endif
 
#endif /* CONFIG_X210 */
/* initialize environment */
env_relocate ();
重定位环境变量,因为环境变量最开始都烧录在SD卡中,这里将SD卡中的环境变量读取到DDR中。
将无效的宏定义去掉后:
void env_relocate (void)
{
    DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
        gd->reloc_off);
  
    if (gd->env_valid == 0) {
        set_default_env();
    }
    else {
        env_relocate_spec ();
    }
    gd->env_addr = (ulong)&(env_ptr->data);
}
这样比较清楚的看到由于gd->env_valid的值是等于0的,所以执行的默认设置环境变量函数是
set_default_env:
void set_default_env(void)
{
    if (sizeof(default_environment) > ENV_SIZE) {
        puts ("*** Error - default environment is too large\n\n");
        return;
    }
 
    memset(env_ptr, 0, sizeof(env_t));
    memcpy(env_ptr->data, default_environment,
           sizeof(default_environment));
#ifdef CFG_REDUNDAND_ENVIRONMENT
    env_ptr->flags = 0xFF;
#endif
    env_crc_update ();
    gd->env_valid = 1;
}
可以看到第一次启动的时候uboot会使用默认的环境变量,用memset和memcpy进行清零后复制。
之后就把gd->env_valid赋值1,即有效。
/* IP Address */
    gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
 
    /* MAC Address */
    {
        int i;
        ulong reg;
        char *s, *e;
        char tmp[64];
 
        i = getenv_r ("ethaddr", tmp, sizeof (tmp));
        s = (i > 0) ? tmp : NULL;
 
        for (reg = 0; reg < 6; ++reg) {
            gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
            if (s)
                s = (*e) ? e + 1 : e;
        }
 
    }
从环境变量中获取IP地址和MAC地址,getenv_IPaddr 是调用getenv 函数来获取环境变量中的字符串格式的IP地址,中间还用string_to_ip 来将字符串格式的IP地址转换成字符串格式的点分十进制格式。
devices_init ();   /* get the devices list going. */
设备初始化函数,放在这里面初始化的设备都是驱动设备,作用就是集中执行各种硬件驱动的初始化函数。
jumptable_init ();
跳转表。应该是要实现函数指针到具体函数的映射,但是查找发现跳转表只是被赋值没有被引用,因此在uboot中没有使用。
#if !defined(CONFIG_SMDK6442)
    console_init_r (); /* fully init console as a device */
#endif
/* enable exceptions */
enable_interrupts ();
中断使能,实际是空函数。没有定义相关的宏,#else分支是空函数。
    /* Initialize from environment */
    if ((s = getenv ("loadaddr")) != NULL) {
        load_addr = simple_strtoul (s, NULL, 16);
    }
#if defined(CONFIG_CMD_NET)
    if ((s = getenv ("bootfile")) != NULL) {
        copy_filename (BootFile, s, sizeof (BootFile));
    }
#endif
这两个环境变量是和内核启动有关的,启动linux内核时会参考这两个变量。
 
#ifdef BOARD_LATE_INIT
    board_late_init ();
#endif
开发板晚期初始化的函数,在x210中,这个函数是空的。
****************lxg added**************/
#ifdef CONFIG_MPAD
    extern int x210_preboot_init(void);
    x210_preboot_init();
#endif
x210启动起来之前的一些初始化,主要是LCD相关。还有屏幕上的logo显示。
/****************end**********************/
 
    /* check menukey to update from sd */
    extern void update_all(void);
    if(check_menu_update_from_sd()==0)//update mode
    {
        puts ("[LEFT DOWN] update mode\n");
        run_command("fdisk -c 0",0);
        update_all();
    }
    else
        puts ("[LEFT UP] boot mode\n");
这里设置了一个自动更新的功能,开机按下LEFT按键会进入 update mode,之后uboot会从SD卡中读取镜像烧录到iNand中。不按LEFT会进入boot mode,即正常启动。
 
    /* main_loop() can return to retry autoboot, if so just run it again. */
    for (;;) {
        main_loop ();
    }
 
uboot启动的最后阶段,main_loop是一个死循环,如果开机倒计时不打断会自动引导内核加载系统,如果打断会进入uboot的命令行。
    /* NOTREACHED - no way out of command loop except booting */
}
到这里uboot启动的第二阶段也分析完了。在start_armboot函数中,主要就是对开发板的硬件做了初始化,还有软件的一些数据结构做了初始化。
主要的函数及功能:
init_sequence
cpu_init 空的
board_init 网卡、机器码、内存传参地址
dm9000_pre_init 网卡
gd->bd->bi_arch_number 机器码
gd->bd->bi_boot_params 内存传参地址
interrupt_init 定时器
env_init
init_baudrate gd数据结构中波特率
serial_init 空的
console_init_f 空的
display_banner 打印启动信息
print_cpuinfo 打印CPU时钟设置信息
checkboard 检验开发板名字
dram_init gd数据结构中DDR信息
display_dram_config 打印DDR配置信息表
mem_malloc_init 初始化uboot自己维护的堆管理器的内存
mmc_initialize inand/SD卡的SoC控制器和卡的初始化
env_relocate 环境变量重定位
gd->bd->bi_ip_addr gd数据结构赋值
gd->bd->bi_enetaddr gd数据结构赋值
devices_init 空的
jumptable_init 不用关注的
console_init_r 真正的控制台初始化
enable_interrupts 空的
loadaddr、bootfile 环境变量读出初始化全局变量
board_late_init 空的
eth_initialize 空的
x210_preboot_init LCD初始化和显示logo
check_menu_update_from_sd 检查自动更新
main_loop 主循环

S5PV210-uboot解析(三)-start_armboot解析相关推荐

  1. Disruptor源码解析三 RingBuffer解析

    目录 系列索引 前言 主要内容 RingBuffer的要点 源码解析 系列索引 Disruptor源码解析一 Disruptor高性能之道 Disruptor源码解析二 Sequence相关类解析 D ...

  2. vue+axios+qs序列化 “三步解析”【含demo实例】- 代码篇

    文章目录 qs序列化:是什么?为什么?怎么办?`实例截图参考` 一.`(简单了解)` · `三步解析 ` 序列化是一种用来处理对象流的机制: 对象.文件.数据,有许多不同的格式,很难统一传输和保存 序 ...

  3. xml文件的三种解析方式 DOM SAM PULL

    <?xml version="1.0" encoding="UTF-8"?> <root><student id="1& ...

  4. xml的三种解析方式

    本篇博客重点介绍Android中三种解析XML的方式,包括PULL.SAX.DOM,当然不止这些,还可以用第三方的jar包提供的解析,只是这三种在Android中比较常用吧.再顺便介绍一下Androi ...

  5. Fixflow引擎解析(三)(模型) - 创建EMF模型来读写XML文件

    Fixflow引擎解析(四)(模型) - 通过EMF扩展BPMN2.0元素 Fixflow引擎解析(三)(模型) - 创建EMF模型来读写XML文件 Fixflow引擎解析(二)(模型) - BPMN ...

  6. 碰撞检测GJK算法论文解析三

    碰撞检测GJK算法论文解析三 再探Appendix Ⅱ 内容详解 再探The Distance Subalgorithm 内容详解 过程1 过程2 过程3 这里要先纠正上篇文章的一些错误,就是上篇文章 ...

  7. 北京络捷斯特第三方物流信息系统技术解析(三) 订单录入-出库订单

    北京络捷斯特第三方物流信息系统技术解析(三) 订单录入-出库订单 2.1.2 出库订单 出库订单包括有订单信息.订单出库信息.订单货品三个标签页的信息编写. 订单信息界面截图: 2.1.2图(1) 订 ...

  8. JSON的三种解析方式以及JSONObject、JSONArray区别

    JSON的常用两种解析方式 一.什么是JSON? JSON是一种取代XML的数据结构,和xml相比,它更小巧但描述能力却不差,由于它的小巧所以网络传输数据将减少更多流量从而加快速度. JSON就是一串 ...

  9. 用python解析xml的几种方法,Python_XML的三种解析方法

    什么是XML? XML 指可扩展标记语言(eXtensible Markup Language). XML 被设计用来传输和存储数据. XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这 ...

最新文章

  1. 影像组学视频学习笔记[44(End)]-带95%置信区间的折线图、Li‘s have a solution and plan.
  2. 性价比高出英特尔45%,亚马逊的云服务器芯片如何做到?| 解读
  3. Java FAQ(1)
  4. 定价相关的主要用户出口
  5. 扔掉老破V100、A100,英伟达新一代计算卡H100来了!
  6. Java并发编程实战~Worker Thread模式
  7. OSGI概念理解和入门
  8. WPF datagrid 加入图片
  9. C语言嵌入式系统编程修炼之道——软件架构篇
  10. word文档图标变成白纸_win10word文档图标变白,win10office图标空白
  11. Matlab导入外部数据绘制函数曲线
  12. Haskell语言学习笔记(75)Conduit
  13. 暴雪和黑客的战争二:暴雪的第一击
  14. 期货市场之反转形态分析
  15. pg_stat_database的字段tup_returned,tup_fetched含义
  16. ecm、ppp、ndis 拨号
  17. 千锋Unity学习笔记
  18. linux快速删除大量目录,Linux 快速删除大量小文件方法
  19. dbms chapter3
  20. STC 纯硬件自动下载电路 V2

热门文章

  1. 历史回顾——中国各省省名之由来
  2. 学习英语的电影推荐!
  3. 华为校园招聘笔试题(机考题目)
  4. 初遇林纳斯(Linux)——Linux历史
  5. STM32103驱动28BYJ48步进电机
  6. qnx限制ssh连接的数量
  7. 2002年世界杯中国队男足的3场比赛(中国vs哥斯达黎加、巴西和土耳其)比分
  8. 受邀参加2009中国电子商务创新发展高峰论坛
  9. 联通签到php,联通营业厅微博双签(云函数自动签到)
  10. 连接请求被计算机拒绝访问,Windows 10共享打印机解决方案被拒绝访问