第一阶段:http://blog.csdn.net/lizuobin2/article/details/52054293
上篇文章说到,再清 BSS 段之后,CPU 跳转到 sdram 里的 start_armboot() 函数,本文,分析 uboot 流程的第二阶段、第三阶段。
start_armboot函数在lib_arm/board.c中定义,是U-Boot第二阶段代码的入口。第二阶段的主要工作是进行单板级别的初始化,初始化 nandflash 、norflash 、初始化串口、设置环境变量、最终跳转到 main_loop 里,接收串口传递进来的各种命令。
第三阶段主要的工作就是设置 uboot 将要传递给内核的 tag 以及解析 uboot 头部里包含的信息,最终跳转到内核起始地址去执行,将主控权交给内核。
一、gd 结构体
-
/* Pointer is writable since we allocated a register for it */
-
gd = ( gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof( gd_t));
-
-
memset (( void*)gd, 0, sizeof ( gd_t));
-
gd->bd = ( bd_t*)(( char*)gd - sizeof( bd_t));
-
memset (gd->bd, 0, sizeof ( bd_t));
-
.globl _armboot_start
-
_armboot_start:
-
.word _start
-
extern ulong _armboot_start; /* code start */
-
extern ulong _bss_start; /* code + data end == BSS start */
-
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? */
-
unsigned long fb_base; /* base address of frame buffer */
-
-
unsigned char vfd_type; /* display type */
-
-
-
unsigned long cpu_clk; /* CPU clock in Hz! */
-
unsigned long bus_clk;
-
unsigned long ram_size; /* RAM size */
-
unsigned long reset_status; /* reset status register at boot */
-
-
void **jt; /* jump table */
-
} gd_t;
-
typedef struct bd_info {
-
int bi_baudrate; /* serial console baudrate 串口波特率*/
-
unsigned long bi_ip_addr; /* IP Address */
-
unsigned char bi_enetaddr[ 6]; /* Ethernet adress 以太网地址*/
-
struct environment_s *bi_env;
-
ulong bi_arch_number; /* unique id for this board 机器ID*/
-
ulong bi_boot_params; /* where this board expects params 参数区地址*/
-
struct /* RAM configuration */
-
{
-
ulong start;
-
ulong size;
-
} bi_dram[CONFIG_NR_DRAM_BANKS];
-
-
/* second onboard ethernet port */
-
unsigned char bi_enet1addr[ 6];
-
-
} bd_t;
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
二、初始化工作
前边分配完了 gd 结构体之后,就开始一系列的初始化工作,初始化函数定义在 init_sequence 中
-
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
-
if ((*init_fnc_ptr)() != 0) {
-
hang ();
-
}
-
}
-
init_fnc_t *init_sequence[] = {
-
cpu_init, /* basic cpu dependent setup */
-
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
-
dram_init, /* configure available RAM banks */
-
display_dram_config,
-
NULL,
-
};
-
int cpu_init (void)
-
{
-
/*
-
* setup up stacks if necessary
-
*/
-
-
IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
-
FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
-
-
return 0;
-
}
2.2 board_init
-
int board_init (void)
-
{
-
//获得时钟寄存器的地址,s3c24x0通用无需修改。
-
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
-
-
//获得GPIO寄存器的地址,s3c24x0不通用需要增加2440GPIO寄存器。
-
S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
-
-
/* to reduce PLL lock time, adjust the LOCKTIME register */
-
clk_power->LOCKTIME = 0xFFFFFF;
-
-
/* configure MPLL */
-
/*MPLL初始化 参数位于./board/smdk2410/smdk2410.c中,2440应有自己的文件以及定义*/
-
clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);
-
-
/* some delay between MPLL and UPLL */
-
delay ( 4000);
-
-
/* configure UPLL */
-
clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);
-
-
/* some delay between MPLL and UPLL */
-
delay ( 8000);
-
-
/* set up the I/O ports */
-
gpio->GPACON = 0x007FFFFF;
-
gpio->GPBCON = 0x00044555;
-
gpio->GPBUP = 0x000007FF;
-
gpio->GPCCON = 0xAAAAAAAA;
-
gpio->GPCUP = 0x0000FFFF;
-
gpio->GPDCON = 0xAAAAAAAA;
-
gpio->GPDUP = 0x0000FFFF;
-
gpio->GPECON = 0xAAAAAAAA;
-
gpio->GPEUP = 0x0000FFFF;
-
gpio->GPFCON = 0x000055AA;
-
gpio->GPFUP = 0x000000FF;
-
gpio->GPGCON = 0xFF95FFBA;
-
gpio->GPGUP = 0x0000FFFF;
-
gpio->GPHCON = 0x002AFAAA;
-
gpio->GPHUP = 0x000007FF;
-
-
/* arch number of SMDK2410-Board */
-
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
-
//#define MACH_TYPE_SMDK2410 193 位于./include/asm-asm/Mach-types.h中定义
-
-
/* adress of boot parameters */
-
gd->bd->bi_boot_params = 0x30000100; //内核启动参数存放地址
-
-
//开数据 指令cache
-
icache_enable();
-
dcache_enable();
-
-
return 0;
-
}
2.3 interrupt_init PWM的初始化,无关紧要
2.4 env_init
-
typedef struct environment_s {
-
unsigned long crc; /* CRC32 over data bytes */
-
unsigned char flags; /* active/obsolete flags */
-
unsigned char data[ENV_SIZE]; /* Environment data */
-
} env_t;
-
env_t *env_ptr = ( env_t *)CFG_ENV_ADDR; // 0+0x070000
-
static env_t *flash_addr = ( env_t *)CFG_ENV_ADDR; // 0+0x070000
-
int env_init(void)
-
{
-
if (crc32( 0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
-
gd->env_addr = (ulong)&(env_ptr->data);
-
gd->env_valid = 1;
-
return( 0);
-
}
-
//如果校验失败,则使用默认的环境变量
-
gd->env_addr = (ulong)&default_environment[ 0]; //将默认环境变量的地址赋给全局指针gd->env_addr
-
gd->env_valid = 0;
-
return ( 0);
-
}
1、uboot 第一次启动,那么 norflash 这个地址处并没有任何东西,校验失败,则使用默认的环境变量,使全局指针 gd->env_addr 指向内存中的默认环境变量,并设置标志位 gd->env_valid 为 0 。
2、uboot 非第一次启动,那么校验成功,将全局指针 gd->env_addr 指向环境变量,并使标志位 gd->env_valid 置一。
默认环境变量的 定义 CONFIG_BOOTARGS 等宏在 Smdk2410.h (include\configs)
-
uchar default_environment[] = {
-
#ifdef CONFIG_BOOTARGS
-
"bootargs=" CONFIG_BOOTARGS "\0"
-
#endif
-
#ifdef CONFIG_BOOTCOMMAND
-
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
-
#endif
-
#ifdef CONFIG_RAMBOOTCOMMAND
-
"ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
-
#endif
-
#ifdef CONFIG_NFSBOOTCOMMAND
-
"nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
-
#endif
-
# if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
-
"bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0"
-
#endif
-
# if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
-
"baudrate=" MK_STR(CONFIG_BAUDRATE) "\0"
-
#endif
-
#ifdef CONFIG_LOADS_ECHO
-
"loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0"
-
#endif
-
#ifdef CONFIG_ETHADDR
-
"ethaddr=" MK_STR(CONFIG_ETHADDR) "\0"
-
#endif
-
#ifdef CONFIG_ETH1ADDR
-
"eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0"
-
#endif
-
#ifdef CONFIG_ETH2ADDR
-
"eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0"
-
#endif
-
#ifdef CONFIG_ETH3ADDR
-
"eth3addr=" MK_STR(CONFIG_ETH3ADDR) "\0"
-
#endif
-
#ifdef CONFIG_IPADDR
-
"ipaddr=" MK_STR(CONFIG_IPADDR) "\0"
-
#endif
-
#ifdef CONFIG_SERVERIP
-
"serverip=" MK_STR(CONFIG_SERVERIP) "\0"
-
#endif
-
#ifdef CFG_AUTOLOAD
-
"autoload=" CFG_AUTOLOAD "\0"
-
#endif
-
#ifdef CONFIG_PREBOOT
-
"preboot=" CONFIG_PREBOOT "\0"
-
#endif
-
#ifdef CONFIG_ROOTPATH
-
"rootpath=" MK_STR(CONFIG_ROOTPATH) "\0"
-
#endif
-
#ifdef CONFIG_GATEWAYIP
-
"gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0"
-
#endif
-
#ifdef CONFIG_NETMASK
-
"netmask=" MK_STR(CONFIG_NETMASK) "\0"
-
#endif
-
#ifdef CONFIG_HOSTNAME
-
"hostname=" MK_STR(CONFIG_HOSTNAME) "\0"
-
#endif
-
#ifdef CONFIG_BOOTFILE
-
"bootfile=" MK_STR(CONFIG_BOOTFILE) "\0"
-
#endif
-
#ifdef CONFIG_LOADADDR
-
"loadaddr=" MK_STR(CONFIG_LOADADDR) "\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"
-
};
#define CONFIG_BOOTARGS "root=ramfs devfs=mount console=ttySA0,9600"
确实如同前边说所,data 里边放置的都是一个个字符串,两个环境变量之间 通过 “\0”隔开。
2.5 init_baudrate 设置波特率 首先从环境变量中读取,如果没有则设置为 115200
2.6 serial_init 串口初始化
-
int serial_init (void)
-
{
-
serial_setbrg ();
-
-
return ( 0);
-
}
-
void serial_setbrg (void)
-
{
-
S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);
-
int i;
-
unsigned int reg = 0;
-
-
/* value is calculated so : (int)(PCLK/16./baudrate) -1 */
-
reg = get_PCLK() / ( 16 * gd->baudrate) - 1;
-
-
/* FIFO enable, Tx/Rx FIFO clear */
-
uart->UFCON = 0x07;
-
uart->UMCON = 0x0;
-
/* Normal,No parity,1 stop,8 bit */
-
uart->ULCON = 0x3;
-
/*
-
* tx=level,rx=edge,disable timeout int.,enable rx error int.,
-
* normal,interrupt or polling
-
*/
-
uart->UCON = 0x245;
-
uart->UBRDIV = reg;
-
-
-
uart->UMCON = 0x1; /* RTS up */
-
-
for (i = 0; i < 100; i++);
-
}
2.7 console_init_f 控制台初始化,无关紧要
2.8 display_banner 打印代码段 BSS段等地址信息
2.9 dram_init
-
int dram_init (void)
-
{
-
gd->bd->bi_dram[ 0].start = PHYS_SDRAM_1;
-
gd->bd->bi_dram[ 0].size = PHYS_SDRAM_1_SIZE;
-
-
return 0;
-
}
2.A display_dram_config 打印 sdram 信息
2.B norflash 初始化
-
-
/* configure available FLASH banks */
-
size = flash_init ();
-
-
puts ( "NAND: ");
-
nand_init(); /* go init the NAND */
-
/* initialize environment */
-
env_relocate ();
-
void env_relocate (void)
-
{
-
DEBUGF ( "%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
-
gd->reloc_off);
-
-
-
//在sdram中开辟一开内存空间并使env_ptr指向它
-
env_ptr = ( env_t *) malloc (CFG_ENV_SIZE);
-
DEBUGF ( "%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
-
-
/*
-
* After relocation to RAM, we can always use the "memory" functions
-
*/
-
env_get_char = env_get_char_memory;
-
-
if (gd->env_valid == 0) // 开始的时候使用的默认环境变量,前边提过了
-
puts ( "*** Warning - bad CRC, using default environment\n\n");
-
SHOW_BOOT_PROGRESS ( -1);
-
-
-
if ( sizeof(default_environment) > ENV_SIZE)
-
{
-
puts ( "*** Error - default environment is too large\n\n");
-
return;
-
}
-
//向sdram中的env_ptr地址拷贝默认的默认环境变量。
-
memset (env_ptr, 0, sizeof( env_t)); //写之前 先初始化
-
memcpy (env_ptr->data,
-
default_environment,
-
sizeof(default_environment));
-
-
env_ptr->flags = 0xFF;
-
-
env_crc_update (); //env_ptr->crc写入校验值
-
gd->env_valid = 1; //告诉全局gd env可用
-
}
-
else {
-
//如果,使用的非默认的env,也就是 nor中有
-
env_relocate_spec (); //将norflash中的env拷贝到sdram
-
}
-
gd->env_addr = (ulong)&(env_ptr->data); //将sdram中 env->data地址告诉gd
-
-
-
disable_nvram();
-
-
}
-
-
-
void env_relocate_spec (void)
-
{
-
//注意次函数从env_relocate()中调用时,env_ptr已经改变为新在内存中开辟的地址
-
//因此次函数功能:将Nor中的env,拷贝到sdram
-
memcpy (env_ptr, ( void*)flash_addr, CFG_ENV_SIZE);
-
}
-
-
-
int saveenv(void)
-
{
-
int len, rc;
-
ulong end_addr;
-
ulong flash_sect_addr;
-
-
uchar *env_buffer = (uchar *)env_ptr; //指向flash中的 0+0x070000
-
-
int rcode = 0;
-
-
flash_sect_addr = (ulong)flash_addr; //flash_sect_addr保存的是env_ptr的地址,也就是0x070000
-
len = CFG_ENV_SIZE;
-
-
end_addr = flash_sect_addr + len - 1; //env的末地址
-
-
-
debug ( "Protect off %08lX ... %08lX\n",
-
(ulong)flash_sect_addr, end_addr);
-
//后边是一些 擦除 写操作,可以看出nandflash的擦除写都是按sect来的,那么env在Nar中的偏移地址肯定得是sect对齐的。
-
if (flash_sect_protect ( 0, flash_sect_addr, end_addr))
-
return 1;
-
-
puts ( "Erasing Flash...");
-
if (flash_sect_erase (flash_sect_addr, end_addr))
-
return 1;
-
-
puts ( "Writing to Flash... ");
-
rc = flash_write(( char *)env_buffer, flash_sect_addr, len);
-
if (rc != 0) {
-
flash_perror (rc);
-
rcode = 1;
-
} else {
-
puts ( "done\n");
-
}
-
-
/* try to re-protect */
-
( void) flash_sect_protect ( 1, flash_sect_addr, end_addr);
-
return rcode;
-
}
-
/* 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;
-
}
-
-
-
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;
-
}
2.F devices_init (); 没研究过,不知道干啥的,应该没啥影响
2.G jumptable_init ()
-
void jumptable_init (void)
-
{
-
int i;
-
-
gd->jt = ( void **) malloc (XF_MAX * sizeof ( void *));
-
for (i = 0; i < XF_MAX; i++)
-
gd->jt[i] = ( void *) dummy;
-
-
gd->jt[XF_get_version] = ( void *) get_version;
-
gd->jt[XF_malloc] = ( void *) malloc;
-
gd->jt[XF_free] = ( void *) free;
-
gd->jt[XF_getenv] = ( void *) getenv;
-
gd->jt[XF_setenv] = ( void *) setenv;
-
gd->jt[XF_get_timer] = ( void *) get_timer;
-
gd->jt[XF_simple_strtoul] = ( void *) simple_strtoul;
-
gd->jt[XF_udelay] = ( void *) udelay;
-
-
gd->jt[XF_install_hdlr] = ( void *) irq_install_handler;
-
gd->jt[XF_free_hdlr] = ( void *) irq_free_handler;
-
-
-
gd->jt[XF_i2c_write] = ( void *) i2c_write;
-
gd->jt[XF_i2c_read] = ( void *) i2c_read;
-
2.H enable_interrupts 在 cpsr 中使能 irq fiq
-
void enable_interrupts (void)
-
{
-
unsigned long temp;
-
__asm__ __volatile__( "mrs %0, cpsr\n"
-
"bic %0, %0, #0x80\n"
-
"msr cpsr_c, %0"
-
: "=r" (temp)
-
:
-
: "memory");
-
}
-
-
-
puts ( "Net: ");
-
-
eth_initialize(gd->bd);
-
for (;;) {
-
main_loop ();
-
}
我们在 uboot 中输入 bootm 命令,U-Boot接收输入的字符串“bootm”,传递给 run_command 函数。run_command 函数调用 common/command.c 中实现的 find_cmd 函数在 __u_boot_cmd_start 与 __u_boot_cmd_end间查找命令,并返回 bootm 命令的 cmd_tbl_t 结构。然后 run_command 函数使用返回的 cmd_tbl_t 结构中的函数指针调用 bootm 命令的响应函数 do_bootm。
-
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
-
{
-
......
-
image_header_t *hdr = &header; //uimage 是内核加了一个4K的头部,这个头部的内容是按照结构体image_header_t来放在,是image传递给Uboot的信息。
-
......
-
if (argc < 2) {
-
addr = load_addr; //如果bootm的参数小于2 则使用默认的加载地址
-
} else {
-
addr = simple_strtoul(argv[ 1], NULL, 16);
-
}
-
......
-
switch (hdr->ih_comp) {
-
case IH_COMP_NONE:
-
if(ntohl(hdr->ih_load) == addr) {
-
/*
-
这里判断“uimage头部里指定的加载地址”与bootm指定的加载地址是否相等,不相等则需要移动
-
判断的方式有两种
-
1、判断 uimage头部里指定的加载地址 == bootm指定的加载地址 (hdr->ih_load == addr)
-
此时:
-
实际存放的地址 == uimage加载地址 == uimage连接地址-64字节
-
bootm == 实际存放的地址
-
例子:
-
实际存放在 0x30007fc0
-
bootm 0x30007fc0
-
加载地址 0x30007fc0
-
连接地址 0x30008000
-
1、uboot根据Bootm指定的0x30007fc0去找image,实际地址为0x30007fc0,找到头部
-
2、读出头部里的加载地址,判断是否和Bootm相等,相等则不移动,启动
-
2、判断 uimage头部里指定的加载地址 == bootm指定的加载地址 + 64字节 (hdr->ih_load == addr+64字节)
-
此时:
-
实际存放地址+64字节 == uimage加载地址 == uimage连接地址
-
bootm == 实际存放的地址
-
例子:
-
实际存放在 0x30007fc0
-
bootm 0x30007fc0
-
加载地址 0x30008000
-
连接地址 0x30008000
-
1、uboot根据Bootm指定的0x30007fc0去找image,实际地址为0x30007fc0,找到头部
-
2、读出头部里的加载地址,判断是否和Bootm + 字节相等,相等则不移动,启动
-
-
首先bootm的地址要和我们 实际存放(不管它的加载地址是多少) 整个uimage的首地址吻合,这样就可以找到头部。
-
这里存在两种情况,我们可以看到 Uboot源码里
-
1、 hdr->ih_load == addr
-
也就是说判断的是bootm_addr 与uimage里的ih_load加载地址是否相等,这样的话,我们在制作uimage的时候就应该让ih_load=bootmaddr
-
那么uimage里的另一个参数,连接地址就应该等于bootm+4K
-
2、hdr->ih_load == addr+64字节 友善以及韦东山老师的uboot里判断条件都被改成了这样
-
那么,uimage里的Load地址和连接地址应该相等,等于bootm+64字节
-
*/
-
printf ( " XIP %s ... ", name);
-
} else {
-
......do_bootm_linux (cmdtp, flag, argc, argv,addr, len_ptr, verify);
-
}
-
lib_arm/armlinux. c
-
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
-
ulong addr, ulong *len_ptr, int verify)
-
{
-
ulong len = 0, checksum;
-
ulong initrd_start, initrd_end;
-
ulong data;
-
/* 启动内核的函数指针 */
-
void (*theKernel)( int zero, int arch, uint params);
-
image_header_t *hdr = &header;
-
bd_t *bd = gd->bd;
-
/* 获得命令行参数 */
-
-
char *commandline = getenv ( "bootargs");
-
-
-
theKernel = ( void (*)( int, int, uint))ntohl(hdr->ih_ep)
-
-
# if defined (CONFIG_SETUP_MEMORY_TAGS) || \
-
defined (CONFIG_CMDLINE_TAG) || \
-
defined (CONFIG_INITRD_TAG) || \
-
defined (CONFIG_SERIAL_TAG) || \
-
defined (CONFIG_REVISION_TAG) || \
-
defined (CONFIG_LCD) || \
-
defined (CONFIG_VFD)
-
setup_start_tag (bd); // 标记必须以它起始
-
/* 在/include/config/smdk2410.h这些宏一个都没定义,因此,uboot一个标记都没给内核放,这样是不对滴,最起码的,内存标记以及cmdline应该告诉内核。不然跳到内核后调用串口驱动的初始化,会让你的串口啥都打印不出来。关于什么是 tag 以及原理,在自己写bootloader一文中介绍过了。
-
*/
-
-
setup_serial_tag (¶ms);
-
-
-
setup_revision_tag (¶ms);
-
-
-
setup_memory_tags (bd); /* 必须放 */
-
-
-
setup_commandline_tag (bd, commandline); /* 必须放 */
-
-
-
if (initrd_start && initrd_end)
-
setup_initrd_tag (bd, initrd_start, initrd_end);
-
-
-
setup_videolfb_tag (( gd_t *) gd);
-
-
setup_end_tag (bd); /* 标记必须以它结尾 */
-
-
-
/* 这句话很常见~~~也经常从此没了下文~~~ */
-
printf ( "\nStarting kernel ...\n\n");
-
-
cleanup_before_linux ();
-
/* 启动内核 第1个参数 0 ,第二个参数 机器ID 第三个参数 tag 地址 */
-
theKernel ( 0, bd->bi_arch_number, bd->bi_boot_params);
-
}
四、添加自定义命令
下面以添加menu命令(启动菜单)为例讲解U-Boot添加命令的方法。
1、建立common/cmd_menu.c
习惯上通用命令源代码放在common目录下,与开发板专有命令源代码则放在board/<board_dir>目录下,并且习惯以“cmd_<命令名>.c”为文件名。
2、定义“menu”命令
在 cmd_menu.c中使用如下的代码定义“menu”命令:参考 cmd_bootm.c
-
U_BOOT_CMD(
-
bootm, CFG_MAXARGS, 1, do_bootm,
-
"bootm - boot application image from memory\n",
-
"[addr [arg ...]]\n - boot application image stored in memory\n"
-
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
-
"\t'arg' can be the address of an initrd image\n"
-
#ifdef CONFIG_OF_FLAT_TREE
-
"\tWhen booting a Linux kernel which requires a flat device-tree\n"
-
"\ta third argument is required which is the address of the of the\n"
-
"\tdevice-tree blob. To boot that kernel without an initrd image,\n"
-
"\tuse a '-' for the second argument. If you do not pass a third\n"
-
"\ta bd_info struct will be passed instead\n"
-
#endif
-
);
-
U_BOOT_CMD(
-
menu, 3, 0, do_menu,
-
"menu - display a menu, to select the items to do something\n",
-
" - display a menu, to select the items to do something"
-
);
各个参数的意义如下:
name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
maxargs:命令的最大参数个数
rep:是否自动重复(按Enter键是否会重复执行)
cmd:该命令对应的响应函数
usage:简短的使用说明(字符串)
help:较详细的使用说明(字符串)
U_BOOT_CMD宏在include/command.h中定义,分为有无帮助信息两种:
-
struct cmd_tbl_s {
-
char *name; /* 命令名 */
-
int maxargs; /* 最大参数个数 */
-
int repeatable; /* 是否自动重复 */
-
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 响应函数 */
-
char *usage; /* 简短的帮助信息 */
-
-
char *help; /* 较详细的帮助信息 */
-
-
-
-
/* 自动补全参数 */
-
int (*complete)( int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
-
-
};
-
typedef struct cmd_tbl_s cmd_tbl_t;
3、实现命令的函数
在cmd_menu.c中添加“menu”命令的响应函数的实现。具体的实现代码略:
-
int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
-
{
-
/* 实现代码略 */
-
}
在common/Makefile中加入如下代码:
COBJS-$(CONFIG_BOOT_MENU) += cmd_menu.o
在include/configs/smdk2410.h加入如代码:
#define CONFIG_BOOT_MENU 1
重新编译下载U-Boot就可以使用menu命令了