catalogue
How is the bootags parameter saved to the array?
Kernel version: 4.14
Simple analysis: how to mount the file system "root=/dev/mtdblock3"
init\do_ Prepare for mounts. C_ namespace().
/* * Prepare the namespace - decide what/where to mount, load ramdisks, etc. */ void __init prepare_namespace(void) { int is_floppy; if (root_delay) { printk(KERN_INFO "Waiting %d sec before mounting root device...\n", root_delay); ssleep(root_delay); } /* * wait for the known devices to complete their probing * * Note: this is a potential source of long boot delays. * For example, it is not atypical to wait 5 seconds here * for the touchpad of a laptop to initialize. */ wait_for_device_probe(); md_run_setup(); if (saved_root_name[0]) { //Judge saved_ root_ Is the name [0] array empty root_device_name = saved_root_name; if (!strncmp(root_device_name, "mtd", 3) || //Compare root_ device_ Is the name array mtd open or ubi open !strncmp(root_device_name, "ubi", 3)) { mount_block_root(root_device_name, root_mountflags); goto out; //If it is mtd, jump to out and mount directly } ROOT_DEV = name_to_dev_t(root_device_name); if (strncmp(root_device_name, "/dev/", 5) == 0) //Is the comparison started with / dev / root_device_name += 5; //If yes, + 5 finds the beginning of mtd } if (initrd_load()) goto out; /* wait for any asynchronous scanning to complete */ if ((ROOT_DEV == 0) && root_wait) { printk(KERN_INFO "Waiting for root device %s...\n", saved_root_name); while (driver_probe_done() != 0 || (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0) msleep(5); async_synchronize_full(); } is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; if (is_floppy && rd_doload && rd_load_disk(0)) ROOT_DEV = Root_RAM0; mount_root(); //Mount the actual file system to the "/ dev/root" directory of rootfs out: devtmpfs_mount("dev"); sys_mount(".", "/", NULL, MS_MOVE, NULL); sys_chroot("."); }
From the above code, saved_ root_ The name array can be obtained from the name. It is used to save the name of the root file system "/ dev/mtdblock3".
How is the bootags parameter saved to the array?
By searching "saved"_ root_ Name ", find the following code init\do_mounts.c.
static int __init root_dev_setup(char *line) { strlcpy(saved_root_name, line, sizeof(saved_root_name)); return 1; } __setup("root=", root_dev_setup);
Where root_ dev_ The setup () function is used to copy all the data in the line array to saved_root_name array. __ setup("root=", root_dev_setup); There is "root =" in the command line. Guess, this estimate is used to match the string starting with "root =" in the command line, and then put "/ dev/mtdblock3" in "root=/dev/mtdblock3" in saved_root_name array.
__setup
Next, analyze the above__ setup("root=", root_dev_setup) macro definition, \ include\linux\init.h.
#define __ setup_ Param (STR, unique_, ID, FN, early) \ / / definition__ setup_param(str, unique_id, fn, early) /*Define string array__ setup_str_##unique_id[]=str; \ Indicates that it is still in define */ static char __setup_str_##unique_id[] __initdata = str; \ // Equivalent to:__ setup_str_ root_dev_setup[]="root=" /*Define structure obs_kernel_param type__ setup_##unique_id*/ static struct obs_kernel_param __setup_##unique_id\ __attribute_used__ \ __attribute__((__section__(".init.setup"))) \ //Set the. init.setup section __attribute__((aligned((sizeof(long))))) \ = { __setup_str_##unique_id, fn, early} / / set "root=",root_dev_setup,0 is placed in the. init.setup section #define __setup(str, fn) \ / / definition__ setup(str, fn) uses__ setup_param(str, fn, fn, 0) __setup_param(str, fn, fn, 0)
Finally__ setup("root=", root_dev_setup) macro = {_setup_str_root_dev_setup [], root_dev_setup, 0};
Three members are stored in the. init.setup section. The first member is a string array equal to "root =" ", the second member is a function, and the third member early=0;
The. Init.setup section is used in vmlinux.lds (. The init.setup section is used to store special contents, such as command line parameters)
#define INIT_SETUP(initsetup_align) \ . = ALIGN(initsetup_align); \ VMLINUX_SYMBOL(__setup_start) = .; \ KEEP(*(.init.setup)) \ VMLINUX_SYMBOL(__setup_end) = .;
Next, analyze the macro__ setup("root=", root_dev_setup); And how it was called. Due to macro__ setup("root=", root_dev_setup);” Finally, it is stored in the. init.setup section, so search first "__ setup_start ", found in init/main.c_ early_ Param function and obsolete_ It is used by the checksetup function.
do_early_param
Let's analyze do first_ early_ Param function, first let's see who called it. Search do_early_param, find it parsed_ early_ Param() function call, as shown in the following figure:
/* Check for early params. */ static int __init do_early_param(char *param, char *val, const char *unused, void *arg) { const struct obs_kernel_param *p; for (p = __setup_start; p < __setup_end; p++) { 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; } void __init parse_early_options(char *cmdline) { parse_args("early options", cmdline, NULL, 0, 0, 0, NULL, do_early_param); } /* Arch code calls this early on, or if not, just before other parsing. */ 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); parse_early_options(tmp_cmdline); done = 1; }
Then search parse_early_param(), found it at start_ Used in the kernel function, as shown in the following figure:
asmlinkage __visible void __init start_kernel(void) { ....... pr_notice("Kernel command line: %s\n", boot_command_line); /* parameters may set static keys */ jump_label_init(); parse_early_param(); after_dashes = parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, -1, -1, NULL, &unknown_bootoption); if (!IS_ERR_OR_NULL(after_dashes)) parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, NULL, set_init_arg); ................. }
Get: start in the kernel_ This do is handled in the kernel ()_ early_ Param function
Next, analyze do_early_param source code
/* Check for early params. */ static int __init do_early_param(char *param, char *val, const char *unused, void *arg) { const struct obs_kernel_param *p; //Define obs_kernel_param structure pointer * p for (p = __setup_start; p < __setup_end; p++) { //Find the contents of the. init.setup section if ((p->early && parameq(param, p->str)) || (strcmp(param, "console") == 0 && strcmp(p->str, "earlycon") == 0) ) { if (p->setup_func(val) != 0) //Function handling early non-0 pr_warn("Malformed early option '%s'\n", param); } } /* We accept everything at this stage. */ return 0; }
Above OBS_ kernel_ The structure of param is defined as follows, which just corresponds to.
struct obs_kernel_param { const char *str; //__setup_str_ root_dev_setup[]="root=" int (*setup_func)(char *); // root_dev_setup(char *line) int early; // early=0 };
__ setup("root=", root_dev_setup) macro = {_setup_str_root_dev_setup [], root_dev_setup, 3 members in 0}. Because__ The early of setup ("root =", root_dev_setup ") is equal to 0, so if (P - > early & & StrCmp (param, P - > STR) = = 0) will never hold. So start strat in the kernel_ The kernel() function will pass do_ early_ The param function is a function that handles cases where early is not 0.
obsolete_checksetup
Then analyze obsolete_ For the checksetup function, first let's see who called it.
start_kernel->unknown_bootoption->obsolete_checksetup
Get: start in the kernel_ This obsolete is handled in the kernel ()_ Checksetup function
static bool __init obsolete_checksetup(char *line) { const struct obs_kernel_param *p; //Define obs_kernel_param structure pointer bool had_early_param = false; p = __setup_start; do { int n = strlen(p->str); if (parameqn(line, p->str, n)) { //Determine if there is content if (p->early) { //If early is not 0, the function is not executed /* 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 = true; } else if (!p->setup_func) { // Handle functions with early 0 pr_warn("Parameter %s is obsolete, ignored\n", p->str); return true; } else if (p->setup_func(line + n)) //Handle functions with early 0 return true; } p++; } while (p < __setup_end); //From__ setup_start to__ setup_end lookup return had_early_param; }
Through the above code analysis:
__ setup("root=", root_dev_setup) macro = {_setup_str_root_dev_setup [], the third member in root_dev_setup, 0}, early=0, will execute root_ dev_ The setup () function, and then copy the file system directory to the global variable saved_ root_ In the name [] array, use the following functions to mount the file system
So start strat in the kernel_ In the kernel() function, obsolete is used_ The checksetup function handles functions with an early of 0.