env_relocate 函数深入分析


 void env_relocate (void)
     2 {
     3     /*
     4     * We must allocate a buffer for the environment
     5     */
     6      env_ptr = (env_t *)malloc (CFG_ENV_SIZE);

     7     if (gd->env_valid == 0) {
     8           puts ("*** Warning - bad CRC, using default environment\n\n");
     9         show_boot_progress (-60);
    10         set_default_env();
    11      } else {
    12         env_relocate_spec ();
    13      }
    14      gd->env_addr = (ulong)&(env_ptr->data);
    15 }

第6行  由于在配置文件中要定义环境变量区域的大小,即#define CFG_ENV_SIZE 0x10000, 这里从
heap堆里分配出这么大的空间来,并用env_ptr指向它

第7行 前面在循环体的 env_init() 中有 
                   gd->env_addr = (ulong)&default_environment[0];
                   gd->env_valid = 1;
第10行 使用默认的环境变量.env_init()中已经看到default_environment[],这是程序初始时的一份
环境变量默认设置,set_default_env()即将default_environment[]数组中的各环境变量项复制到
env_ptr所指向的env_t结构里(确切地说是复制到env_t结构的数据区里)。

第12行 环境变量存在nand中,将其从nand中读出,并填入env_ptr指向的env_t结构里
从nand读出时,配置项中有 CFG_ENV_OFFSET, 即环境变量在nand中存储的起始地址
                     CFG_ENV_SIZE 环境变量大小
数据读错 或 数据校验出错,都会使用默认的环境变量配置use_default(), 第一次运行uboot时板子会打
印如下信息
   *** Warning - bad CRC or NAND, using default environment
就是因为读出的数据经过crc32校验出错(此时读出的数据不是环境变量),进而调用use_default(),在
use_default()中会打印该信息。

第14行 又将gd->env_addr指向env_ptr->data

来看下env_t结构, 在include/environment.h中
typedef    struct environment_s {
    uint32_t    crc;        /* CRC32 over data bytes    */
    unsigned char    data[ENV_SIZE]; /* Environment data        */
} env_t;
整个env_t结构占CFG_ENV_SIZE大小,所以data区就占CFG_ENV_SIZE - sizeof(crc) 大小,足够
使用了。

小结;env_relocate
所做的事情有3件

     1. 从heap中分配一段空间,用于env_t结构
     2. 找到环境变量(或从内存中找或从nand中找),填充env_t结构
     3. 将gd->env_addr指向env_ptr->data, 这个也就是这里的relocate所在吧。


1        /* IP Address */
2        gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

将环境变量弄完之后,紧接着就是从环境变量的相应项中获取信息,环境变量是用户与u-boot的一个交互方式,
有了它之后,用户即可通过修改环境变量来修改板子的一些信息配置。这里的ip地址和网卡地址即是其中的一
个典型例子。来看上面的程序:
第2行,获取ip地址,注意gd->bd->bi_ip_addr是ungisned long 类型,而ip地址是类似于
"192.168.1.111"的字符串。往下跟踪:


     1    IPaddr_t getenv_IPaddr (char *var)
     2    {
     3        return (string_to_ip(getenv(var)));
     4    }

getenv("ipaddr") 即在环境变量中找到ipaddr这一项对应的字符串,假设这里为"192.168.1.111"
"192.168.1.111"传入string_to_ip。 
IPaddr_t 类型是unsigned long 的一个typedef


     5    IPaddr_t string_to_ip(char *s)
     6    {
     7        IPaddr_t addr;
     8        char *e;
     9        int i;

    10        if (s == NULL)
    11            return(0);

    12        for (addr=0, i=0; i<4; ++i) {
    13            ulong val = s ? simple_strtoul(s, &e, 10) : 0;
    14            addr <<= 8;
    15            addr |= (val & 0xFF);
    16            if (s) {
    17                s = (*e) ? e+1 : e;
    18            }
    19        }

    20        return (htonl(addr));
    21    }

12~19行 192.168.1.111 分为四个段,也就是要做4次
simple_strtoul()转换成10进制的整型
第次转换后的值赋给val。addr是unsigned long型,32位的,将其分为4段,每8位存储ip地址中的一个
段,比如这里,
最后addr = (((((192 << 8) | 168) << 8) | 1) << 8 ) | 111 = 0xc0a8016f

第20行,主机字节顺序转换为网络字节顺序返回
若CPU为小端模式时,addr如下存储

31           24 23           16 15             8 7               0
+--------------+---------------+----------------+-----------------+
| 192 = 0xc0   | 168 = 0xa8    |   1 = 0x01     |  111 = 0x6f     |
+--------------+---------------+----------------+-----------------+
     3                2                  1               0

若CPU为大端模式时,addr如下存储

31           24 23           16 15             8 7               0
+--------------+---------------+----------------+-----------------+
| 111 = 0x6f   |  1 = 0x01     |  168 = 0xa8    |  192 = 0xc0     |
+--------------+---------------+----------------+-----------------+
      3                2                 1               0

当与另一台计算机通信时,通常不知道对方存储数据时是先存放最高位字节 (MSB)还是最低位字节 (LSB)
恰恰网络字节顺序跟大端模式时相同,htonl函数就是将主机字节顺序转为网络字节顺序,在最高位字节
(MSB)-最前 的系统上,这些函数什么都不做。在 最低位字节(LSB)-最前的系统上它们将值转换为正确的
顺序。

最后将值返回给了gd->bd->bi_ip_addr, 所以其值应该是 0x6f01800a


     3        /* MAC Address */
     4        {
     5            int i;
     6            ulong reg;
     7            char *s, *e;
     8            char tmp[64];

     9            i = getenv_r ("ethaddr", tmp, sizeof (tmp));
    10            s = (i > 0) ? tmp : NULL;

    11            for (reg = 0; reg < 6; ++reg) {
    12                gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
    13                if (s)
    14                    s = (*e) ? e + 1 : e;
    15            }
    16        }

网卡地址以十六进制的形式存于gd->bd->bi_enetaddr[]数组中

时间: 2024-08-29 01:20:12

env_relocate 函数深入分析的相关文章

jquery ready函数深入分析_C 语言

最近看一些关于jquery ready 有人说他缓慢,有人说他快,说法不一. 于是自己深入研究一下.首先看了一下jquery 文档 关于ready 的描述 While JavaScript provides the load event for executing code when a page is rendered, this event does not get triggered until all assets such as images have been completely

Android深入浅出之Binder机制_Android

Android深入浅出之Binder机制 一 说明  Android系统最常见也是初学者最难搞明白的就是Binder了,很多很多的Service就是通过Binder机制来和客户端通讯交互的.所以搞明白Binder的话,在很大程度上就能理解程序运行的流程. 我们这里将以MediaService的例子来分析Binder的使用:          ServiceManager,这是Android OS的整个服务的管理程序         MediaService,这个程序里边注册了提供媒体播放的服务程

binder 分析

Android深入浅出之Binder机制 一 说明 Android系统最常见也是初学者最难搞明白的就是Binder了,很多很多的Service就是通过Binder机制来和客户端通讯交互的.所以搞明白Binder的话,在很大程度上就能理解程序运行的流程. 我们这里将以MediaService的例子来分析Binder的使用: <!--[if !supportLists]-->l <!--[endif]-->ServiceManager,这是Android OS的整个服务的管理程序 &l

Android深入浅出之Binder机制

Android深入浅出之Binder机制 一 说明 Android系统最常见也是初学者最难搞明白的就是Binder了,很多很多的Service就是通过Binder机制来和客户端通讯交互的.所以搞明白Binder的话,在很大程度上就能理解程序运行的流程. 我们这里将以MediaService的例子来分析Binder的使用: ServiceManager,这是Android OS的整个服务的管理程序 MediaService,这个程序里边注册了提供媒体播放的服务程序MediaPlayerServic

深入分析jQuery的ready函数是如何工作的(工作原理)_jquery

本文深入分析jQuery的ready函数是如何工作的.分享给大家供大家参考,具体如下: jQuery 是一个伟大的脚本库,由 John Resig 在 2006年1月的BarCamp NYC上释出第一个版本.你可以在 http://jquery.com/ 下载到最新版本,截止本文发布为止已更新到jQuery 2.1.4版.这里以jQuery1.8.3为例分析. 学习 jQuery 有许多途径,我们今天从 jQuery 的 ready 函数开始.本例中的代码都来自于 jQuery 脚本库. 如果你

深入分析PHP strtotime函数

PHP strtotime函数将任何英文文本的日期时间描述解析为Unix时间戳[将系统时间转化成unix时间戳] 一,获取指定日期的unix时间戳 strtotime("2009-1-22″) 示例如下: echo strtotime("2009-1-22") 结果:1232553600 说明:返回2009年1月22日0点0分0秒时间戳 二,获取英文文本日期时间 示例如下: 便于比较,使用date将当时间戳与指定时间戳转换成系统时间  代码如下 复制代码 (1)打印明天此时的

PHP rawurlencode与urlencode函数的深入分析_php技巧

问题:2个函数都是针对字符串转义使其适合做文件名.该用哪个?哪个更标准? 结论:rawurlencode遵守是94年国际标准备忘录RFC 1738,urlencode实现的是传统做法,和上者的主要区别是对空格的转义是'+'而不是'%20'javascript的encodeURL也是94年标准, 而javascript的escape是另一种用"%xxx"标记unicode编码的方法. 推荐在PHP中使用用rawurlencode.弃用urlencode 样例source: 超级无敌的人s

javascript回调函数(模式)原理和示例深入分析

                                                                                 广大网友读懂了我之前论述的javascript原理这篇文章很容易懂 回调函数来自一种著名的编程范式--函数式编程,在基本层面上,函数式编程指定的了函数的参数.函数式编程虽然现在的使用范围变小了,但它一直被"专业的聪明的"程序员看作是一种难懂的技术,以前是这样,未来也将是如此. 幸运的是,函数式编程已经被阐述的像你我这样的一般人

c++ 虚函数与纯虚函数的区别(深入分析)_C 语言

在面向对象的C++语言中,虚函数(virtual function)是一个非常重要的概念.因为它充分体现 了面向对象思想中的继承和多态性这两大特性,在C++语言里应用极广.比如在微软的MFC类库中,你会发现很多函数都有virtual关键字,也就是说, 它们都是虚函数.难怪有人甚至称虚函数是C++语言的精髓. 那么,什么是虚函数呢,我们先来看看微软的解释: 虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本. 这个定