The start_armboot function is defined in lib_arm/board. C and is the entry to the second-stage code of U-boot. The second phase of U-boot startup is as follows:
U- boot stage 2 Execution Process
1) gd_t struct
U-boot uses a struct gd_t to store data in the global data zone. This struct is defined in include/ASM-arm/global_data.h as follows:
Typedef struct global_data {
Bd_t * BD;
Unsigned long flags;
Unsigned long baudrate;
Unsigned long have_console;/* serial_init () was called */
Unsigned long env_addr;/* address of Environment struct */
Unsigned long env_valid;/* checksum of Environment valid? */
Unsigned long fb_base;/* base address of Frame Buffer */
Void ** JT;/* Jump table */
} Gd_t;
U-boot uses a pointer GD stored in the register to record the address of the global data zone:
# Define declare_global_data_ptr register volatile gd_t * gd asm ("R8 ")
Declare_global_data_ptr defines a pointer to the gd_t global data structure, which is stored in the specified register R8. This statement also avoids the compiler from allocating R8 to other variables. For any code that wants to access the global data zone, you only need to add a line of code "declare_global_data_ptr" at the beginning of the code, and then you can use the GD pointer to access the global data zone.
The GD value can be calculated based on the U-boot memory usage diagram:
GD = text_base-config_sys_malloc_len-sizeof (gd_t)
(2) bd_t struct
Bd_t is defined in include/asm-arm.u/u-boot.h as follows:
Typedef struct bd_info {
Int bi_baudrate;/* serial communication baud rate */
Unsigned long bi_ip_addr;/* IP Address */
Struct environment_s * bi_env;/* Environment Variable Start address */
Ulong bi_arch_number;/* machine code of the Development Board */
Ulong bi_boot_params;/* start address of the Kernel Parameter */
Struct/* ram configuration information */
{
Ulong start;
Ulong size;
} Bi_dram [config_nr_dram_banks];
} Bd_t;
When u-boot starts the kernel, parameters must be passed to the kernel. In this case, the information in the gd_t and bd_t struct should be used to set the tag list.
(3) init_sequence Array
U-boot uses an array of init_sequence to store function pointers for initialization functions executed on most development boards. There are many compilation options in the init_sequence array. After removing the compilation options, the init_sequence array is shown as follows:
Typedef int (init_fnc_t) (void );
Init_fnc_t * init_sequence [] = {
Board/Samsung/mini2440/mini2440.c */
Timer_init,/* Clock initialization -- CPU/ARM920T/s3c24x0/Timer. C */
Env_init,/* initialize the environment variable -- Common/env_flash.c or common/env_nand.c */
Init_baudrate,/* initialize the baud rate -- lib_arm/board. C */
Serial_init,/* serial port initialization -- Drivers/serial/serial_s3c24x0.c */
Lele_init_f,/* control communication station initialization Phase 1 -- Common/console. C */
Display_banner,/* print the U-boot version and compile time -- gedit lib_arm/board. C */
Dram_init,/* configure available Ram -- board/Samsung/mini2440/mini2440.c */
Display_dram_config,/* display the ram size -- lib_arm/board. C */
Null,
};
The board_init function is defined in board/Samsung/mini2440/mini2440.c. This function sets mpllcom, upllcon, and the value of some gpio registers, the U-boot machine code and kernel startup parameter address are also set:
/* Machine code of the mini2440 Development Board */
GD-> BD-> bi_arch_number = mach_type_mini2440;
/* Kernel startup parameter address */
GD-> BD-> bi_boot_params = 0x30000100;
The dram_init function is defined in board/Samsung/mini2440/mini2440.c as follows:
Int dram_init (void)
{
/* Since mini2440 is only available */
GD-> BD-> bi_dram [0]. Start = phys_sdram_1;
GD-> BD-> bi_dram [0]. size = phys_sdram_memory size;
Return 0;
}
Mini2440 uses 2 32 mb sdram to Form 64 MB memory, connected to the storage controller's bank6, and the address space is 0x30000000 ~ 0x34000000.
In include/configs/mini2440.h, phys_sdram_1 and phys_sdram_0000size are defined as 0x30000000 and 0x04000000 (64 m) respectively ).
After the above data structure is analyzed, the start_armboot function is analyzed below:
Void start_armboot (void)
{
Init_fnc_t ** init_fnc_ptr;
Char * s;
... ...
/* Calculate the address GD of the global data structure */
GD = (gd_t *) (_ armboot_start-config_sys_malloc_len-sizeof (gd_t ));
... ...
Memset (void *) Gd, 0, sizeof (gd_t ));
GD-> BD = (bd_t *) (char *) Gd-sizeof (bd_t ));
Memset (Gd-> BD, 0, sizeof (bd_t ));
GD-> flags | = gd_flg_reloc;
Monitor_flash_len = _ bss_start-_ armboot_start;
/* Call the initialization function in the init_sequence array one by one */
For (init_fnc_ptr = init_sequence; * init_fnc_ptr; ++ init_fnc_ptr ){
If (* init_fnc_ptr )()! = 0 ){
Hang ();
}
}
/* Armboot_start is initialized in CPU/ARM920T/start. s as _ start */In the u-boot.lds connection script */
Mem_malloc_init (_ armboot_start-config_sys_malloc_len,
Config_sys_malloc_len );
/* Nor flash initialization */
# Ifndef config_sys_no_flash
/* Configure available Flash banks */
Display_flash_config (flash_init ());
# Endif/* config_sys_no_flash */
... ...
/* NAND Flash initialization */
# If defined (config_assist_nand)
Puts ("NAND :");
Nand_init ();/* Go init the NAND */
# Endif
... ...
/* Configure the environment variable and locate again */
Env_relocate ();
... ...
/* Obtain the IP address from the environment variable */
GD-> BD-> bi_ip_addr = getenv_ipaddr ("ipaddr ");
Stdio_init ();/* Get the devices list going .*/
Jumptable_init ();
... ...
Lele_init_r ();/* fully init console as a device */
... ...
/* Enable exceptions */
Enable_interrupts ();
# Ifdef config_usb_device
Usb_init_slave ();
# Endif
/* Initialize from Environment */
If (S = getenv ("loadaddr "))! = NULL ){
Load_addr = simple_strtoul (S, null, 16 );
}
# If defined (config_cmd_net)
If (S = getenv ("bootfile "))! = NULL ){
Copy_filename (bootfile, S, sizeof (bootfile ));
}
# Endif
... ...
/* Nic initialization */
# If defined (config_cmd_net)
# If defined (config_net_multi)
Puts ("Net :");
# Endif
Eth_initialize (Gd-> BD );
... ...
# Endif
/* Main_loop () can return to retry autoboot, if so just run it again .*/
For (;;){
Main_loop ();
}
/* Notreached-No Way Out of command loop handle T booting */
}
The main_loop function is defined in common/Main. C. Generally, it does not take several seconds to enter the main_loop function.
1.1.3 U-boot Linux Startup Process (in arm-Kernel
Memory collection has a detailed explanation)
U-boot uses tagged list to pass Parameters to Linux. The data structure of the tag is tag, which is defined in the U-boot source code directory include/ASM-arm/setup. h as follows:
Struct tag_header {
U32 size;/* indicates the actual data size of the Union U in the tag Data Structure */
U32 tag;/* indicates the tag type */
};
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;
/*
* Acorn specific
*/
Struct tag_acorn acorn;
/*
* Dc21285 specific
*/
Struct tag_memclk memclk;
} U;
};
U-boot uses the bootm command to start the kernel that has been loaded into the memory. The bootm Command actually calls the do_bootm function. For linux kernels, The do_bootm function calls the do_bootm_linux function to set the tag list and start the kernel. The do_bootm_linux function is defined in lib_arm/bootm. C as follows:
59 int do_bootm_linux (INT flag, int argc, char * argv [], bootm_headers_t * images)
60 {
61 bd_t * BD = Gd-> BD;
62 char * s;
63 int machid = BD-> bi_arch_number;
64 void (* Thekernel) (INT zero, Int Arch, uint Params );
65
66 # ifdef config_cmdline_tag
67 char * CommandLine = getenv ("bootargs");/* U-boot environment variable bootargs */
68 # endif
... ...
73 Thekernel = (void (*) (INT, Int, uint) images-> EP;/* Get the kernel entry address */
... ...
86 # If defined (config_setup_memory_tags) | \
87 defined (config_cmdline_tag) | \
88 defined (config_initrd_tag) | \
89 defined (config_serial_tag) | \
90 defined (config_revision_tag) | \
91 defined (config_ LCD) | \
92 defined (config_vfd)
93 setup_start_tag (BD);/* set the atag_core flag */
... ...
100 # ifdef config_setup_memory_tags
101 setup_memory_tags (BD);/* set the memory tag */
102 # endif
103 # ifdef config_cmdline_tag
104 setup_commandline_tag (BD, CommandLine);/* set the command line flag */
105 # endif
... ...
113 setup_end_tag (BD);/* set the atag_none flag */
114 # endif
115
116/* we assume that the kernel is in place */
117 printf ("\ nstarting kernel... \ n ");
... ...
126 cleanup_before_linux ();/* set the CPU before starting the kernel */
127
128 Thekernel (0, machid, BD-> bi_boot_params);/* call the kernel */
129/* does not return */
130
131 return 1;
132}
The setup_start_tag, setup_memory_tags and setup_end_tag functions are defined as follows in lib_arm/bootm. C:
(1) setup_start_tag Function
Static void setup_start_tag (bd_t * BD)
{
Params = (struct tag *) BD-> bi_boot_params;/* start address of the Kernel Parameter */
Params-> HDR. Tag = atag_core;
Params-> HDR. size = tag_size (tag_core );
Params-> U. Core. Flags = 0;
Params-> U. Core. pagesize = 0;
Params-> U. Core. rootdev = 0;
Params = tag_next (Params );
}
The tag list must start with atag_core. The setup_start_tag function sets an atag_core tag at the start address of the kernel parameter.
(2) setup_memory_tags Function
Static void setup_memory_tags (bd_t * BD)
{
Int I;
/* Set a memory tag */
For (I = 0; I <config_nr_dram_banks; I ++ ){
Params-> HDR. Tag = atag_mem;
Params-> HDR. size = tag_size (tag_mem32 );
Params-> U. mem. Start = BD-> bi_dram [I]. Start;
Params-> U. mem. size = BD-> bi_dram [I]. size;
Params = tag_next (Params );
}
}
The setup_memory_tags function sets an atag_mem mark, which contains the memory start address and memory size parameters.
(3) setup_end_tag Function
Static void setup_end_tag (bd_t * BD)
{
Params-> HDR. Tag = atag_none;
Params-> HDR. size = 0;
}
The tag list must end with the tag atag_none. The setup_end_tag function sets an atag_none tag, indicating the end Of the tag list.
After the U-boot tag list is set, the kernel will be called. However, before calling the kernel, the CPU must meet the following conditions:
(1) CPU register settings
R0 = 0
R1 = machine code
Ø r2 = start address of the Kernel Parameter tag list in Ram
(2) CPU Operation Mode
Disable IRQ and FIQ interruptions
The CPU is in SVC mode.
(3) invalidate the data cache and command Cache
In do_bootm_linux, The cleanup_before_linux function is called to disable interruption and disable cache. The cleanup_before_linux function is defined in CPU/ARM920T/CPU:
Int cleanup_before_linux (void)
{
/*
* This function is called just before we call Linux
* It prepares the processor for Linux
*
* We turn off caches etc...
*/
Disable_interrupts ();/* disable FIQ/IRQ interruption */
/* Turn Off I/D-Cache */
Icache_disable ();/* invalidate the Instruction Cache */
Dcache_disable ();/* invalidate data cache */
/* Flush I/D-Cache */
Cache_flush ();/* refresh cache */
Return 0;
}
Since U-boot has been working in SVC mode since it was started, you do not need to set the CPU working mode.
In do_bootm_linux:
64 void (* Thekernel) (INT zero, Int Arch, uint Params );
... ...
73 Thekernel = (void (*) (INT, Int, uint) images-> EP;
... ...
128 Thekernel (0, machid, BD-> bi_boot_params );
Line 3 of code forcibly converts the kernel entry address "images-> EP" to a function pointer. According to the atpcs rule, when the number of function parameters cannot exceed 4, R0 ~ is used ~ R3 registers to pass parameters. Therefore, the function call of Line 1 will put 0 into r0, machine code machid into R1, Kernel Parameter address BD-> bi_boot_params put into R2, thus completing register settings, finally, go to the kernel entry address.
At this point, U-boot is finished, and the system jumps to the Linux kernel for code execution.
1.1.4 how to add commands for U-boot and how to execute the U-boot command
The following uses the "add menu" command (Start Menu) as an example to explain how to add a command to U-boot.
(1) create common/cmd_menu.c
Traditionally, the General Command source code is placed in the common directory, and the development board proprietary command source code is placed in the Board/<board_dir> directory, and is used to "cmd _ <command name>. C "is the file name.
(2) define the "menu" command
Use the following code in cmd_menu.c to define the "menu" command:
_ Boot_cmd (
Menu, 3, 0, do_menu,
"Menu-display a menu, to select the items to do something \ n ",
"-Display a menu, to select the items to do something"
);
The u_boot_cmd command format is as follows:
U_boot_cmd (name, maxargs, rep, CMD, usage, help)
The meanings of parameters are as follows:
Name: The command name, which is not a string, but is converted to a string using the "#" symbol in u_boot_cmd.
Maxargs: Maximum number of parameters of a command
REP: whether to automatically repeat (whether repeated execution will be performed by pressing enter)
CMD: The corresponding response function of the command.
Usage: short usage (string)
Help: More detailed instructions (string)
Saving the help field of the command in the memory occupies a certain amount of memory. You can choose whether to save the help field by configuring U-boot. If the config_sys_longhelp macro is defined in include/configs/mini2440.h, the usage and help fields are displayed when you use the help command in U-boot to view the help information of a command, otherwise, only the usage field is displayed.
U_boot_cmd macro is defined in include/command. h:
# Define u_boot_cmd (name, maxargs, rep, CMD, usage, help )\
Pai_tbl_t _ u_boot_cmd _ # name struct_section = {# name, maxargs, rep, CMD, usage, help}
Both "#" and "#" are pre-compiled operators. "#" supports string connection. "#" indicates a string followed by a string.
Cmd_tbl_t is defined in include/command. h as follows:
Struct rj_tbl_s {
Char * Name;/* command name */
Int maxargs;/* Maximum number of parameters */
Int repeatable;/* auto-Repetition */
INT (* cmd) (struct cmd_tbl_s *, Int, Int, char * []);/* response function */
Char * usage;/* brief help information */
# Ifdef config_sys_longhelp
Char * help;/* Detailed Help information */
# Endif
# Ifdef config_auto_complete
/* Auto-completion parameters */
INT (* Complete) (INT argc, char * argv [], char last_char, int maxv, char * cmdv []);
# Endif
};
Typedef struct into _tbl_s into _tbl_t;
A cmd_tbl_t struct variable contains the information required to call a command.
Struct_section is defined in include/command. h as follows:
# Define struct_section _ attribute _ (unused, section (". u_boot_cmd ")))
Anything with _ attribute _ (unused, section (". u_boot_cmd ") attribute declaration variables will be stored in ". u_boot_cmd ", and no warning is generated even if the variable is not explicitly used by the compiler in the code.
The. u_boot_cmd section is defined in the U-boot connection script u-boot.lds:
. = .;
_ U_boot_cmd_start =.;/* specify _ u_boot_cmd_start as the current address */
. U_boot_cmd: {* (. u_boot_cmd )}
_ U_boot_cmd_end =.;/* specify _ u_boot_cmd_end as the current address */
This indicates that the function or variable with the ". u_boot_cmd" declaration will be stored in the "u_boot_cmd" section. In this way, you only need to add ". u_boot_cmd "declaration, the compiler will automatically put it in the" u_boot_cmd "section, and search between _ u_boot_cmd_start and _ u_boot_cmd_end when searching for the variable pai_tbl_t.
Therefore, the definition of the "menu" command is expanded as follows:
Pai_tbl_t _ u_boot_cmd_menu _ attribute _ (unused, section (". u_boot_cmd ") = {menu, 3, 0, do_menu," menu-display a menu, to select the items to do something \ n ","-display a menu, to select the items to do something "}
In essence, a struct of the cmd_tbl_t type is constructed using the u_boot_cmd macro-defined information. The compiler places the struct in the "u_boot_cmd" section. When executing the command, you can find the corresponding struct of the cmd_tbl_t type in the "u_boot_cmd" section.
(3) functions implementing commands
Add the implementation of the response function of the "menu" command in cmd_menu.c. The specific implementation code is omitted:
Int do_menu (cmd_tbl_t * cmdtp, int flag, int argc, char * argv [])
{
/* Implementation Code omitted */
}
(4) Compile common/cmd_menu.c into u-boot.bin
Add the following code to the common/makefile:
Cobjs-$ (config_boot_menu) + = pai_menu.o
Add the following code to include/configs/mini2440.h:
# Define config_boot_menu 1
Re-compile and download the U-boot command to use the menu command.
(5) menu Command Execution Process
When the "menu" command is entered in U-boot for execution, U-boot receives the input string "menu" and passes it to the run_command function. The run_command function calls the find_cmd function implemented in common/command. C to find the command between _ u_boot_cmd_start and _ u_boot_cmd_end, and returns the cmd_tbl_t structure of the menu command. Then, the run_command function uses the function pointer in the returned javas_tbl_t structure to call the do_menu function of the menu command, thus executing the command.