C语言函数入口参数的格式,uboot中C语言代码入口函数(start_armboot)的注释

这篇博客详细介绍了嵌入式系统中u-boot的全局数据结构、板级配置参数以及初始化流程。作者从gd_t和bd_t数据结构入手,解析了u-boot中保存配置信息和板级参数的重要结构,并逐步讲解了从cpu_init到dram_init的初始化过程,涉及串口初始化、环境变量、内存分配和设备初始化等关键步骤。博客适合嵌入式新手学习,帮助理解u-boot的底层工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自开学以来一直在研究s3c2440A的板子,现在在嵌入式方面,我现在基本上处于0阶段,

所以进度非常慢.我先学的是uboot,不过现在还没学会,因为没有针对这个板子把

uboot移植成功.唉...急....

在介绍该函数之前,我们需要看一看几个数据结构,这些是u-boot中几个重要的数据结构:

1)、gd_t该数据结构保存了u-boot需要的配置信息(我暂时称它为全局信息表),

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? */

#ifdef CONFIG_VFD  //我们一般没有配置这个,这个是frame buffer的首地址

unsigned long    fb_base;    /* base address of frame buffer,显存缓存区 基址*/

#endif

#if 0

unsigned long cpu_clk; /* CPU clock in Hz! CPU的时钟频率*/

unsigned long bus_clk; //总线的时钟频率

unsigned long ram_size; /* RAM size, RAM的大小*/

unsigned long reset_status; /* reset status register at boot */

#endif

void  **jt;  /* jump table ,保存着些函数的入口地址,在common/Exports.c中进行填充*/

} gd_t;

2)、bd_t 保存与板子相关的配置参数

typedef struct bd_info {

int            bi_baudrate;    /* serial console baudrate ,串口波特率 */

unsigned long    bi_ip_addr;    /* IP Address ,IP地址*/

unsigned char    bi_enetaddr[6]; /* 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 configuration */

{

ulong start; //RAM的起始地址

ulong size; //RAM的大小

}             bi_dram[CONFIG_NR_DRAM_BANKS];

} bd_t;

3). 初始化函数列表(以数组的形式)

init_fnc_t *init_sequence[] = {

cpu_init,   /* basic cpu dependent setup  || cpu/arm920t/cpu.c ,cpu的初始化,有待于分析*/

//这个是对板子的初始化,

board_init,   /* basic board dependent setup|| board/smdk2440/smdk2440.c */

interrupt_init,  /* set up exceptions || cpu/arm920t,s3c24x0/interrupts.c */

env_init,   /* initialize environment */

init_baudrate,  /* initialze baudrate settings */

serial_init,  /* serial communications setup || cpu/arm920t/s3c24x0/serial.c */

//串口初始化后我们就可以打印信息了

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

dram_init,  /* configure available RAM banks */

display_dram_config,

NULL,

};

//===========================================

//===========================================

int cpu_init (void) //cpu/arm920t/Cpu.c中的函数

{

/*

* setup up stacks if necessary

*/

//这里只是做了对中断栈和快速中断栈空间地址的定义

//IRQ_STACK_START 和 FIQ_STACK_START 的值在start.S的开始几行中有定义

//其中的那个 -4 操作是难道是为PC跳转留的一个地址???

#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;

}

//===================================================

//===================================================

//---------mem_malloc_init----------------

//  参数: malloc内在区的起始地址

//  功能: 完成malloc函数所要用到的静

//   态变量的初始化.

//返回值:  无

//----------------------------------------

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, //把这段空间初始化为0

mem_malloc_end - mem_malloc_start);

}

......

init_fnc_t *init_sequence[] = {

//该函数只是做了对中断栈和快速中断栈空间地址的定义

cpu_init,   /* basic cpu dependent setup  || cpu/arm920t/cpu.c */

//完成各时钟和端口还有gd中的两个成员的初始化

board_init,   /* basic board dependent setup|| board/smdk2440/smdk2440.c */

//PWM(Pulse Width Modulation 脉宽调制器) TIMER的初始化,

interrupt_init,  /* set up exceptions || cpu/arm920t,s3c24x0/interrupts.c */

//环境的初始化,没深入分析

env_init,   /* initialize environment */

//初始化波特率,并写进gd的成员变量中

init_baudrate,  /* initialze baudrate settings */

//串口初始化后我们就可以打印信息了

serial_init,  /* serial communications setup || cpu/arm920t/s3c24x0/serial.c */

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

//DRAM的初始化,这里只是对gd中的 bi_dram结构中的两个成员赋值,

//也即BANK的起始地址和大小

dram_init,  /* configure available RAM banks */

//打印BANK的相关信息

display_dram_config,

NULL, //用以标识列表数组的结束

};

//------------start_armboot--------------------

//功能: 完成uboot第二阶级的一系列的

//  硬件初始化工作, 然后转入main函数.

//备注: 该函数是C程序的入口函数,从汇编语

//  言跳转到此 .

//---------------------------------------------

void start_armboot (void)

{

init_fnc_t **init_fnc_ptr; //init_fnc_t 是各初始化函数的数组

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_t: 定义在 /include/asm-arm/Global_data.h中,包含一些全局通用的变量.

_armboot_start: 代码的起始地址,它定义在start.S中的前几行中,定义为 _start

当系统第一次加电时,指令是从0x0地址开始执行的,所以此时的 _start值

应为0x0;而当uboot经过代码重定位后,指令会从 _TEXT_BASE 处开始执行,

此时的 _start值就成了 _TEXT_BASE的值.

CFG_MALLOC_LEN: 在/include/configs/smdk2440.h中有定义,该变量表示供

malloc函数使用的内存池空间,代码中定义值为:0x10000+128*1024

|-------| |  4    |

|-------| |  3    |

|-------| |  2    |

|-------| |  1    |

-------

4 就是为malloc函数预留的数据空间

3 是全信息表gd的数据区

2 是板卡信息表bd的数据区

网上找了个图片,更能反应这个空间的分配关系:

081120162105.jpg

*/

//分配区域 3 给 gd ,gd是一个全局静态变量

gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

/* compiler optimization barrier needed for GCC >= 3.4 */

__asm__ __volatile__("": : :"memory");

//把gd变量的内容填充为0 ,填充 3 区的数据为0 ,即初始化gd表.注意:这里并没有

//初始化bd表,在gd表中的bd成员只是一个指针,因为对初始化的是个指针地址

memset ((void*)gd, 0, sizeof (gd_t));

/*

bd_t 结构体在/include/asm-arm/U-boot.h中定义, 定义板子的一些信息,包括:

波特率,IP地址, 以太网地址, 架构编码,启动参数 ,BANK的起始地址和大小等

*/

//分配区域 2 给bd, bd的基址 = gd的基址 - bd的尺寸

gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

//把区域 2 填充为 0 ,即初始化 bd 表

memset (gd->bd, 0, sizeof (bd_t));

/*monitor_falsh_len定义在 /lib_arm/Board.c

在bin文件中 BSS 段和 TEXT 段和 DATA 段存放的顺序从前向后依次是:

代码段[可执行代码段(.text), 只读数据段(.rodata)]

数据段[已初始化数据段(.data),未初始化数据段(.bss)]

所以 _bss_start 的基址等于代码段的长度加上.data的长度.

即: _bss_start(BSS段基址) = 代码段长度+数据段长度

下面是bin文件中段的分布:

一.存放态的分布:

+-----------------------------

|  .bss    |

+----------+---数据段

|  .data   |

+----------+------------------

|  .rodata |

+----------+---代码段

|  .text   |

+----------+------------------

二.运行态

|   ...    |

+----------+------------------

|  .bss    | ZI段

+----------+--------数据段

|  .data   | RW段

+----------+-------------

|  .rodata |

+----------+---代码段(RO段)

|  .text   |

+----------+-------------

|   ...    |

装载前

|    ...      |

+-------------+-- ZI段结束地址

|   ZI段    |

+-------------+-- ZI段起始地址

|   保留区2    |

+-------------+-- RW段结束地址

|    RW段     |

+-------------+-- RW段起始地址

|   保留区1    |

+-------------+-- RO段结束地址

|    RO 段    |

+-------------+-- RO段起始地址

|    ...      |

装载后

BSS(Block Started by Symbol)段是未被初始化的数据段,是存放程序中

未被初始化的全局变量的一块内存区域,初始化时应清零;该段只有

名称和大小却没有值;该段不包含任何数据,只是简单的维护开始和

结束的地址,以便内存区能在运行时被有效地清零,它在应用程序的

映像文件(ARM中也即bin文件)中并不存在.

*/

//_armboot_start 在start.S中定义为_start,而_start为代码的起始地址

//只包含 RO(TEXT) 和 RW(DATA) 段.重定位前的值为0x0,此时指向flash,

//重定位后则指向RAM中的某一地址

//由此可以知道:  _bss_start - _armboot_start 的值即是在第一阶段从

//flash中重定位到RAM中的那部分代码的长度,也即可TEXT和DATA数据段,

//这个值与start.S中的重定位那部分代码所计算的值是相等的

//所以,monitor_flash_len表示从flash中搬来的代码的长度

monitor_flash_len = _bss_start - _armboot_start;

//_bss_start 在u-boot.lds中定位

//各设置的初始化.当返回值不为0时表示初始化失败 ,此时会调用 hang()函数

//打印一错误提示信息,然后进入死循环

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()对其进行初始化.

#ifndef CFG_NO_FLASH

/* configure available FLASH banks */

size = flash_init ();

display_flash_config (size); //打印flash的信息,这里仅输出它的大小

#endif /* CFG_NO_FLASH */

#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

# ifndef PAGE_SIZE

#   define PAGE_SIZE 4096

# endif

/*

* reserve memory for LCD display (always full pages)

*///为LCD分配RAM(内存)空间

/* 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 */

//malloc函数使用缓冲区的初始化

mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

//如果定义了命令和NAND命令,则初始化nand

#if (CONFIG_COMMANDS & CFG_CMD_NAND)

puts ("NAND:  ");

nand_init();  /* go init the NAND */

#endif

#ifdef CONFIG_HAS_DATAFLASH

AT91F_DataflashInit();

dataflash_print_info();

#endif

/* initialize environment 环境的初始化,代码在common\env_common.c中 */

env_relocate ();

#ifdef CONFIG_VFD

/* must do this after the framebuffer is allocated */

drv_vfd_init();

#endif /* CONFIG_VFD */

/* IP Address 为全局变量的成员赋值:IP地址*/

gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");//ipaddr在smdk2440.h中的CONFIG_IPADDR中出现,应该是该常量

/* MAC Address *///高处MAC地址 ,并赋给gd的成员变量

{

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;

}

#ifdef CONFIG_HAS_ETH1

i = getenv_r ("eth1addr", tmp, sizeof (tmp));

s = (i > 0) ? tmp : NULL;

for (reg = 0; reg < 6; ++reg) {

gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;

if (s)

s = (*e) ? e + 1 : e;

}

#endif

}

//这个函数涉及好多,我没深入分析,若哪位分析了希望能分享一下:cczy_english@163.com,将不胜感激

devices_init (); /* get the devices list going. */

#ifdef CONFIG_CMC_PU2

load_sernum_ethaddr ();

#endif /* CONFIG_CMC_PU2 */

//初始化跳转表,对gd中的jt(函数跳转表)数组进行初始化,其中保存着一些函数的入口地址

jumptable_init ();

console_init_r (); /* fully init console as a device 我没具体分析内部实现*/

#if defined(CONFIG_MISC_INIT_R)

/* miscellaneous platform dependent initialisations, miscellaneous:各色各样混在一起, 混杂的*/

misc_init_r ();

#endif

/* enable exceptions 设置cpsr的I和F位以充许中断*/

enable_interrupts ();

/* Perform network card initialisation if necessary */

#ifdef CONFIG_DRIVER_CS8900

cs8900_get_enetaddr (gd->bd->bi_enetaddr);

#endif

#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)

if (getenv ("ethaddr")) {

smc_set_mac_addr(gd->bd->bi_enetaddr);

}

#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

/* Initialize from environment */

if ((s = getenv ("loadaddr")) != NULL) {

load_addr = simple_strtoul (s, NULL, 16);

}

#if (CONFIG_COMMANDS & CFG_CMD_NET)

if ((s = getenv ("bootfile")) != NULL) {

copy_filename (BootFile, s, sizeof (BootFile));

}

#endif /* CFG_CMD_NET */

#ifdef BOARD_LATE_INIT

board_late_init ();

#endif

#if (CONFIG_COMMANDS & CFG_CMD_NET)

#if defined(CONFIG_NET_MULTI)

puts ("Net:   ");

#endif

eth_initialize(gd->bd);

#endif

/* main_loop() can return to retry autoboot, if so just run it again. */

//直接进入main_loop 该函数在common\main.c中

//!!!!!!!!至此,硬件初始化完成

for (;;) {

main_loop ();

}

/* NOTREACHED - no way out of command loop except booting */

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值