uboot————第二阶段start_armboot 函数详解

1:上一节讲到start.S中进行了一系列的SoC相关硬件初始化以后进行了长跳转到start_armboot 函数中;

start_armboot进一步初始化board中硬件,并设置了uboot下的命令行、环境变量、基本命令、跳转到kernel

下面详细介绍start_armboot中的代码:

------------------------第一段代码---------------------------------------------

红色代码为条件编译以后要执行的代码

 1 void start_armboot (void)
 2 {
 3     init_fnc_t **init_fnc_ptr;
 4     char *s;
 5     int mmc_exist = 0;
 6 #if !defined(CFG_NO_FLASH) || defined (CONFIG_VFD) || defined(CONFIG_LCD)
 7     ulong size;
 8 #endif
 9
10 #if defined(CONFIG_VFD) || defined(CONFIG_LCD)
11     unsigned long addr;
12 #endif
13
14 #if defined(CONFIG_BOOT_MOVINAND)
15     uint *magic = (uint *) (PHYS_SDRAM_1);
16 #endif
17
18     /* Pointer is writable since we allocated a register for it */
19 #ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
20     ulong gd_base;
21
22     gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
23 #ifdef CONFIG_USE_IRQ
24     gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
25 #endif
26     gd = (gd_t*)gd_base;
27 #else    //CONFIG_MEMORY_UPPER_CODE
28     gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
29 #endif
30
31     if (readl(INF_REG_BASE+INF_REG0_OFFSET)==0xFFAADDEE)
32     {
33         extern int gbl_silent;
34         gbl_silent = 1;
35     }
36
37     /* compiler optimization barrier needed for GCC >= 3.4 */      //这段是c语言内嵌汇编,为了实现内存墙;
38     __asm__ __volatile__("": : :"memory");
39
40     memset ((void*)gd, 0, sizeof (gd_t));                
41     gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
42     memset (gd->bd, 0, sizeof (bd_t));
43
44     monitor_flash_len = _bss_start - _armboot_start;
45
46     for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
47         if ((*init_fnc_ptr)() != 0) {
48             hang ();
49         }
50     }-------------------未完待续------------------------

首先看一下

init_fnc_t **init_fnc_ptr;这个变量,这是一个init_fnc_t 类型的二重指针;typedef int (init_fnc_t) (void);可以看出init_fnc_t类型为 返回值为int 传参为空的函数类型,看一下下面这段代码:对
init_fnc_ptr 赋值为init_sequence(init_sequence为一个函数指针数组,这个数字为一个全局变量,存放的是硬件初始化有关的一些函数这些函数类型都是init_fnc_t类型),因此下面for循环的作用就是遍历init_sequence数组中的所有函数,并执行这些函数;如果这些初始化函数的返回值为0的话则执行hang() 挂起函数;hang函数的作用是输出puts ("### ERROR ### Please RESET the board ###\n");并进入一个死循环;总结一下:这段代码是初始化一个全局变量数组,数组中存放一些硬件初始化相关的函数指针,遍历这些函数,并进行相应硬件的初始化;这些全局变量放在数据段; 
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) {
            hang ();
        }
    }
 1 init_fnc_t *init_sequence[] = {
 2     cpu_init,        /* basic cpu dependent setup */
 3 #if defined(CONFIG_SKIP_RELOCATE_UBOOT)
 4     reloc_init,        /* Set the relocation done flag, must
 5                    do this AFTER cpu_init(), but as soon
 6                    as possible */
 7 #endif
 8     board_init,        /* basic board dependent setup */
 9     interrupt_init,        /* set up exceptions */
10     env_init,        /* initialize environment */
11     init_baudrate,        /* initialze baudrate settings */
12     serial_init,        /* serial communications setup */
13     console_init_f,        /* stage 1 init of console */
14     display_banner,        /* say that we are here */
15 #if defined(CONFIG_DISPLAY_CPUINFO)
16     print_cpuinfo,        /* display cpu info (and speed) */
17 #endif
18 #if defined(CONFIG_DISPLAY_BOARDINFO)
19     checkboard,        /* display board info */
20 #endif
21 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
22     init_func_i2c,
23 #endif
24     dram_init,        /* configure available RAM banks */
25     display_dram_config,
26     NULL,
27 };
1 void hang (void)
2 {
3     puts ("### ERROR ### Please RESET the board ###\n");
4     for (;;);
5 }

下面这段代码是为gd_base、gd_bd、两个全局变量分配内存地址;gd_t类型为结构体其中的内容为:大小为36byte;

{  bd_t 类型指针              //4字节  flag  无符号整形             //4字节  baudrate 波特率 无符号整形        //4字节  have_console 无符号整形          //4字节  reloc_off; /* Relocation Offset */  //4字节  env_addr                  //4字节  env_valid                  //4字节  unsigned long fb_base LCD的内存基地址  //4字节  void **jt; /* jump table */      //指针4字节         }

bd_t 也为一结构体,大小为42字节{  int 波特率          //4字节  unsigned int IP地址    //4字节  unsigned char 网卡地址   //6字节  环境变量指针          //4字节  机器码            //4字节  启动参数          //4字节  内存配置结构体        //8字节*2          }

下面这段代码的作用:gd_base 为0x23e00000 + 0x200000 - 912K -512K - 36byte这个地址用来存放这个全局变量

同样 bd_t 全局变量的地址设置在gd_base往下移动42byte的地址;

从这段代码我们可以看出uboot是如何进行内存分配的;

可以看下图:下图为uboot中的内存;

这段代码作的事情就是为gd_t、bd_t两个结构体分配内存地址,并初始化gd、gd->bd指针分别指向这两个结构体;

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

gd为一个register volatile结构体指针;asm ("r8")意思是放在r8寄存器中;

20     ulong gd_base;
21
22     gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
23
26     gd = (gd_t*)gd_base;

31   36
37     /* compiler optimization barrier needed for GCC >= 3.4 */      //这段是c语言内嵌汇编,为了实现内存墙;
38     __asm__ __volatile__("": : :"memory");
39
40     memset ((void*)gd, 0, sizeof (gd_t));                
41     gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
42     memset (gd->bd, 0, sizeof (bd_t));
43
44     monitor_flash_len = _bss_start - _armboot_start;
 1 typedef    struct    global_data {
 2     bd_t        *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 */
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     phys_size_t    ram_size;    /* RAM size */
17     unsigned long    reset_status;    /* reset status register at boot */
18 #endif
19     void        **jt;        /* jump table */
20 } gd_t;
 1 typedef struct bd_info {
 2     int            bi_baudrate;    /* serial console baudrate */
 3     unsigned long    bi_ip_addr;    /* IP Address */
 4     unsigned char    bi_enetaddr[6]; /* Ethernet adress */
 5     struct environment_s           *bi_env;
 6     ulong            bi_arch_number;    /* unique id for this board */
 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];
13 #ifdef CONFIG_HAS_ETH1
14     /* second onboard ethernet port */
15     unsigned char   bi_enet1addr[6];
16 #endif
17 } bd_t;

下面看一下init_sequence数组中有有哪些函数:

init_fnc_t *init_sequence[] = {
    cpu_init,        /* basic cpu dependent setup */
#if defined(CONFIG_SKIP_RELOCATE_UBOOT)
    reloc_init,        /* Set the relocation done flag, must
                   do this AFTER cpu_init(), but as soon
                   as possible */
#endif
    board_init,        /* basic board dependent setup */
    interrupt_init,        /* set up exceptions */
    env_init,        /* initialize environment */
    init_baudrate,        /* initialze baudrate settings */
    serial_init,        /* serial communications setup */
    console_init_f,        /* stage 1 init of console */
    display_banner,        /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
    print_cpuinfo,        /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
    checkboard,        /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
    init_func_i2c,
#endif
    dram_init,        /* configure available RAM banks */
    display_dram_config,
    NULL,
};

函数1:cpu_init函数;因为cpu相关的初始化已经在_start函数中做了,所以这里什么也没有做;

 1 int cpu_init (void)
 2 {
 3     /*
 4      * setup up stacks if necessary
 5      */
 6 #ifdef CONFIG_USE_IRQ            //这个未定义
 7     IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
 8     FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
 9 #endif
10     return 0;
11 }

函数2:board_init函数;这个函数中初始化了dm9000网卡,并且对gd->bd中的机器码以及启动参数赋值;

这里要注意一下:uboot中赋值的机器码要和linux内核中的机器码要一致,否则不能正常启动;

boot参数为:0x02000000+0x100;

 1 int board_init(void)
 2 {
 3     DECLARE_GLOBAL_DATA_PTR;
 4
 5
 6 #ifdef CONFIG_DRIVER_DM9000
 7     dm9000_pre_init();
 8 #endif
 9
10     gd->bd->bi_arch_number = MACH_TYPE;
11     gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
12
13     return 0;
14 }

函数3:interrupt_init中的初始化

 1 int interrupt_init(void)
 2 {
 3
 4     S5PC11X_TIMERS *const timers = S5PC11X_GetBase_TIMERS();
 5
 6     /* use PWM Timer 4 because it has no output */
 7     /* prescaler for Timer 4 is 16 */
 8     timers->TCFG0 = 0x0f00;              //设置预分频为15 +1 = 16
 9     if (timer_load_val == 0) {
10         /*
11          * for 10 ms clock period @ PCLK with 4 bit divider = 1/2
12          * (default) and prescaler = 16. Should be 10390
13          * @33.25MHz and  @ 66 MHz
14          */
15         timer_load_val = get_PCLK() / (16 * 100);    //设置为10ms
16     }
17
18     /* load value for 10 ms timeout */
19     lastdec = timers->TCNTB4 = timer_load_val;
20     /* auto load, manual update of Timer 4 */
21     timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | TCON_4_UPDATE;        
22     /* auto load, start Timer 4 */
23     timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | COUNT_4_ON;
24     timestamp = 0;
25
26
27     return (0);
28 }

代码解析:

  typedef vu_long S5PC11X_REG32;

S5PC11X_TIMERS:定义了一个结构体类型,把与时钟有关的所有所有寄存器都存放在这个结构体内
typedef struct {
    S5PC11X_REG32    TCFG0;
    S5PC11X_REG32    TCFG1;
    S5PC11X_REG32    TCON;
    S5PC11X_TIMER    ch[4];
    S5PC11X_REG32    TCNTB4;
    S5PC11X_REG32    TCNTO4;
} /*__attribute__((__packed__))*/ S5PC11X_TIMERS;

这句代码的意思就是把

S5PC11X_TIMERS *const timers = S5PC11X_GetBase_TIMERS();
S5PC11X_GetBase_TIMERS函数:的作用就是把timer寄存器的基地址强制类型转换为S5PC11X_TIMERS *  类型然后赋值给 timers变量;timers->TCFG0实际就是代表基地址右移4字节
之后的地址中的值,直接赋值相当于把0x0f00这个值放到 TCFG0对应的寄存器地址处,但是这个要注意的是,寄存器必须设置为连续的/或者一一对应的,否则会造成赋值的地址错误;
 timers->TCFG0 = 0x0f00;
static inline S5PC11X_TIMERS * S5PC11X_GetBase_TIMERS(void)
{
    return (S5PC11X_TIMERS *)ELFIN_TIMER_BASE;}

剩下的代码就和裸机的代码一致了;TCON的timer4的相应控制为清0,设置为自动reload,并且第一次要手动载入,然后时在清0,设置reload,开启timer4

timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | TCON_4_UPDATE;        
22     /* auto load, start Timer 4 */
23     timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | COUNT_4_ON;
----------------------------------

函数4:env_init
 1 int env_init(void)
 2 {
 3 #if defined(ENV_IS_EMBEDDED)
 4     ulong total;
 5     int crc1_ok = 0, crc2_ok = 0;
 6     env_t *tmp_env1, *tmp_env2;
 7
 8     total = CFG_ENV_SIZE;      // tatal = 0x4000 16k的大小,环境变量整个大小为16k
 9
10     tmp_env1 = env_ptr;
11     tmp_env2 = (env_t *)((ulong)env_ptr + CFG_ENV_SIZE);
12
13     crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
14     crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
15
16     if (!crc1_ok && !crc2_ok)
17         gd->env_valid = 0;
18     else if(crc1_ok && !crc2_ok)
19         gd->env_valid = 1;
20     else if(!crc1_ok && crc2_ok)
21         gd->env_valid = 2;
22     else {
23         /* both ok - check serial */
24         if(tmp_env1->flags == 255 && tmp_env2->flags == 0)
25             gd->env_valid = 2;
26         else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)
27             gd->env_valid = 1;
28         else if(tmp_env1->flags > tmp_env2->flags)
29             gd->env_valid = 1;
30         else if(tmp_env2->flags > tmp_env1->flags)
31             gd->env_valid = 2;
32         else /* flags are equal - almost impossible */
33             gd->env_valid = 1;
34     }
35
36     if (gd->env_valid == 1)
37         env_ptr = tmp_env1;
38     else if (gd->env_valid == 2)
39         env_ptr = tmp_env2;
40 #else /* ENV_IS_EMBEDDED */
41     gd->env_addr  = (ulong)&default_environment[0];
42     gd->env_valid = 1;
43 #endif /* ENV_IS_EMBEDDED */
44
45     return (0);
46 }

执行的是红色的代码:即把common.c中初始化好的default_environment地址赋值到gd->env_addr中,env_valid 赋值为1; 这里对字符串数组的初始化有些疑问???????

uchar default_environment[] = {
#endif
#ifdef    CONFIG_BOOTARGS
    "bootargs="    CONFIG_BOOTARGS            "\0"
#endif
#ifdef    CONFIG_BOOTCOMMAND
    "bootcmd="    CONFIG_BOOTCOMMAND        "\0"
#endif
。。。。。。。。。。。。。。。。。。。。。
#ifdef  CONFIG_CLOCKS_IN_MHZ
    "clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
    "pcidelay="    MK_STR(CONFIG_PCI_BOOTDELAY)    "\0"
#endif
#ifdef  CONFIG_EXTRA_ENV_SETTINGS
    CONFIG_EXTRA_ENV_SETTINGS
#endif
    "\0"
};

---------------------------------------------------------

函数5:init_baudrate初始化波特率:从env中获取波特率 赋值给gd->bd->bi_baudrate  gd->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);
}


/*这个函数的作用是读取环境变量name 到 缓存buf中,读取成功返回n大于0,失败返回0*/ 1 int getenv_r (char *name, char *buf, unsigned len)
 2 {
 3     int i, nxt;
 4
 5     for (i=0; env_get_char(i) != ‘\0‘; i=nxt+1) {
 6         int val, n;
 7
 8         for (nxt=i; env_get_char(nxt) != ‘\0‘; ++nxt) {
 9             if (nxt >= CFG_ENV_SIZE) {
10                 return (-1);
11             }
12         }
13         if ((val=envmatch((uchar *)name, i)) < 0)
14             continue;
15         /* found; copy out */
16         n = 0;
17         while ((len > n++) && (*buf++ = env_get_char(val++)) != ‘\0‘)
18             ;
19         if (len == n)
20             *buf = ‘\0‘;
21         return (n);
22     }
23     return (-1);
24 }
uchar env_get_char (int index)
{
    uchar c;

    /* if relocated to RAM */
    if (gd->flags & GD_FLG_RELOC)
        c = env_get_char_memory(index);
    else
        c = env_get_char_init(index);

    return (c);
}
 1 int envmatch (uchar *s1, int i2)
 2 {
 3
 4     while (*s1 == env_get_char(i2++))
 5         if (*s1++ == ‘=‘)
 6             return(i2);
 7     if (*s1 == ‘\0‘ && env_get_char(i2-1) == ‘=‘)
 8         return(i2);
 9     return(-1);
10 }
1 uchar env_get_char_memory (int index)
2 {
3     if (gd->env_valid) {
4         return ( *((uchar *)(gd->env_addr + index)) );
5     } else {
6         return ( default_environment[index] );
7     }
8 }
 
				
时间: 2024-08-06 03:39:34

uboot————第二阶段start_armboot 函数详解的相关文章

php socket函数详解

转自:http://blog.163.com/[email protected]/blog/static/2889641420138213514298/ 最近在用socket实现服务端向客户端主动推送消息函数名 描述socket_accept() 接受一个Socket连接socket_bind() 把socket绑定在一个IP地址和端口上socket_clear_error() 清除socket的错误或最后的错误代码socket_close() 关闭一个socket资源socket_connec

delphi中的Format函数详解

首先看它的声明:[[email protected]][@21ki!] function Format(const Format: string; const Args: array of const): string; overload;[[email protected]][@21ki!] 事实上Format方法有两种形式,另外一种是三个参数的,主要区别在于它是线程安全的,[[email protected]][@21ki!]但并不多用,所以这里只对第一个介绍:[[email protect

c++ 虚函数详解

下面是对C++的虚函数的理解. 一,定义 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略.下面来看一段简单的代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 classA { publi

linux网络编程之shutdown() 与 close()函数详解

linux网络编程之shutdown() 与 close()函数详解 参考TCPIP网络编程和UNP: shutdown函数不能关闭套接字,只能关闭输入和输出流,然后发送EOF,假设套接字为A,那么这个函数会关闭所有和A相关的套接字,包括复制的:而close能直接关闭套接字. 1.close()函数 [cpp] view plain copy print? <span style="font-size:13px;">#include<unistd.h> int 

linux中fork()函数详解[zz]

转载自:http://www.cnblogs.com/york-hust/archive/2012/11/23/2784534.html 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事. 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都复制到新的新进程中,只有

Python内置函数详解

置顶   内置函数详解 https://docs.python.org/3/library/functions.html?highlight=built#ascii 此文参考了别人整理好的东西(地址:http://www.cnblogs.com/sesshoumaru/p/6140987.html#p1),然后结合自己的理解,写下来,一方面方便自己,让自己好好学习,顺便回忆回忆:另一方面,让喜欢的盆友也参考一下. 经查询,3.6版本总共有68个内置函数,主要分类如下: 数学运算(7个) 类型转换

【转载】3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解

原文:3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解 3D中z值会影响屏幕坐标系到世界坐标系之间的转换,2D中Z值不会产生影响(而只是屏幕宽高比会产生影响,z值只对深度剔除产生影响).所以U3D中如果用2D摄像机那么屏幕坐标和世界坐标之间的转换需要用指定的2D摄像机才行,如果用主3D摄像机那么UI转换会产生计算结果异常. 一.D3DXMatrixPerspectiveFovLH函数 作用:Builds a left-handed

CreateFile函数详解

CreateFile函数详解 CreateFile The CreateFile function creates or opens the following objects and returns a handle that can be used to accessthe object: files pipes mailslots communications resources disk devices(Windows NT only) consoles directories(open

Android总结篇系列:Activity中几个主要函数详解

专注Android领域开发. 仰望星空,同时需要脚踏实地. ——好记性不如烂博客 Android总结篇系列:Activity中几个主要函数详解 Activity作为Android系统中四大基本组件之一,包含大量的与其他的各大组件.intent.widget以及系统各项服务等之间的交互的函数.在此,本文主要选取实际项目开发中常用的,但完全理解又需要有一定深入了解的几个函数进行讲解,后续本文会根据需要不断更新. 1. startActivityForResult / onActivityResult