22 }
23 p++;
24 } while (p < __setup_end);
25 return 0;
26 }
Examination of this code should be fairly straightforward, with a couple of explanations. The function is called with a single command line argument, parsed elsewhere within main.c. In the example we've been discussing, line would point to the string console=ttyS0, 115200, which is one component from the kernel command line. The two external structure pointers __setup_start and __setup_end are defined in a linker script file, not in a C source or header file. These labels mark the start and end of the array of obs_kernel_param structures that were placed in the .init.setup section of the object file.
The code in Listing 5-6 scans all these structures via the pointer p to find a match for this particular kernel command line parameter. In this case, the code is searching for the string console= and finds a match. From the relevant structure, the function pointer element returns a pointer to the console_setup() function, which is called with the balance of the parameter (the string ttyS0, 115200) as its only argument. This process is repeated for every element in the kernel command line until the kernel command line has been completely exhausted.
The technique just described, collecting objects into lists in uniquely named ELF sections, is used in many places in the kernel. Another example of this technique is the use of the __init family of macros to place one-time initialization routines into a common section in the object file. Its cousin __initdata, used to mark one-time-use data items, is used by the __setup macro. Functions and data marked as initialization using these macros are collected into a specially named ELF section. Later, after these one-time initialization functions and data objects have been used, the kernel frees the memory occupied by these items. You might have seen the familiar kernel message near the final part of the boot process saying, 'Freeing init memory: 296K.' Your mileage may vary, but a third of a megabyte is well worth the effort of using the __init family of macros. This is exactly the purpose of the __initdata macro in the earlier declaration of __setup_str_console_setup[].
You might have been wondering about the use of symbol names preceded with obsolete_. This is because the kernel developers are replacing the kernel command line processing mechanism with a more generic mechanism for registering both boot time and loadable module parameters. At the present time, hundreds of parameters are declared with the __setup macro. However, new development is expected to use the family of functions defined by the kernel header file .../include/linux/moduleparam.h, most notably, the family of module_param* macros. These are explained in more detail in Chapter 8, 'Device Driver Basics,' when we introduce device drivers.
The new mechanism maintains backward compatibility by including an unknown function pointer argument in the parsing routine. Thus, parameters that are unknown to the module_param* infrastructure are considered unknown, and the processing falls back to the old mechanism under control of the developer. This is easily understood by examining the well-written code in .../kernel/params.c and the parse_args() calls in .../init/main.c.
The last point worth mentioning is the purpose of the flag member of the obs_kernel_param structure created by the __setup macro. Examination of the code in Listing 5-6 should make it clear. The flag in the structure, called early, is used to indicate whether this particular command line parameter was already consumed earlier in the boot process. Some command line parameters are intended for consumption very early in the boot process, and this flag provides a mechanism for an early parsing algorithm. You will find a function in main.c called do_early_param() that traverses the linker-generated array of __setup- generated structures and processes each one marked for early consumption. This gives the developer some control over when in the boot process this processing is done.
5.4. Subsystem Initialization
Many kernel subsystems are initialized by the code found in main.c. Some are initialized explicitly, as with the calls to init_timers() and console_init(), which need to be called very early. Others are initialized using a technique very similar to that described earlier for the __setup macro. In short, the linker builds lists of function pointers to various initialization routines, and a simple loop is used to execute each in turn. Listing 5-7 shows how this works.
Listing 5-7. Example Initialization Routine
static int __init customize_machine(void) {
/* customizes platform devices, or adds new ones */
if (init_machine) init_machine();
return 0;
}
arch_initcall(customize_machine);
This code snippet comes from .../arch/arm/kernel/setup.c. It is a simple routine designed to provide a customization hook for a particular board.
5.4.1. The *__initcall Macros
Notice two important things about the initialization routine in Listing 5-7. First, it is defined with the __init macro. As we saw earlier, this macro applies the
The second thing to notice is the macro immediately following the definition of the function: arch_initcall (customize_machine). This macro is part of a family of macros defined in .../include/linux/init.h. These macros are reproduced here as Listing 5-8.
Listing 5-8. initcall Family of Macros
#define __define_initcall(level,fn)
static initcall_t __initcall_##fn __attribute_used__
__attribute__((__section__('.initcall' level '.init'))) = fn
#define core_initcall(fn) __define_initcall('1',fn)
#define postcore_initcall(fn) __define_initcall('2',fn)
#define arch_initcall(fn) __define_initcall('3',fn)
#define subsys_initcall(fn) __define_initcall('4',fn)
#define fs_initcall(fn) __define_initcall('5',fn)
#define device_initcall(fn) __define_initcall('6',fn)
#define late_initcall(fn) __define_initcall('7',fn)
In a similar fashion to the __setup macro previously detailed, these macros declare a data item based on the name of the function, and use the
As you can see from Listing 5-8, the name of the section is .initcallN.init, where