Bootloader 将参数放在某个约定的地方之后,再启动内核,内核启动后从这个地方获得参数。
除了约定好参数存放的地址外,还要规定参数的结构。Linux 2.4.x 以后的内核都期望以
标记列表(tagged list)的形式来传递启动参数。标记,就是一种数据结构;标记列表,就是
挨着存放的多个标记。
标记列表以标记ATAG_CORE 开始,以标记ATAG_NONE 结束。
标记的数据结构为tag,它由一个tag_header 结构和一个联合(union)组成。
tag_header结构表示标记的类型及长度,比如是表示内存还是表示命令行参数等。
对于不同类型的标记使用不同的联合(union),比如表示内存时使用tag_mem32,表示命令行时使用tag_cmdline。
数据结构tag 和tag_header 定义在Linux 内核源码的uboot/include/asm-arm/setup.h头文件中
参考嵌入式Linux应用开发完全手册ch15.1 p243
uboot/include/asm-arm/setup.h标记列表类型定义
/* * linux/include/asm/setup.h * * Copyright (C) 1997-1999 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Structure passed to kernel to tell it about the * hardware it's running on. See linux/Documentation/arm/Setup * for more info. * * NOTE: * This file contains two ways to pass information from the boot * loader to the kernel. The old struct param_struct is deprecated, * but it will be kept in the kernel for 5 years from now * (2001). This will allow boot loaders to convert to the new struct * tag way. */ #ifndef __ASMARM_SETUP_H #define __ASMARM_SETUP_H /* * Usage: * - do not go blindly adding fields, add them at the end * - when adding fields, don't rely on the address until * a patch from me has been released * - unused fields should be zero (for future expansion) * - this structure is relatively short-lived - only * guaranteed to contain useful data in setup_arch() */ #define COMMAND_LINE_SIZE 1024 /* This is the old deprecated way to pass parameters to the kernel */ struct param_struct { union { struct { unsigned long page_size; /* 0 */ unsigned long nr_pages; /* 4 */ unsigned long ramdisk_size; /* 8 */ unsigned long flags; /* 12 */ #define FLAG_READONLY 1 #define FLAG_RDLOAD 4 #define FLAG_RDPROMPT 8 unsigned long rootdev; /* 16 */ unsigned long video_num_cols; /* 20 */ unsigned long video_num_rows; /* 24 */ unsigned long video_x; /* 28 */ unsigned long video_y; /* 32 */ unsigned long memc_control_reg; /* 36 */ unsigned char sounddefault; /* 40 */ unsigned char adfsdrives; /* 41 */ unsigned char bytes_per_char_h; /* 42 */ unsigned char bytes_per_char_v; /* 43 */ unsigned long pages_in_bank[4]; /* 44 */ unsigned long pages_in_vram; /* 60 */ unsigned long initrd_start; /* 64 */ unsigned long initrd_size; /* 68 */ unsigned long rd_start; /* 72 */ unsigned long system_rev; /* 76 */ unsigned long system_serial_low; /* 80 */ unsigned long system_serial_high; /* 84 */ unsigned long mem_fclk_21285; /* 88 */ } s; char unused[256]; } u1; union { char paths[8][128]; struct { unsigned long magic; char n[1024 - sizeof(unsigned long)]; } s; } u2; char commandline[COMMAND_LINE_SIZE]; }; /* * The new way of passing information: a list of tagged entries */ /* The list ends with an ATAG_NONE node. */ #define ATAG_NONE 0x00000000 struct tag_header { u32 size; u32 tag; }; /* The list must start with an ATAG_CORE node */ #define ATAG_CORE 0x54410001 struct tag_core { u32 flags; /* bit 0 = read-only */ u32 pagesize; u32 rootdev; }; /* it is allowed to have multiple ATAG_MEM nodes */ #define ATAG_MEM 0x54410002 struct tag_mem32 { u32 size; u32 start; /* physical start address */ }; /* VGA text type displays */ #define ATAG_VIDEOTEXT 0x54410003 struct tag_videotext { u8 x; u8 y; u16 video_page; u8 video_mode; u8 video_cols; u16 video_ega_bx; u8 video_lines; u8 video_isvga; u16 video_points; }; /* describes how the ramdisk will be used in kernel */ #define ATAG_RAMDISK 0x54410004 struct tag_ramdisk { u32 flags; /* bit 0 = load, bit 1 = prompt */ u32 size; /* decompressed ramdisk size in _kilo_ bytes */ u32 start; /* starting block of floppy-based RAM disk image */ }; /* describes where the compressed ramdisk image lives (virtual address) */ /* * this one accidentally used virtual addresses - as such, * its depreciated. */ #define ATAG_INITRD 0x54410005 /* describes where the compressed ramdisk image lives (physical address) */ #define ATAG_INITRD2 0x54420005 struct tag_initrd { u32 start; /* physical start address */ u32 size; /* size of compressed ramdisk image in bytes */ }; /* board serial number. "64 bits should be enough for everybody" */ #define ATAG_SERIAL 0x54410006 struct tag_serialnr { u32 low; u32 high; }; /* board revision */ #define ATAG_REVISION 0x54410007 struct tag_revision { u32 rev; }; /* initial values for vesafb-type framebuffers. see struct screen_info * in include/linux/tty.h */ #define ATAG_VIDEOLFB 0x54410008 struct tag_videolfb { u16 lfb_width; u16 lfb_height; u16 lfb_depth; u16 lfb_linelength; u32 lfb_base; u32 lfb_size; u8 red_size; u8 red_pos; u8 green_size; u8 green_pos; u8 blue_size; u8 blue_pos; u8 rsvd_size; u8 rsvd_pos; }; /* command line: \0 terminated string */ #define ATAG_CMDLINE 0x54410009 struct tag_cmdline { char cmdline[1]; /* this is the minimum size */ }; /* acorn RiscPC specific information */ #define ATAG_ACORN 0x41000101 struct tag_acorn { u32 memc_control_reg; u32 vram_pages; u8 sounddefault; u8 adfsdrives; }; /* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */ #define ATAG_MEMCLK 0x41000402 struct tag_memclk { u32 fmemclk; }; struct tag { struct tag_header hdr; union { struct tag_core core;//标记起始 struct tag_mem32 mem;//内存标记,告诉内核内存的分布 struct tag_videotext videotext;// struct tag_ramdisk ramdisk; struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; struct tag_videolfb videolfb; struct tag_cmdline cmdline;//命令标记,一个字符串,告诉内核要做什么,即在uboot命令中设置的环境变量bootargs //"console=ttySAC0 noinitrd root=/dev/mtdblock3 init=/linuxrc" /* * Acorn specific */ struct tag_acorn acorn; /* * DC21285 specific */ struct tag_memclk memclk; } u; }; struct tagtable { u32 tag; int (*parse)(const struct tag *); }; #define __tag __attribute__((unused, __section__(".taglist"))) #define __tagtable(tag, fn) \ static struct tagtable __tagtable_##fn __tag = { tag, fn } #define tag_member_present(tag,member) \ ((unsigned long)(&((struct tag *)0L)->member + 1) \ <= (tag)->hdr.size * 4) #define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size)) #define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2) #define for_each_tag(t,base) \ for (t = base; t->hdr.size; t = tag_next(t)) /* * Memory map description */ #define NR_BANKS 8 struct meminfo { int nr_banks; unsigned long end; struct { unsigned long start; unsigned long size; int node; } bank[NR_BANKS]; }; extern struct meminfo meminfo; #endif
uboot/lib_arm/bootm.c 标记列表
/* * (C) Copyright 2002 * Sysgo Real-Time Solutions, GmbH <www.elinos.com> * Marius Groeger <mgroeger@sysgo.de> * * Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include <common.h> #include <command.h> #include <image.h> #include <u-boot/zlib.h> #include <asm/byteorder.h> DECLARE_GLOBAL_DATA_PTR;/**获取全局结构体指针gd***gd_t gd,此宏在uboot/include/asm-arm/global_data.h定义,参考blog.csdn.net/songqqnew/article/details/6847699**/ #if defined (CONFIG_SETUP_MEMORY_TAGS) || \ defined (CONFIG_CMDLINE_TAG) || \ defined (CONFIG_INITRD_TAG) || \ defined (CONFIG_SERIAL_TAG) || \ defined (CONFIG_REVISION_TAG) || \ defined (CONFIG_VFD) || \ defined (CONFIG_LCD) static void setup_start_tag (bd_t *bd); # ifdef CONFIG_SETUP_MEMORY_TAGS static void setup_memory_tags (bd_t *bd); # endif static void setup_commandline_tag (bd_t *bd, char *commandline); # ifdef CONFIG_INITRD_TAG static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulong initrd_end); # endif static void setup_end_tag (bd_t *bd); # if defined (CONFIG_VFD) || defined (CONFIG_LCD) static void setup_videolfb_tag (gd_t *gd); # endif static struct tag *params;/***************定义tag***************************************/ #endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */ int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) { bd_t *bd = gd->bd;/***********************获取bd*************************************/ char *s; int machid = bd->bi_arch_number; void (*theKernel)(int zero, int arch, uint params); #ifdef CONFIG_CMDLINE_TAG char *commandline = getenv ("bootargs"); #endif if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) return 1; theKernel = (void (*)(int, int, uint))images->ep; s = getenv ("machid"); if (s) { machid = simple_strtoul (s, NULL, 16); printf ("Using machid 0x%x from environment\n", machid); } show_boot_progress (15); debug ("## Transferring control to Linux (at address %08lx) ...\n", (ulong) theKernel); #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); #ifdef CONFIG_SERIAL_TAG setup_serial_tag (¶ms); #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); #endif #ifdef CONFIG_INITRD_TAG if (images->rd_start && images->rd_end) setup_initrd_tag (bd, images->rd_start, images->rd_end); #endif #if defined (CONFIG_VFD) || defined (CONFIG_LCD) setup_videolfb_tag ((gd_t *) gd); #endif setup_end_tag (bd); #endif /* we assume that the kernel is in place */ printf ("\nStarting kernel ...\n\n"); //#ifdef CONFIG_USB_DEVICE #if 0 { extern void udc_disconnect (void); udc_disconnect (); } #endif cleanup_before_linux (); theKernel (0, machid, bd->bi_boot_params); /* does not return */ return 1; } #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) static void setup_start_tag (bd_t *bd) { params = (struct tag *) bd->bi_boot_params;/*bi_boot_param=30000100,在uboot/board/mini2440/mini2440.c中指定***param指向参数区首地址,存放参数**/ 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); } #ifdef CONFIG_SETUP_MEMORY_TAGS static void setup_memory_tags (bd_t *bd) { int i; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { params->hdr.tag = ATAG_MEM;//标记类型:内存标记,hdr是struct tag_header类型结构体,为tag一成员 params->hdr.size = tag_size (tag_mem32);//标记大小 params->u.mem.start = bd->bi_dram[i].start;//内存起始地址,u是union类型,为tag一成员 params->u.mem.size = bd->bi_dram[i].size;//内存结束地址 params = tag_next (params); } } #endif /* CONFIG_SETUP_MEMORY_TAGS */ 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;//标记类型:命令字符串 params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; strcpy (params->u.cmdline.cmdline, p); params = tag_next (params); } #ifdef CONFIG_INITRD_TAG static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulong initrd_end) { /* an ATAG_INITRD node tells the kernel where the compressed * ramdisk can be found. ATAG_RDIMG is a better name, actually. */ params->hdr.tag = ATAG_INITRD2; params->hdr.size = tag_size (tag_initrd); params->u.initrd.start = initrd_start; params->u.initrd.size = initrd_end - initrd_start; params = tag_next (params); } #endif /* CONFIG_INITRD_TAG */ #if defined (CONFIG_VFD) || defined (CONFIG_LCD) extern ulong calc_fbsize (void); static void setup_videolfb_tag (gd_t *gd) { /* An ATAG_VIDEOLFB node tells the kernel where and how large * the framebuffer for video was allocated (among other things). * Note that a _physical_ address is passed ! * * We only use it to pass the address and size, the other entries * in the tag_videolfb are not of interest. */ params->hdr.tag = ATAG_VIDEOLFB; params->hdr.size = tag_size (tag_videolfb); params->u.videolfb.lfb_base = (u32) gd->fb_base; /* Fb size is calculated according to parameters for our panel */ params->u.videolfb.lfb_size = calc_fbsize(); params = tag_next (params); } #endif /* CONFIG_VFD || CONFIG_LCD */ #ifdef CONFIG_SERIAL_TAG void setup_serial_tag (struct tag **tmp) { struct tag *params = *tmp; struct tag_serialnr serialnr; void get_board_serial(struct tag_serialnr *serialnr); get_board_serial(&serialnr); params->hdr.tag = ATAG_SERIAL; params->hdr.size = tag_size (tag_serialnr); params->u.serialnr.low = serialnr.low; params->u.serialnr.high= serialnr.high; params = tag_next (params); *tmp = params; } #endif #ifdef CONFIG_REVISION_TAG void setup_revision_tag(struct tag **in_params) { u32 rev = 0; u32 get_board_rev(void); rev = get_board_rev(); params->hdr.tag = ATAG_REVISION; params->hdr.size = tag_size (tag_revision); params->u.revision.rev = rev; params = tag_next (params); } #endif /* CONFIG_REVISION_TAG */ static void setup_end_tag (bd_t *bd) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; } #endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */
2012-12-22 22:19:53 添加
内核在启动的时候会从内存中取得标记列表,使用setup_arch
asmlinkage void __init start_kernel(void) { char * command_line; extern const struct kernel_param __start___param[], __stop___param[]; smp_setup_processor_id(); /* * Need to run as early as possible, to initialize the * lockdep hash: */ lockdep_init(); debug_objects_early_init(); /* * Set up the the initial canary ASAP: */ boot_init_stack_canary(); cgroup_init_early(); local_irq_disable(); early_boot_irqs_disabled = true; /* * Interrupts are still disabled. Do necessary setups, then * enable them */ tick_init(); boot_cpu_init(); page_address_init(); printk(KERN_NOTICE "%s", linux_banner); setup_arch(&command_line); ... }
setup_arch---------->squash_mem_tags(tags);
static void __init squash_mem_tags(struct tag *tag) { for (; tag->hdr.size; tag = tag_next(tag)) if (tag->hdr.tag == ATAG_MEM) tag->hdr.tag = ATAG_NONE; }
command_line就是这个时候建立的。内核中也有一个default_command_line,是在内核 Boot options ---> 中定义的。如果在uboot没有传递command_line tag,内核就使用default_command_line。从squash_mem_tags函数可以看出来(uboot没传递的话,在tag list里面就找不到tag_cmdline)