uboot重定位后初始化
本文接博文《uboot启动之第一次运行C函数到uboot重定位》,从relocate_done处开始。
relocate_done之后,就是跳转到lr处执行。
这个lr在relocate_code之前就设置好了,为uboot/arch/arm/lib/crt0.S中的here:
114行使用bl跳转到c_runtime_cpu_setup,这样就吧116行指令地址传给了lr寄存器(r14)。
c_runtime_cpu_setup定义在uboot/arch/arm/cpu/arm1176/start.S:
247行直接将lr给了pc,于是程序就回到了uboot/arch/arm/lib/crt0.S中的116行。
116-124行,就是将__bss_start - __bss_end这一段内存清零。
126行跳转到coloured_LED_init, 这个C函数定义在uboot/common/board_f.c中。
uboot/common/board_f.c:
这是个空函数。返回后,程序来到127行,跳转到red_led_on。
和coloured_LED_init一样,这也是个空函数。定义在uboot/common/board_f.c
程序返回uboot/arch/arm/lib/crt0.S继续执行。
130行,r8保存的是relocate之后的gd的地址。
131行r1为uboot image relocate之后的地址。
133行,将PC指向board_init_r,即下一条指令就是运行board_init_r,有两个参数r0和r1,
r0为新gd地址, r1为image的新地址。
board_init_r定义在uboot/common/board_r.c中。
uboot/common/board_r.c
925行循环调用init_sequence_r list中的函数。
initr_reloc:
设置relocate flags,mark boot stage为uboot_r.
initr_caches:
使能指令和数据cache.
uboot/arch/arm/cpu/astcommon/cache.c(CONFIG_SYS_ICACHE_OFF未定义,但是CONFIG_SYS_DCACHE_OFF定义了,故这里只有icache_enable做了)
uboot/arch/arm/lib/cache_cp15.c
board_init:
板子初始化,定义在uboot/board/ast2500evb/ast2500evb.c
97行使能icache多余,前面已经做过了。之后,配置GPIO寄存器。
105行disable watch dog.
initr_reloc_global_data:
只是记录下uboot image size。
CONFIG_SYS_SYM_OFFSETS在uboot/include/asm-generic/sections.h
initr_serial:
初始化串口。
271行调用serial_assign,设置当前串口UART为ns16550 COM5.
initr_malloc:
初始化malloc zone为uboot image前面的128K, 并初始化为0.
uboot/common/dlmalloc.c
power_init_board:
ast2500evb没有这个函数,使用uboot/common/board_r.c中默认的。
initr_flash:
初始化flash.
首先向串口打印”Flash: ”, 然后调用board_flash_wp_on. ast2500evb使用默认的__board_flash_wp_on,返回0.
故直接进入了flash_init(), 该函数定义在uboot/drivers/spiflash/spiflash.c
flash_init调用SPI_CTRL_DIRVER_INIT()去初始化spi driver. SPI_CTRL_DRIVER_INIT定义在uboot/arch/arm/cpu/astcommon/ast_spiflash.c
SPI_CTRL_DRIVER_INIT做两件事:
- 调用ast_spiflash_init()去配置Firmware spi Memory Controller(FMC, 基址0x1E620000)。
- 调用register_spi_ctrl_driver()注册驱动到spi_ctrl_drvs_list.
之后,flash_init通过register_spi_chip_driver()注册各种不同spi chip(芯片)的驱动, 比如atmel, m25pxx, spansion, micron等。
并清空flash_info.( CONFIG_SYS_MAX_FLASH_BANKS为5,定义在uboot/include/configs/ast.cfg)
然后调用probe_spi_chips去探测系统中存在的spi存储芯片。
我们的系统中使用的是美光Micron N25Q00A存储芯片,我们来看一下micron_init()时注册的probe函数:micron_probe()
uboot/drivers/spiflash/micron.c
micron_probe() 调用 spi_generic_probe()去探测芯片。
spi_generic_probe打开write enable, 写入spi_read命令来读取前3个字节的设备数据。
然后用这3个字节的设备数据去匹配已知的设备list,来确定是哪个设备。
uboot/dribers/spiflash/generic.c
这里的ctrl_drv->spi_transfer()为uboot/arch/arm/cpu/astcommon/ast_spiflash.c中的ast_spiflash_transfer().
uboot/arch/arm/cpu/astcommon/ast_spiflash.c
spi_generic_probe得知是什么micron芯片后,设置IO read/write模式到ctrl_drv->fast_read/ctrl_drv->fast_write,并将flash chip信息拷贝到出参chip_info, 最后通过串口打印部分芯片信息。
这里printk() -> printf(), printf()在uboot/common/console.c中,它将调用puts() -> putc()将信息向串口ns16550 COM5输出。
initr_mmc:
先向串口输出“MMC: ”,然后调用mmc_initialize()。
uboot/drivers/mmc/mmc.c
对于ast2500evb,board_mmc_init未定义,使用默认的,返回-1,cpu_mmc_init()就是去配置sd寄存器。
uboot/arch/arm/cpu/astcommon/ast_mmc.c
initr
_env:
初始化环境变量env,我们在uboot relocate之前,也初始化了一个default ENV(uboot/common/board_f.c),并保存到gd->env_addr。现在relocate之后,我们要使用特定的ENV,不使用default ENV。这个特定的ENV是烧写在flash上的,即地址0x20040000(512M).
init_env() -> env_relocate()
uboot/common/board_r.c
uboot/common/env_common.c
由于在uboot image relocate之前设置了default ENV,这个gd->env_valid被设置为1了,故env_relocate() 调用env_relocate_spec(). Env_relocate_spec()从flash ENV烧写地址0x20040000处读取环境变量,然后import()。
uboot/common/env_spi.c
flash_read()最终通过调用micron驱动read来读取micron flash上的ENV数据。
从flash上读取的ENV数据存放在buf中,然后调用env_import()进一步处理。
uboot/common/env_common.c
env_import()在himport_r()是会从malloc区申请一块内存来存放ENV数据,在190行由gd->env_addr来指向。
这里使用的malloc函数在uboot/include/malloc.h定义:
uboot/include/malloc.h
uboot/common/dlmalloc.c
stdio_init:
stdio设置。
uboot/common/stdio.c
drv_system_init()注册两个stdio设备: serial 和 nulldev.
serial_stdio_init()将之前的ns16550 COM5设备注册未stdio设备。
uboot/drivers/serial/serial.c
console_init_r:
控制台初始化,将之前配置的stdio devices之一设置为stdin, stdout, stderr设备。
uboot/common/console.c
779行获取之前通过stdio_register()注册的stdio设备,如下:
uboot/common/stdio.c
797-808行便利stdio device list找出第一个可input&output的device,从之前的stdio_init()看,第一个设备为serial,这里找到的肯定就是这个设备。
812行将找到的设备serial设置为stdout/stderr设备。
822行将找到的设备serial设置为stdin设备。
uboot/common/console.c
834行设置三个env变量:stdin, stdout, stderr,值均为找到设备serial.
uboot/common/stdio.c
misc_init_r:
通过配置SCU和LPC寄存器来控制各种设备。
uboot/board/ast2500evb/ast2500evb.c
initr_enable_interrupts:
enable中断。
uboot/common/board_r.c
enable_interruptes()通过配置cprs寄存器来enable中断。
uboot/arch/arm/lib/interrupts.c
61行将bit7清零。
62行将temp设置为cpsr_c(cpsr的低8bits), 由于61行将bit7(I)清零,故这里就enable IRQ了。
initr_ethaddr:
从环境变量中
拿出ethaddr地址(MAC地址)。
uboot/common/board_r.c
initr_net:
初始化net。
uboot/common/board_r.c
605行先向串口ns16550 COM5输出”Net: ”, 然后调用eth_initialize();
uboot/net/eth.c
eth_initialize()在316行调用cpu_eth_init去初始化eth,在333行将该设备的name向串口输出,344行去从ENV变量中取出ethxaddr=<MAC>, 并将MAC地址写入eth设备(x表示eth的index,比如eth1, eth2…, 如果是eth0,则为ethaddr=<MAC>).
我们来看一下cpu_eth_init().
uboot/arch/arm/cpu/astcommon/ast_eth.c
ast_eth_initialize()主要是配置SCU相关寄存器以配置eth。然后申请eth_device内存,并通过eth_register注册上去。最后enable MII管脚,注册miiphy.
uboot/net/eth.c
run_main_loop:
uboot/common/board_r.c
uboot/common/main.c
process_boot_delay(),处理auto boot kernel之前的delay时间,如果delay时间内,用户输入任意键,则auto boot kernel停止,进入和用户的交互程序。
387行,在我的环境上, bootdelay=3。
417行,获取环境变量bootcmd的值。
441行,打印输出”DRAM ECC disabled”。
444行,调用abortboot() -> abortboot_normal()。
225行,打印提示”Hit key ‘b’ or ‘B’ to stop autoboot: 3 ”.
234行,测试控制台console的stdin是否有输入,如果有输入,则236行获取该输入的字符。
247-271行,每1s检查一次是否console的stdin有输入,有则停止auto boot;否则每过1s打印一下剩余bootdelay时间。
如果在这期间,我们中断了auto boot,则uboot进入交互式cli界面了,可以使用uboot提供的cli命令。