(其中就指定了我们用那个串口来输出调试信息,一般CPU都有几路串口,LINUX内核中会按照CPU介绍手册中分配的顺序来列出串口序号,根据UBOOT传入参数来决定调用那几路)
UBOOT版本:uboot-2010.12.
相关函数调用简略关系:
do_bootm_linux->setup_commandline_tag->kernel_entry
do_bootm_linux:/arch/arm/lib/bottm.c
当采用bootm来引导LINUX时在执行到引导LINUX流程时会执行到do_bootm_linux()函数(UBOOT使用BOOTM来引导LINUX的原理不在本节分析范围内,本节只分析传参原理)。
Do_bootm->do_bootm_linux:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
bd_t*bd = gd->bd;
#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");//获取传入的引导参数,我们参入的参数为:
【Android-CommandLine = console=ttySAC0,115200n8 androidboot.console=ttySAC0 ctp=2 skipcali=y vmalloc=384m ethmac=1C:6F:65:34:51:7E androidboot.selinux=permissive】
#endif
s = getenv ("machid");//获得机器码
machid = simple_strtoul (s, NULL, 16);
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG)
setup_start_tag (bd); //让params变量指向(struct tag *) bd->bi_boot_params;既params指向一个tag地址,后面在对params的操作就是对tag的操作
#ifdef CONFIG_SERIAL_TAG
setup_serial_tag (¶ms);
/*
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params;//params指向
//gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100)=0x40000100;//在机器的板级引导文件中初始化
/arh/arm/board/厂家/具体CPU型号文件。
/* DRAM Base */
#define PHYS_SDRAM_1 CONFIG_SYS_SDRAM_BASE /* SDRAM Bank #1 */
#define CONFIG_SYS_SDRAM_BASE0x40000000
params->hdr.tag = ATAG_CORE; //
/* The list must start with an ATAG_CORE node */
#define ATAG_CORE0x54410001在UBOOT传入参数时TAG标记的地址处第一个标记码必须在此是固定的,因为LINUX在解析是会判断这个地址处的这个值,相当才会解析的
params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params);
params = tag_next (params);//params是一个链表,及是多个TAG结构体的结合,也就说明了UBOOT可以传入多种参数,操控台参数仅仅是其中一种,bd->bi_boot_params是开始地址,是传入LINXU的始地址,但传入不同的参数TAG时这个变量不能更改,但又为了传入多个值则必须由另一个变量来递增
}
*/
#endif
#ifdef CONFIG_REVISION_TAG
setup_revision_tag (¶ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);//把传入的命令行放入TAG中
/*
static void setup_commandline_tag (bd_t *bd, char *commandline)
{
char *p;
if (!commandline)
return;
/* eat leading white space */
for (p = commandline; *p == ' '; p++);
/* skip non-existent command lines so the kernel will still
* use its default command line.
*/
if (*p == '\0')
return;
params->hdr.tag = ATAG_CMDLINE;
///* command line: \0 terminated string */
#define ATAG_CMDLINE0x54410009对于传入的命令行参数的tag类型必须是唯一的,这儿应当在LINUX解析命令行参数时会判断的,不能错。
params->hdr.size =
(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline, p);
//struct tag_cmdline {
charcmdline[1];/* this is the minimum size */
};//把传入的参数存储在tag结构中的tag_cmdline数组中。提取时需要在儿去取
params = tag_next (params);
}
*/
#endif
#ifdef CONFIG_INITRD_TAG
if (images->rd_start && images->rd_end)
setup_initrd_tag (bd, images->rd_start, images->rd_end);
#endif
setup_end_tag(bd);
/*
static void setup_end_tag (bd_t *bd)
{
params->hdr.tag = ATAG_NONE;//TAG结束标记,在LINUX解析时解析到此变量说明传入的参数结束了,不再继续解析了。判断此内存处的变量值。
params->hdr.size = 0;
}
*/
#endif
上面代码中用一个gd,它是由一个DECLARE_GLOBAL_DATA_PTR在申明的,注册了一个寄存器变量gd,它代表寄存器r8,同时r8指向的是gd_t结构体变量,这个结构中存的是板级设备信息/arch/arm/include/asm/global_data.h。bd指向gd->bd.通过上面代码我们要传入LINUX的参数存存储在了0x40000100开始的tag结构类型的一个链表中(可以传内存信息 初始化信息等TAG)。上面代码用的两个非常重要结构体如下:
struct tag {
struct tag_header hdr;
union {
struct tag_corecore;
struct tag_mem32mem;
struct tag_videotextvideotext;
struct tag_ramdiskramdisk;
struct tag_initrdinitrd;
struct tag_serialnrserialnr;
struct tag_revisionrevision;
struct tag_videolfbvideolfb;
struct tag_cmdlinecmdline;
/*
* Acorn specific
*/
struct tag_acornacorn;
/*
* DC21285 specific
*/
struct tag_memclkmemclk;
} u;
};//TAG结构体中列出了UBOOT可以传入LINUX的参数类型,看u共用体
typedefstructglobal_data {
bd_t*bd;
unsigned longflags;
unsigned longbaudrate;
unsigned longhave_console;/* serial_init() was called */
unsigned longenv_addr;/* Address of Environment struct */
unsigned longenv_valid;/* Checksum of Environment valid? */
unsigned longfb_base;/* base address of frame buffer */
#ifdef CONFIG_VFD
unsigned charvfd_type;/* display type */
#endif
#ifdef CONFIG_FSL_ESDHC
unsigned longsdhc_clk;
#endif
#ifdef CONFIG_AT91FAMILY
/* "static data" needed by at91's clock.c */
unsigned longcpu_clk_rate_hz;
unsigned longmain_clk_rate_hz;
unsigned longmck_rate_hz;
unsigned longplla_rate_hz;
unsigned longpllb_rate_hz;
unsigned longat91_pllb_usb_init;
#endif
#ifdef CONFIG_ARM
/* "static data" needed by most of timer.c on ARM platforms */
unsigned longtimer_rate_hz;
unsigned longtbl;
unsigned longtbu;
unsigned long longtimer_reset_value;
unsigned longlastinc;
#endif
unsigned longrelocaddr;/* Start address of U-Boot in RAM */
phys_size_tram_size;/* RAM size */
unsigned longmon_len;/* monitor len */
unsigned longirq_sp;/* irq stack pointer */
unsigned longstart_addr_sp;/* start_addr_stackpointer */
unsigned longreloc_off;
#if !(defined(CONFIG_SYS_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE))
unsigned longtlb_addr;
#endif
void**jt;/* jump table */
charenv_buf[32];/* buffer for getenv() before reloc. */
} gd_t; //这个结构体是定义的全局寄存器变量的类型结构本。说了我们定义全局寄存变量的类型为它。
通过上面代码我们要传入LINUX的参数存存储在了0x40000100开始的tag结构类型的一个链表中(可以传内存信息 初始化信息等TAG),由下面函数传入LINUX中。
kernel_entry(0, machid, bd->bi_boot_params);bd->bi_boot_params=0x40000100
R0=0,r1=machid,r2=0x40000100.
kernel_entry大致实现原理(具体细节不在本节分析范畴)
movpc CONFIG_PHY_UBOOT_BASE
#define CONFIG_PHY_UBOOT_BASECONFIG_SYS_SDRAM_BASE + 0x3e00000
/* DRAM Base */
#define CONFIG_SYS_SDRAM_BASE0x40000000
CONFIG_PHY_UBOOT_BASE=0x40000000+0x3e00000=0x43e00 0000//Linux内核代码的存放地址
通过上面调转就进入LINUX中了。
其中传和的参数分别为R0=0,r1=machid,r2=0x40000100.
同时0X40000100存放的内容为:
【Android-CommandLine =console=ttySAC0,115200n8 androidboot.console=ttySAC0 ctp=2 skipcali=y vmalloc=384m ethmac=1C:6F:65:34:51:7E androidboot.selinux=permissive】黑体字为我们在LINUX中要观注的,其它的暂时不分析