Linux kernel version 4.14: kernel startup process of ARM64 - prepare_namespace() mounts the root file system

catalogue

How is the bootags parameter saved to the array?

__setup

do_early_param

obsolete_checksetup

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.

Added by bluntster on Mon, 22 Nov 2021 09:21:46 +0200