I. macro definition in/include/linux/Init. [cpp] # define _ setup (str, fn) \ _ setup_param (str, fn, fn, 0) [cpp] # define early_param (str, fn) \ _ setup_param (str, fn, fn, 1) Both macros call the _ setup_param trace into _ setup_param macro definition [cpp] # 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 _ se Ction (. init. setup) \ _ attribute _ (aligned (sizeof (long) \ = {_ setup_str _ # unique_id, fn, early} This macro contains a struct obs_kernel_param [cpp] struct obs_kernel_param {const char * str; int (* setup_func) (char *); int early ;}; combine the two macros and a struct to expand _ setup (str, fn) macro defines a static const char _ setup_str_fn [] variable = str, then defines a static struct obs_kernel_param _ setup_fn struct, and assigns values (marked as compiled. init. setup section) {str; fn (char *); 0, Or 1} 2. the role of macros 1. compilation is related to/include/asm-generic/Vmlinux. lds. the H file defines _ setup_start ..... _ setup_end segment [cpp] # define INIT_SETUP (initsetup_align )\. = ALIGN (initsetup_align); \ VMLINUX_SYMBOL (_ setup_start) = .; \*(. init. setup) \ VMLINUX_SYMBOL (_ setup_end) = .; marked. init. the setup function will be compiled into this section 2. the call relationship of kernel startup is called in start_kernel parse_early_param () [cpp] void _ init parse_early_param (void) {static _ initdata int done = 0; stati C _ initdata char tmp_cmdline [COMMAND_LINE_SIZE]; if (done) return; strlcpy (tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE); // copy the startup command line data parse_early_options (tmp_cmdline ); // call the parse_early_options function done = 1;} parse_early_options function [cpp] void _ init parse_early_options (char * cmdline) {parse_args ("early options", limit line, NULL, 0, do_early_param);} Then call the parse_args function [cpp] int parse_args (const char * Name, char * args, const struct kernel_param * params, unsigned num, int (* unknown) (char * param, char * val) {char * param, * val; DEBUGP ("Parsing ARGS: % s \ n", args); args = skip_spaces (args); while (* args) {// traverse the startup command line int ret; int irq_was_disabled; args = next_arg (args, & param, & val); // obtain the next parameter and fill in the param and val parameters (for example, param -- console; val -- tty2, 115200n8) irq_was_disabled = irqs_disabled (); ret = parse_one (para M, val, params, num, unknown); // parse a command line parameter if (irq_was_disabled &&! Irqs_disabled () {printk (KERN_WARNING "parse_args (): option '% s' enabled" "irq's! \ N ", param);} switch (ret) {case-ENOENT: printk (KERN_ERR" % s: Unknown parameter '% s' \ n ", name, param ); return ret; case-ENOSPC: printk (KERN_ERR "% s: '% s' too large for parameter' % s' \ n", name, val?: "", Param); return ret; case 0: break; default: printk (KERN_ERR "% s: '% s' invalid for parameter' % s' \ n ", name, val?: "", Param); return ret ;}/ * All parsed OK. */return 0;} parsing of command line parameters parse_one [cpp] static int parse_one (char * param, char * val, const struct kernel_param * params, unsigned num_params, int (* handle_unknown) (char * param, char * val) {unsigned int I; int err;/* Find parameter */for (I = 0; I <num_params; I ++) {// num_params = 0 if (parameq (param, params [I]. name) {if (! Val & params [I]. ops-> set! = Param_set_bool) return-EINVAL; DEBUGP ("They are equal! Calling % p \ n ", params [I]. ops-> set); mutex_lock (& param_lock); err = params [I]. ops-> set (val, & params [I]); mutex_unlock (& param_lock); return err ;}} if (handle_unknown) {// If the handle_unknown function has DEBUGP ("Unknown argument: calling % p \ n", handle_unknown); return handle_unknown (param, val); // call the handle_unknown function, the parameter is param, val} DEBUGP ("Unknown argument '% s' \ n", param); return-ENOENT;} Go Back To The handle_unknow Function Is do_early_param [cpp] static int _ init do_early_param (char * param, char * val) {const struct obs_kernel_param * p; for (p = _ setup_start; p <_ setup_end; p ++) {if (p-> early & strcmp (param, p-> str) = 0) | (strcmp (param, "console") = 0 & strcmp (p-> str, "earlycon") = 0) {if (p-> setup_func (val )! = 0) printk (KERN_WARNING "Malformed early option '% s' \ n", param) ;}/ * We accept everything at this stage. */return 0;} The do_early_param function traverses from _ setup_start to _ setup_end, judges the parameter, and enters the if function body if (p-> setup_func (val )! = 0) This statement calls the corresponding setup_func or early_param function, and uses val as its parameter. val is actually _ setup (str, fn) or the str in _ early_param is actually the first if in fn (str), which will be replaced by the _ setup definition (except for the console and earlycon parameters ), because the _ setup-defined obs_kernel_param struct p-> early = 0 _ setup-defined fn will be in start_kernel-> parse_args ("Booting kernel", static_command_line, _ start ___ param, __stop ___ param-_ start ___ param, & unknown_bootoption); The unknown_bootoption-> obsolete_checksetup function shows the Call Sequence in start_kernel [cpp] parse_early_param (); parse_args ("Booting kernel", static_command_line, _ start ___ param, _ stop ___ param-_ start ___ param, & unknown_bootoption ); it can be seen that the parsing parameter functions defined by _ early_param and the parameter parsing functions defined by _ setup (console and earlycon) are called before other parsing parameter functions defined by _ setup