Analysis of the U-boot driver process of the Zhan Xun Platform

Source: Internet
Author: User
Recently, I am developing the Android mobile phone driver for Zhan Xun. I need to be clear about the U-boot startup process and then write the relevant driver code. LCD drivers generally choose to load a specific driver during U-boot startup, and the code in U-boot and kernel is roughly the same, android will display the boot logo during the boot process, which involves displaying some drivers. The U-boot startup process can be divided into two phases. The functions of the two phases are as follows:
Stagei: the first-stage function (Zhan Xun is a 16 K assembly code) Hardware Device initializes and loads the second-stage U-boot code to the ram space. Set the stack to jump to the second-stage code entry.
Stageii: second-stage Functions
Initialize the hardware device used in this phase. Check the system memory ing. Read the kernel from flash to Ram and set startup parameters for the kernel to call the kernel.

1.1.1 U-boot startup phase I code analysis

The file corresponding to the first stage is lowlevel_init.s. The first phase of U-boot startup is as follows: 1. hardware Device initialization (1) setting the exception vector where the command "B start_code" for resetting the exception vector determines that after U-boot is started, it will automatically jump to the label "start_code" for execution. (2) set the control register address (3) disable the watchdog (4) Disable MMU, cache (5) initialize the lowlevel_init In the ram control register to complete memory initialization, because the memory initialization depends on the Development Board, the lowlevel_init code is generally stored in the corresponding directory under the board.
Board/Samsung/mini2440/lowlevel_init.o will be linked to the back of CPU/ARM920T/start. O, so board/Samsung/mini2440/lowlevel_init.o is also in the first 4 kb of Code of U-boot. When u-boot is started in NAND Flash, lowlevel_init.o will be automatically read to the 4 kb internal RAM of the CPU. Therefore, 137th ~ The 146 line of code will copy the register value from the CPU's internal RAM to the corresponding register. When u-boot is started in nor flash, U-boot is still in nor Flash because the address determined during U-boot connection is in the memory, therefore, you need to read data to ram in nor flash. Since the nor flash start address is 0, and U-boot load to the memory starting address is text_base, smrdata label in flash Address is SMRDATA-TEXT_BASE. To sum up, lowlevel_init is used to copy the 13 values starting with smrdata to the 13 Registers starting with [bwscon], thus completing the storage controller settings. (6) copy the second-stage code of U-boot to ram CPU/ARM920T/start. s's original code only supports starting from nor flash. After modification, U-boot can be started on either nor flash or NAND Flash. The implementation idea is as follows: BL bbootfrmnorflash/* determines whether U-boot is enabled in NAND Flash or nor flash */CMP r0, #0/* R0 stores the return value of the bbootfrmnorflash function. If the return value is 0, it indicates that the NAND Flash is started, otherwise, the system starts */beq nand_boot/* in nor flash to jump to the NAND Flash startup Code * // * The nor flash startup Code */B stack_setup/* skips the NAND Flash startup Code */ nand_boot: /* NAND Flash startup Code */stack_setup:/* Others Code */where the bbootfrmnorflash function is used to determine whether U-boot is started in NAND Flash or nor flash. If U-boot is started in nor flash, 1 is returned; otherwise, 0 is returned. According to the atpcs rule, function return values are stored in R0 registers. After the bbootfrmnorflash function is used, you can determine whether U-boot is enabled in NAND Flash or nor FLASH based on the R0 value. The bbootfrmnorflash function is defined in board/Samsung/mini2440/nand_read.c as follows:
Int bbootfrmnorflash (void) {volatile unsigned int * PDW = (volatile unsigned int *) 0; unsigned int dwval; dwval = * PDW; /* record the original data first */* PDW = 0x12345678; If (* PDW! = 0x12345678)/* write failure, indicating that the write was successful at nor flash */{return 1;} else, it indicates that */{* PDW = dwval;/* restore the original data */return 0;} is started in NAND Flash ;}}
U-boot can choose to enter two different NAND Flash modes, one is the traditional normal_nand_mode, and the other is normal_emmc_mode. This flash standard can make flash into a large storage capacity.
(6) Jump to the second-stage code entry ldr pc and _ start_armboot:. Word start_armboot to the second-stage code entry start_armboot. 1.1.2 code analysis of the second phase of U-boot startup the start_armboot function is defined in lib_arm/board. C as the entry of the second phase of U-boot code. The second phase of U-boot startup is as follows:
Select a mode to enter: The display startup mode includes fast_mode, specilal_mode, normal_mode, panic_reboot_mode, and so on. Generally, the startup mode enters the normal mode.
Before analyzing the start_armboot function, let's take a look at some important data structures: (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 value of GD 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 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) The init_sequence array U-boot uses an array init_sequence to store the function pointer of the initialization function to be executed on most development boards. There are many compilation options in the init_sequence array. After removing the compilation option, the init_sequence array is as follows: typedef int (init_fnc_t) (void); init_fnc_t * init_sequence [] = {board_init, /* Development Board-related configuration -- 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 */console_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,}; Gd-> BD-> bi_arch_number = mach_type_mini2440;/* kernel startup parameter address */Gd-> BD-> bi_boot_params = 0x30000100;
Int dram_init (void) {Gd-> BD-> bi_dram [0]. start = phys_sdram_1; Gd-> BD-> bi_dram [0]. size = phys_sdram_1_size; return 0;} after the preceding data structure is analyzed, the start_armboot function is analyzed as follows:
Void start_armboot (void) {init_fnc_t ** init_fnc_ptr; char * s ;...... /* Calculate the global data structure address GD */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 at CPU/ARM920T/start. S is initialized as the _ start */mem_malloc_init (_ armboot_start-config_sys_malloc_len, config_sys_malloc_len) in the u-boot.lds connection script ); /* 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_mirror_nand) puts ("NAND:"); nand_init ();/* Go init the NAND */# endif...... /* Configure the environment variable and reposition */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...... The main_loop function is defined in common/Main. C. Generally, U-boot does not start Linux within several seconds after entering the main_loop function, and uses tagged list to pass Parameters to Linux. The data structure of the tag is tag. In the U-boot source code directory include/ASM-arm/setup. H is defined as follows: struct tag_header {u32 size;/* indicates the size of the data stored by the Union U of the tag Data Structure */u32 tag; /* indicates the tag type */}; struct tag {struct tag_header HDR; Union {struct tag_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 cmdline;/** 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 in lib_arm/bootm. C is defined as follows: int do_bootm_linux (INT flag, int argc, char * argv [], bootm_headers_t * images) {bd_t * BD = Gd-> BD; int machid = BD-> bi_arch_number; void (* Thekernel) (INT zero, Int Arch, uint Params); # ifdef config_cmdline_tag char * CommandLine = getenv ("bootargs "); /* U-boot environment variable bootargs */# endif...... Thekernel = (void (*) (INT, Int, uint) images-> EP;/* Get the kernel entry address */... ...} 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. (4) invalidates the data cache and command cache. 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 Row 3 puts 0, R0, and machine code machid into R1, And the Kernel Parameter address BD-> bi_boot_params is 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.
 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.