__setup macro. This macro expects two arguments; in this case, it is passed a string literal and a function pointer. It is no coincidence that the string literal passed to the __setup macro is the same as the first eight characters of the kernel command line related to the console: console=.
Listing 5-4. Console Setup Code Snippet
/*
* Setup a list of consoles. Called from init/main.c
*/
static int __init console_setup(char *str)
{
char name[sizeof(console_cmdline[0].name)];
char*s, *options;
int idx;
/*
* Decode str into name, index, options.
*/
return 1;
}
__setup('console=', console_setup);
You can think of this macro as a registration function for the kernel command-line console parameter. In effect, it says: When the console= string is encountered on the kernel command line, invoke the function represented by the second __setup macro argumentin this case, the console_setup() function. But how is this information communicated to the early setup code, outside this module, which has no knowledge of the console functions? The mechanism is both clever and somewhat complicated, and relies on lists built by the linker.
The details are hidden in a set of macros designed to conceal the syntactical tedium of adding
Let's now examine how this is done for the __setup macro case. Listing 5-5 is a portion of code from the header file .../include/linux/init.h defining the __setup family of macros.
Listing 5-5. Family of __setup Macro Definitions from init.h
...
#define __setup_param(str, unique_id, fn, early)
static char __setup_str_##unique_id[] __initdata = str;
static struct obs_kernel_param __setup_##unique_id
__attribute_used__
__attribute__((__section__('.init.setup')))
__attribute__((aligned((sizeof(long)))))
= { __setup_str_##unique_id, fn, early }
#define __setup_null_param(str, unique_id)
__setup_param(str, unique_id, NULL, 0)
#define __setup(str, fn
__setup_param(str, fn, fn, 0)
...
Listing 5-5 is the author's definition of syntactical tedium! Recall from Listing 5-4 that our invocation of the original __setup macro looked like this:
__setup('console=', console_setup);
With some slight simplification, here is what the compiler's preprocessor produces after macro expansion:
static char __setup_str_console_setup[] __initdata = 'console=';
static struct obs_kernel_param __setup_console_setup
__attribute__((__section__('.init.setup'))) =
{__setup_str_console_setup, console_setup, 0};
To make this more readable, we have split the second and third lines, as indicated by the UNIX line- continuation character .
We have intentionally left out two compiler attributes whose description does not add any insight to this discussion. Briefly, the __attribute_used__ (itself a macro hiding further syntactical tedium) tells the compiler to emit the function or variable, even if the optimizer determines that it is unused. [43] The __attribute__ (aligned) tells the compiler to align the structures on a specific boundary, in this case sizeof(long).
What we have left after simplification is the heart of the mechanism. First, the compiler generates an array of characters called __setup_str_console_ setup[] initialized to contain the string console= . Next, the compiler generates a structure that contains three members: a pointer to the kernel command line string (the array just declared), the pointer to the setup function itself, and a simple flag. The key to the magic here is the section attribute attached to the structure. This attribute instructs the compiler to emit this structure into a special
Listing 5-6. Kernel Command Line Processing
1 extern struct obs_kernel_param __setup_start[], __setup_end[];
2
3 static int __init obsolete_checksetup(char *line)
4 {
5 struct obs_kernel_param *p;
6
7 p = __setup_start;
8 do {
9 int n = strlen(p->str);
10 if (!strncmp(line, p->str, n)) {
11 if (p->early) {
12 /* Already done in parse_early_param? (Needs
13 * exact match on param part) */
14 if (line[n] == ' ' || line[n] == '=')
15 return 1;
16 } else if (!p->setup_func) {
17 printk(KERN_WARNING 'Parameter %s is obsolete,'
18 ' ignored
', p->str);
19 return 1;
20 } else if (p->setup_func(line + n))
21 return 1;