In an embedded system, bootloader is used to initialize hardware, load the kernel, and pass parameters. Because the hardware environment of the embedded system is different, the bootloader of the embedded system is also different. The most common one is u-boot. It supports different architectures, such as arm, powerPC, x86, MIPS, etc. This article describes the basic function of passing parameters between Bootloader and kernel. The hardware platform is based on at91rm9200 processor system, and the software platform is the Linux-2.6.19.2 kernel. The kernel image file is zimage.
1. system hardware platform Overview
At91rm9200 processor, which is a microprocessor based on ARM920T kernel of ATMEL. It has a memory management unit and a CPU clock of up to 240 MHz. It has a wide range of standard interfaces and EBI interfaces, static storage controller (SMC), SDRAM controller, and burst flash controller are integrated internally. For instructions on processors, see the at91rm9200 Data Manual. In this system, the SDRAM (64 MB) address is 0x20000000, And the norflash (8 Mb) address is 0x10000000.
2. Design and Implementation of bootloader
The documentation/ARM/booting document under the kernel source code directory specifies the basic functions of bootloader Based on the ARM architecture. In addition to completing these basic functions, the bootloader of the system also adds code handling and other functions based on the features of its own hardware.
The bootloader process is: after the system is powered on and reset, it starts to run from norflash (determined by the connection of the BMS pin of the processor ), because the 0 address of the processor is the first address of norflash (0x10000000), and the bootloader is burned in this position, the address range mapped by the at91rm9200 processor is only 0x0000-0x001f FFFF. The first step of bootloader execution is to move its own code from norflash to the RAM (0x00200000) in the processor, and then map the 0 address to the internal RAM, go to the corresponding address of the internal RAM and continue execution. After entering the internal RAM, the system enters the real hardware initialization phase. Various controllers initialized in this phase are required by the kernel, including PMC, Ebi, SMC, SDRAM, and usart. The next step is to create the tagged list. After the chain is created, the kernel image and the root file system image written in norflash are moved to the SDRAM, disable interruption, MMU, and data cache based on the basic requirements of the kernel for Bootloader, and configure R0 = 0, r1 = 0x0000 00fb or 0x00000106 (according to the machine number specified by Linux/ARCH/ARM/tools/Mach-types in the kernel ), r2 = 0x20000100 (the physical address that bootloader passes to the Kernel Parameter linked list). In the ARM architecture, this address is default in the machine Descriptor (machine_desc) of the same processor, therefore, this parameter can be left unspecified. Finally, the bootloader directly jumps to the SDRAM kernel for execution.
3. List of Kernel Parameters
Bootloader can pass parameters to the kernel in two ways. One is the old parameter structure (parameter_struct), which is mainly used by the kernel before 2.6. Another method is the tagged list method currently used by the 2.6 kernel. These parameters mainly include the system root device identifier, page size, memory start address and size, ramdisk start address and size, and the compressed ramdisk root file system start address and size, kernel command parameters.
The format and description of the Kernel Parameter linked list can be found in include/ASM-arm/setup. h In the kernel source code directory tree. The parameter linked list must start with atag_core and end with atag_none. Here, atag_core and atag_none are the tags of each parameter. They are a 32-bit value, for example, atag_core = 0x54410001.
Other parameter tags include atag_mem32, atag_initrd, atag_ramdisk, and atag_comdline. Each parameter tag represents a parameter struct, which forms a parameter linked list. The parameter struct is defined as follows:
Struct tag
{
Struct tag_header HDR;
Union {
Struct tag_core core;
Struct tag_mem32 MEM;
Struct tag_videotext videotext;
Struct tag_ramdisk ramdisk;
Struct tag_initrd initrd;
Struct tag_serialnr serialnr;
Struct tag_revision revision;
Struct tag_videolfb videolfb;
Struct tag_cmdline using line;
Struct tag_acorn acorn;
Struct tag_memclk memclk;
} U;
};
The parameter struct includes two parts: one is the tag_header struct and the other is the U consortium.
The tag_header struct is defined as follows:
Struct tag_header
{
U32 size;
U32 tag;
};
Size: indicates the size of the entire tag struct (expressed by the number of words, rather than the number of bytes), equal to the size of the tag_header plus the size of the U consortium. For example, the size of the parameter struct atag_core
Size = (sizeof (tag-> tag_header) + sizeof (tag-> U. core)> 2. Generally, the tag_size (struct * tag_xxx) function is used to obtain the size of each parameter struct. Tag indicates the tag of the entire tag structure, such as atag_core.
The Consortium U includes all available Kernel Parameter types, including tag_core, tag_mem32, and tag_ramdisk. The traversal between parameter struct is implemented through the tag_next (struct * tag) function. The system parameter linked list includes the following struct types: atag_core, atag_mem, atag_ramdisk, atag_initrd32, atag_1_line, and atag_end. In the parameter linked list, apart from the fixed positions of the parameter struct atag_core and atag_end, the order of the parameter struct is arbitrary. The parameter Chain List passed by this bootloader is as follows: the first Kernel Parameter structure is marked as atag_core and the parameter type is tag_core. For the definition of each parameter type, see the source code file.
Tag_array is initialized as a pointer to the first struct of the parameter linked list.
Tag_array-> HDR. Tag = atag_core;
Tag_array-> HDR. size = tag_size (tag_core );
Tag_array-> U. Core. Flags = 1;
Tag_array-> U. Core. pagesize = 4096;
Tag_array-> U. Core. rootdev = 0x00100000;
Tag_array = tag_next (tag_array );
Tag_array-> HDR. Tag = atag_mem;
Tag_array-> HDR. size = tag_size (tag_mem32 );
Tag_array-> U. mem. size = 0x04000000;
Tag_array-> U. Memory. Start = 0x20000000;
Tag_array = tag_next (tag_array );
......
Tag_array-> HDR. Tag = atag_none;
Tag_array-> HDR. size = 0;
Tag_array = tag_next (tag_array );
Finally, copy the linked list of kernel parameters to the default physical address 0x20000100 of the kernel. In this way, the parameter linked list is created.
4. kernel receiving Parameters
The following describes how the Linux kernel receives the kernel parameters passed by bootloader from the zimage Image Based on the ARM architecture,
In the file ARCH/ARM/boot/compressed/head. S, start is the starting point of zimage. Some code is as follows:
Start:
MoV R7, r1
MoV R8, r2
......
MoV r0, r4
MoV R3, r7
BL decompress_kernel
B call_kernel
Call_kernel:
......
MoV r0, #0
MoV R1, r7
MoV R2, R8
MoV PC, r4
First, save the values of R1 (machine number) and R2 (physical address of the parameter linked list) passed by bootloader to R7 and R8, then, pass R7 as a parameter to the decompressed function decompress_kernel (). In the extract function, pass R7 to the global variable _ machine_arch_type. Restore R7 and R8 to R1 and R2.
In the ARCH/ARM/kernel/head. s file, part of the kernel (vmlinux) entry code is as follows:
Stext:
MRC P15, 0, R9, C0, C0
BL _ lookup_processor_type
.........
BL _ lookup_machine_type
First, the ARM Kernel type is obtained from the special registers (CP15) inside the processor, and from the processor kernel Descriptor (proc_info_list) Table (_ proc_info_begin-_ proc_info_end) to check whether the ARM Kernel type is available. If not, the system exits with an error. The processor kernel descriptor is defined in include/ASM-arm/procinfo. h, the specific function implementation in arch/ARM/MM/proc-xxx.S, In the compilation of the connection process to combine various processor kernel descriptor into a table. Then, query whether the machine number specified by the R1 register is available from the machine Descriptor (machine_desc) Table (_ mach_info_begin-_ mach_info_end). If no machine number is available, the system exits with an error. The machine number mach_type_xxx is described in the arch/ARM/tools/Mach-types file. Each machine descriptor contains a unique machine number, the machine descriptor is defined in include/ASM-arm/Mach/arch. h. The specific implementation is in the arch/ARM/Mach-XXXX folder. During the compilation and connection process, different machine descriptors based on the same processor are combined into tables. For example, various machine descriptors based on the at91rm9200 processor can refer to arch/ARM/mach-at91rm9200/board-xxx.c, machine descriptor with machine Number 262 is as follows:
Machine_start (at91rm9200dk, "Atmel AT91RM9200-DK ")
/* Maintainer: San people/Atmel */
. Phys_io = at91_base_sys,
. Io_pg_offst = (at91_va_base_sys> 18) & 0 xfffc,
. Boot_params = at91_sdram_base + 0x100,
. Timer = & at91rm9200_timer,
. Map_io = dk_map_io,
. Init_irq = dk_init_irq,
. Init_machine = dk_board_init,
Machine_end
The last step is to open MMU and jump to the start_kernel (initialization system) of init/Main. C. In init/Main. C, part of the code of the start_kernel () function is as follows:
{
......
Setup_arch ();
......
}
In ARCH/ARM/kernel/setup. C, part of the code of the setup_arch () function is as follows:
{
......
Setup_processor ();
Mdesc = setup_machine (machine_arch_type );
......
Parse_tags (TAGS );
......
}
The setup_processor () function finds matching descriptors from the processor kernel Descriptor Table and initializes some processor variables. Setup_machine () uses the machine number (assigned in decompress_kernel) as the parameter to return the machine descriptor. Obtain the physical address of the Kernel Parameter from the machine Descriptor and assign it to the tags variable. Then, call the parse_tags () function to analyze the Kernel Parameter linked list and pass each parameter value to the global variable. In this way, the kernel receives the parameters passed by bootloader.
5. Verification and testing of parameter passing
The result of passing parameters can be verified through the print information of kernel startup.
MACHINE: Atmel AT91RM9200-DK
......
Kernel command line: Console = ttys0, 115200 root =/dev/Ram RW init =/linuxrc
......
Memory: 64 MB = 64 MB total
......
Checking if image is initramfs... It isn' t (no cpio magic); looks like an initrd
Freeing initrd memory: 1024 K
......
Ramdisk: compressed image found at Block 0
A complete bootloader is a complex project. This article only describes the basic functions of the embedded system bootloaer. This basic function is indispensable for any bootloader. The kernel can be started correctly only when these parameters are received. It also lays a good foundation for kernel porting and debugging.
PASS Parameters by copying tags in the bootm command
For ease of reading, a few modifications are made, but the function remains unchanged. This function parameter is the address for storing startup parameters.
Static void setup_linux_tag (ulong param_base)
{
Struct tag * Params = (struct tag *) param_base;
Char * linux_cmd;
Char * P;
Memset (Params, 0, sizeof (struct tag ));
/* Step 1: Setup start tag */
Params-> HDR. Tag = atag_core;
Params-> HDR. size = tag_size (tag_core );
Params-> U. Core. Flags = 0;
Params-> U. Core. pagesize = linux_page_size;
Params-> U. Core. rootdev = 0;
Params = tag_next (Params );
/* Step 2: setup your line tag */
Params-> HDR. Tag = atag_cmdline;
Linux_cmd = getenv ("bootargs ");
/* Eat leading white space */
For (P = linux_cmd; * P = ''; P ++) {/* do nothing */;}
Params-> HDR. size = (sizeof (struct tag_header) + strlen (linux_cmd) + 1 + 4)> 2;
Memcpy (Params-> U. cmdline. cmdline, linux_cmd, strlen (linux_cmd) + 1 );
Params = tag_next (Params );
/* Step 3: Setup end tag */
Params-> HDR. Tag = atag_none;
Params-> HDR. size = 0;
}
In uboot, all the tag-setting functions are in lib_arm/armlinux. C. Before these functions, ifdef
# If defined (config_setup_memory_tags) |/
Defined (config_cmdline_tag) |/
Defined (config_initrd_tag) |/
Defined (config_serial_tag) |/
Defined (config_revision_tag) |/
Defined (config_ LCD) |/
Defined (config_vfd)
Therefore, if your bootm command cannot pass kernel parameters, it should be because the above
Set macro. Just define it.