分类: LINUX
2016-01-28 10:21:29
--/kernel/printk/printk.c ...... __setup("console=", console_setup); ...... |
--/include/linux/init.h #define __setup_param(str, unique_id, fn, early) \ static const char __setup_str_##unique_id[] __initconst \ __aligned(1) = str; \ static struct obs_kernel_param __setup_##unique_id \ __used __section(.init.setup) \ __attribute__((aligned((sizeof(long))))) \ = { __setup_str_##unique_id, fn, early } #define __setup(str, fn) \ __setup_param(str, fn, fn, 0) struct obs_kernel_param { #NOTE:该变量定义在/include/linux/init.h中 const char *str; int (*setup_func)(char *); int early; #NOTE:此字段用于控制命令行参数的初始化等级 }; |
__setup("console=", console_setup) --> (NOTE:此次变换,直接替换参数即可) _setup_param("console=", console_setup, console_setup, 0) --> (NOTE:此次变换,替换后定义了两个变量: 一个字符数组__setup_str_console_setup,并初始化其为"console="; 一个struct obs_kernel_param对象__setup_console_setup,并使用__setup_str_console_setup, console_setup, 0分别对其成员进行初始化。 ) static const char __setup_str_console_setup[] __initconst \ __aligned(1) = "console="; \ static struct obs_kernel_param __setup_console_setup \ __used __section(.init.setup) \ __attribute__((aligned((sizeof(long))))) \ = { __setup_str_console_setup, console_setup, 0 } (#NOTE:此处引用了上面定义的字符数组变量) (#NOTE:上述替换过程中出现了一些编译器选项,下面一一进行解答: __initconst: 编译器处理选项,主要用于控制链接器链接时,程序的代码段布局,其定义在include/linux/init.h,其内容如下: #define __init __section(.init.text) __cold notrace #define __initdata __section(.init.data) #define __initconst __constsection(.init.rodata) #NOTE:该选项表明作用对象链接到程序的.init.rodata段中 #define __exitdata __section(.exit.data) #define __exit_call __used __section(.exitcall.exit) __aligned(1): 存储类修饰符,主要用于控制字节对其,这里使用1字节对其,与char数组常规对其一致,因此此处没有什么实际作用,只是为了安全考虑。 __used: 变量或函数属性,当未被使用时编辑器将提供警告信息,作用与__unused相反。 __section(.init.setup) :指定代码段为.init.setup代码段,此处即为该参数处理的入口。这里我们看看链接脚本arch/mips/kernel/vmlinux.lds,此段是如何链接的: __setup_start = .; KEEP(*(.init.setup)) __setup_end = .; __attribute__((aligned((sizeof(long))))) :指定对其长度为sizeof(long) ) |
--/init/main.c char __initdata boot_command_line[COMMAND_LINE_SIZE]; asmlinkage __visible void __init start_kernel(void) { char *command_line; ...... setup_arch(&command_line); ...... mangle_bootargs(command_line); setup_command_line(command_line); ...... pr_notice("Kernel command line: %s\n", boot_command_line); #NOTE:启动时命令行参数的打印信息出自此处 ...... } |
--/arch/mips/kernel/setup.c static char __initdata command_line[COMMAND_LINE_SIZE]; char __initdata arcs_cmdline[COMMAND_LINE_SIZE]; #ifdef CONFIG_CMDLINE_BOOL static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE; #endif void __init setup_arch(char **cmdline_p) { ...... prom_init(); #NOTE:此处初始化prom,对9531而言,调用arch/mips/ath79/prom.c中的prom_init()函数 ...... arch_mem_init(cmdline_p); ...... } static void __init arch_mem_init(char **cmdline_p) { ...... (#NOTE:下述宏定义来自于内核的.config,位于kernel_menuconfig的如下位置: Kernel hacking--> [*]Built-in kernel command line --> (CONFIG_CMDLINE_BOOL) (rootfstype=squashfs,jffs2 noinitrd) Default kernel command string (CONFIG_CMDLINE) [ ] Built-in command line overrides firmware arguments (CONFIG_CMDLINE_OVERRIDE) ) #ifdef CONFIG_CMDLINE_BOOL #ifdef CONFIG_CMDLINE_OVERRIDE #NOTE:采用静态CMDLINE, 若定义了OVERRIDE配置时,直接用builtin_cmdline(CONFIG_CMDLINE)替换boot_command_line strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE); #else if (builtin_cmdline[0]) { #NOTE:采用静态CMDLINE,若未定义OVERRIDE配置时,用builtin_cmdline(CONFIG_CMDLINE)追加在arcs_cmdline之后, 然后替换boot_command_line,arcs_cmdline的首次初始化在setup_arch()函数中调用的prom_init()函数中进行, 详情参见"2.3 Openwrt patch-cmdline机制" strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE); strlcat(arcs_cmdline, builtin_cmdline, COMMAND_LINE_SIZE); } strlcpy(boot_command_line, arcs_cmdline, COMMAND_LINE_SIZE); #endif #else #NOTE:不采用静态CMDLINE,则直接用arcs_cmdline替换boot_command_line中的内容 strlcpy(boot_command_line, arcs_cmdline, COMMAND_LINE_SIZE); #endif strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); *cmdline_p = command_line; parse_early_param(); #NOTE:此处在平台中对解析了struct obs_kernel_param中early字段为1的对象 ...... } |
--/init/main.c static int __init obsolete_checksetup(char *line) { const struct obs_kernel_param *p; int had_early_param = 0; p = __setup_start; #NOTE:__setup_start定义在链接文件中,其为.init.setup段的首部。 do { int n = strlen(p->str); #NOTE: 取struct obs_kernel_param中的str的长度,如"console="。 if (parameqn(line, p->str, n)) { #NOTE: 匹配对应的str字段 if (p->early) { #NOTE: 标记此标记的参数,已经在函数parse_early_param()中处理。 /* Already done in parse_early_param? * (Needs exact match on param part). * Keep iterating, as we can have early * params and __setups of same names 8( */ if (line[n] == '\0' || line[n] == '=') had_early_param = 1; } else if (!p->setup_func) { #NOTE: 处理函数为空则忽略该参数 pr_warn("Parameter %s is obsolete, ignored\n", p->str); return 1; } else if (p->setup_func(line + n)) #NOTE:调用处理函数,并将参数的值传入,line+n指示的字符串即为命令行参数中对应参数=后面的值。 return 1; } p++; } while (p < __setup_end); #NOTE:__setup_end定义在链接文件中,其为.init.setup段的尾部。 return had_early_param; } |
--/init/main.c asmlinkage __visible void __init start_kernel(void) { ...... pr_notice("Kernel command line: %s\n", boot_command_line); #NOTE:启动时命令行参数的打印信息出自此处 parse_early_param(); #NOTE:第一次解析参数,解析struct obs_kernel_param中early字段为1的参数,该动作可能在setup_arch()函数中已经完成。 after_dashes = parse_args("Booting kernel", #NOTE:第二次解析参数,解析函数为unknown_bootoption static_command_line, __start___param, __stop___param - __start___param, -1, -1, &unknown_bootoption); ...... } 第一次参数解析: void __init parse_early_param(void) { static int done __initdata; static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata; if (done) return; /* All fall through to do_early_param. */ strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); #NOTE:拷贝boot_command_line,为解析early参数做准备 parse_early_options(tmp_cmdline); #NOTE:解析命令行参数 done = 1; } void __init parse_early_options(char *cmdline) { #NOTE:这里可以看出,第一次和第二次参数解析调用函数一致,只是携带的处理函数不一样,这里携带的是do_early_param()回调函数。 parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param); } /* Check for early params. */ static int __init do_early_param(char *param, char *val, const char *unused) { const struct obs_kernel_param *p; for (p = __setup_start; p < __setup_end; p++) { #NOTE:这里也是以.init.setup段中的__setup_start和__setup_end为搜索边界 if ((p->early && parameq(param, p->str)) || (strcmp(param, "console") == 0 && strcmp(p->str, "earlycon") == 0) ) { if (p->setup_func(val) != 0) pr_warn("Malformed early option '%s'\n", param); } } /* We accept everything at this stage. */ return 0; } |
--/kernel/params.c /* Args looks like "foo=bar,bar2 baz=fuz wiz". */ char *parse_args(const char *doing, char *args, const struct kernel_param *params, unsigned num, s16 min_level, s16 max_level, int (*unknown)(char *param, char *val, const char *doing)) { char *param, *val; /* Chew leading spaces */ args = skip_spaces(args); if (*args) pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args); while (*args) { int ret; int irq_was_disabled; args = next_arg(args, ?m, &val); #NOTE: 此处进行参数分割 /* Stop at -- */ if (!val && strcmp(param, "--") == 0) return args; irq_was_disabled = irqs_disabled(); ret = parse_one(param, val, doing, params, num, min_level, max_level, unknown); if (irq_was_disabled && !irqs_disabled()) pr_warn("%s: option '%s' enabled irq's!\n", doing, param); switch (ret) { case -ENOENT: pr_err("%s: Unknown parameter `%s'\n", doing, param); return ERR_PTR(ret); case -ENOSPC: pr_err("%s: `%s' too large for parameter `%s'\n", doing, val ?: "", param); return ERR_PTR(ret); case 0: break; default: pr_err("%s: `%s' invalid for parameter `%s'\n", doing, val ?: "", param); return ERR_PTR(ret); } } /* All parsed OK. */ return NULL; } (NOTE:此处以args 为"board=PIONEER-9531 console=ttyS0,115200 rootfstype=squashfs,jffs2 noinitrd"进行分析) static char *next_arg(char *args, char **param, char **val) { unsigned int i, equals = 0; int in_quote = 0, quoted = 0; char *next; if (*args == '"') { #NOTE:第一次调用此函数时,此条件不成立,*args='b' args++; in_quote = 1; quoted = 1; } for (i = 0; args[i]; i++) { if (isspace(args[i]) && !in_quote) #NOTE:第一次调用时in_quote为0,直接跳出 break; if (equals == 0) { if (args[i] == '=') equals = i; } if (args[i] == '"') in_quote = !in_quote; } *param = args; if (!equals) #NOTE:第一次调用时,变量equals为0,满足条件 *val = NULL; else { args[equals] = '\0'; *val = args + equals + 1; /* Don't include quotes in value. */ if (**val == '"') { (*val)++; if (args[i-1] == '"') args[i-1] = '\0'; } } if (quoted && args[i-1] == '"') #NOTE:第一次调用时,变量quoted为0,不满足条件 args[i-1] = '\0'; if (args[i]) { args[i] = '\0'; next = args + i + 1; } else next = args + i; /* Chew up trailing spaces. */ return skip_spaces(next); } |
staging_dir/host/bin/patch-cmdline build_dir/target-mips_34kc_musl-1.1.11/linux-ar71xx_generic/pioneer-qca9531-64m-16m-kernel.bin 'board=PIONEER-9531 console=ttyATH0,115200' |
tools/patch-image/src/patch-cmdline.c int main() { ...... if (((fd = open(argv[1], O_RDWR)) < 0) || (ptr = (char *) mmap(0, SEARCH_SPACE + CMDLINE_MAX, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == (void *) (-1)) { #NOTE:此处利用mmap将固件内容映射出来 fprintf(stderr, "Could not open kernel image"); goto err2; } for (p = ptr; p < (ptr + SEARCH_SPACE); p += 4) { if (memcmp(p, "CMDLINE:", 8) == 0) { #NOTE:此处匹配head.s中设置的标记字符CMDLINE found = 1; p += 8; break; } } if (!found) { fprintf(stderr, "Command line marker not found!\n"); goto err3; } memset(p, 0, CMDLINE_MAX - 8); #NOTE:下述利用调用工具时传入的参数替换固件中的bootargs strcpy(p, argv[2]); msync(p, CMDLINE_MAX, MS_SYNC|MS_INVALIDATE); ret = 0; ...... } |
--arch/mips/kernel/head.S #ifdef CONFIG_IMAGE_CMDLINE_HACK .ascii "CMDLINE:" #NOTE: 定义一个"CMDLINE:" ascii标记字符串,注意没有使用asciiz,末尾没有\0 EXPORT(__image_cmdline) #NOTE: 到处一个符号标识,这里可以看成是后面1024字节数组的数组名称 .fill 0x400 #NOTE: 在字符串标记后,开辟1024B的空间,用于存放bootargs #endif /* CONFIG_IMAGE_CMDLINE_HACK */ |
--arch/mips/ath79/prom.c void __init prom_init(void) { const char *env; if (ath79_prom_init_myloader()) return; if (!ath79_use_image_cmdline()) fw_init_cmdline(); ...... } #ifdef CONFIG_IMAGE_CMDLINE_HACK #NOTE:这里是CMDLINE_HACK的配置开关 extern char __image_cmdline[]; static int __init ath79_use_image_cmdline(void) { char *p = __image_cmdline; #NOTE:指向head.s中的1024字节的bootargs区域. int replace = 0; if (*p == '-') { #NOTE: 用patch-cmdline工具打入的bootargs,若以-开头,表示强制覆盖,否则按追加处理. replace = 1; p++; } if (*p == '\0') #NOTE: 若bootargs中的字符串开头即为\0,则立即返回 return 0; if (replace) { #NOTE: 根据上述bootargs是否前缀-来控制是替换还是追加到arcs_cmdline strlcpy(arcs_cmdline, p, sizeof(arcs_cmdline)); } else { strlcat(arcs_cmdline, " ", sizeof(arcs_cmdline)); strlcat(arcs_cmdline, p, sizeof(arcs_cmdline)); } /* Validate and setup environment pointer */ if (fw_arg2 < CKSEG0) #NOTE:重新校正环境变量指针 _fw_envp = NULL; else _fw_envp = (int *)fw_arg2; return 1; } #else static inline int ath79_use_image_cmdline(void) { return 0; } #endif |