使用u-boot引导linux的启动过程
在使用u-boot引导linux的过程中,需要进行一系列的步骤,以下从内核映像的制作开始概述linux的整个启动过程。
一.内核映像的制作
在u-boot编译过程中有生成工具mkimage,该工具用来生成u-boot的bootm命令引导的内核映像,命令如下:
#mkimage –n “linux-2.6.26.5” –A arm –O linux –T kernel –C none –a 0x30008000 –e 0x30008040 –d zImage zImage.img
执行该命令后,原来的内核文件在开头被加上了64字节的头信息,该信息的数据结构如下:
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address ,该部分即0x30008000,即系统的加载的地址 */
uint32_t ih_ep; /* Entry Point Address ,该部分即头后的地址0x30008040,即系统的入口地址 */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System linux */
uint8_t ih_arch; /* CPU architecture arm */
uint8_t ih_type; /* Image Type kernel */
uint8_t ih_comp; /* Compression Type none */
uint8_t ih_name[IH_NMLEN]; /* Image Name*/ IH_NMLEN=32字节
} image_header_t;
二.通过bootm引导linux内核启动
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,
&images, &os_data, &os_len);//取得内核信息,包括所在的地址
switch (genimg_get_format (os_hdr)) {
case IMAGE_FORMAT_LEGACY:
type = image_get_type (os_hdr);
comp = image_get_comp (os_hdr);
os = image_get_os (os_hdr);
image_end = image_get_image_end (os_hdr);
load_start = image_get_load (os_hdr);
break;
//以上是从mkimage生成的64字节中取信息
……
switch (os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
fixup_silent_linux();
#endif
do_bootm_linux (cmdtp, flag, argc, argv, &images);//启动Linux内核
break; //images在函数boot_get_kernel()中配置
}
//
static void *boot_get_kernel (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
bootm_headers_t *images, ulong *os_data, ulong *os_len)
{
if (argc < 2) {//没有参数的情况,直接bootm
img_addr = load_addr; //ulong load_addr = CFG_LOAD_ADDR;
//fs2410即为 0x30008000
debug ("* kernel: default image load address = 0x%08lx\n",
load_addr);
}
switch (genimg_get_format ((void *)img_addr)) {
case IMAGE_FORMAT_LEGACY:
printf ("## Booting kernel from Legacy Image at %08lx ...\n",
img_addr);
hdr = image_get_kernel (img_addr, images->verify);
//以上函数即为image_header_t *hdr = (image_header_t *)img_addr;
if (!hdr)
return NULL;
show_boot_progress (5);
/* get os_data and os_len */
switch (image_get_type (hdr)) {
case IH_TYPE_KERNEL:
*os_data = image_get_data (hdr);
*os_len = image_get_data_size (hdr);
break;
memmove (&images->legacy_hdr_os_copy, hdr, sizeof(image_header_t));
//将hdr的内容复制到images中去
/* save pointer to image header */
images->legacy_hdr_os = hdr;
return (void *)img_addr;
}
//
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
bootm_headers_t *images)
{
int machid = bd->bi_arch_number; //machine id, linux内核中会验证
char *commandline = getenv ("bootargs");//传递的参数
if (images->legacy_hdr_valid) {
ep = image_get_ep (&images->legacy_hdr_os_copy);
}
theKernel = (void (*)(int, int, uint))ep; //即为0x30008040的地址
setup_start_tag (bd);
setup_commandline_tag (bd, commandline);//定义传递到内核中的参数
theKernel (0, machid, bd->bi_boot_params);//启动内核
}
/
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params;//传输的参数的地址
params->hdr.tag = ATAG_CORE;
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);
}
int board_init (void)
{
/* arch number of SMDK2410-Board */
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410; //193
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100; //参数的地址
}
本段部分参考百度文库《bootm命令浅析》
三 linux内核部分的启动过程
b 1f
.word 0x016f2818 @ Magic numbers to help the loader
.word start @ absolute load/run zImage address
.word _edata @ zImage end address
1: mov r7, r1 @ save architecture ID MACH_TYPE_SMDK2410
mov r8, r2 @ save atags pointer 0x30000100;
__lookup_machine_type:
adr r3, 3b
ldmia r3, {r4, r5, r6}
sub r3, r3, r4 @ get offset between virt&phys
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space
1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
teq r3, r1 @ matches loader number?
beq 2f @ found
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
cmp r5, r6
blo 1b
mov r5, #0 @ unknown machine
2: mov pc, lr
//
DEFINE(MACHINFO_TYPE, offsetof(struct machine_desc, nr));
struct machine_desc {
unsigned int nr; /* architecture number */
……
}
MACHINE_START(SMDK2410, "SMDK2410")
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,// 传递的参数的保存地址,即为0x30000100
.map_io = smdk2410_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = smdk2410_init,
.timer = &s3c24xx_timer,
MACHINE_END
//
#define MACHINE_START(_type,_name) \//mach-smdk2410.c
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \ // MACH_TYPE_ SMDK2410
.name = _name,
#define MACHINE_END \
};
/
smdk2410 ARCH_SMDK2410 SMDK2410 193 //定义于mach-types,于u-boot中的数值对应
/
Setup_arch()
{
char *from = default_command_line;
……
tags = phys_to_virt(mdesc->boot_params);//从0x300000100取参数
……
if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
save_atags(tags);
parse_tags(tags);//此处解析,循环中会执行到 strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE),即为
//static int __init parse_tag_cmdline(const struct tag *tag)
//{
//strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
//return 0;
//}
}
……
memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
parse_cmdline(cmdline_p, from);
……
}
//
Start_kernel()
{//启动中与参数相关的函数
……
setup_arch(&command_line);
……
setup_command_line(command_line);
……
parse_early_param();
……
parse_args
}