Uboot源码解析

h

基础知识

虚拟地址
大端和小端
MMU
arm7内核

代码获取

官方的移植手册 《i.MX Porting Guide》

编译和反汇编

ARCH = arm
CROSS_COMPILE = arm-linux-gnueabihf-
make mx6ull_14x14_evk_emmc_defconfig
make -j10
arm-linux-gnueabihf-objdump -S u-boot >> u-boot.txt

补充:反汇编主要用于函数入口的准确查询

头文件

头文件里面有asm那么在平台路径下 include/asm/
头文件里面有linux那么在根目录下 include/linux/

生成的头文件

#include <config.h>

include/config.h
这个头文件很重要,编译的时候根据配置动态生成的

#define CONFIG_IMX_CONFIG board/freescale/mx6ull_alpha/imximage.cfg

官方提供的参数
业务流程:判断启动方式、是否启用插件、DCD数据是否使用
SD启动、不使用插件、DCD数据不使用(外部DCD)

#define CONFIG_MX6ULL_EVK_EMMC_REWORK 1

这个重要点因为后续的开发会以官方的开发板为蓝本进行改进,所以此项一定要有(配置里面使能)

#define CONFIG_BOARDDIR board/freescale/mx6ull_alpha

指定板子板级的路径

#include <config_defaults.h>

一些uboot的默认配置:引导内核相关的配置

#include <config_uncmd_spl.h>

SPL是厂商为了节省成本想出来的反人类法子,此处略

#include <configs/mx6ull_alpha.h>

这个就很重要了,用户的板级配置都在这里面

#include <asm/config.h>

arm内核相关

#include <config_fallbacks.h>

备用的宏定义头文件

#include <linux/linkage.h>

linux常用的汇编

补充的头文件

include/generated/autoconf.h
这个头文件是自动生成的

内核初始化

ARM开始格式:复位+7个异常向量地址

启动代码里面没有这部分的驱动,反汇编发现在这里

arch/arm/lib/vectors.S

查看连接脚本发现 arch/arm/cpu/u-boot.lds

*(.vectors)
CPUDIR/start.o (.text*)

所以实际先看的代码应该是:arch/arm/lib/vectors.S 但是这部分比较简单,这里略过

开启虚拟化

多操作系统支持

进入超级模式 略

关闭中断 略

CP15初始化

TLB、icache、Branch array、DSB、ISB、V、Align、BTB

读取CPU的ID以及版本信息 略

lowlevel_init (没有开启)

内部RAM作为堆栈首地址、预留global_data结构体作为存储信息表
注意:这部分是不执行的,没有开启这部分,很显然这是个被废弃的驱动,或者用户可以做点事情

s_init

arch/arm/cpu/armv7/mx6/soc.c
时钟相关的操作,这里略过,注意如果开启 lowlevel_init 那么后面可以使用c语言了
很显然这是个被废弃的驱动

_main

arch/arm/lib/crt0.S

设置堆栈(并没有预留global_data的内存)
common/init/board_init.c
这段代码稍微有些重要,开辟了malloc的内存,最后返回堆栈的首地址

#if defined(CONFIG_SYS_MALLOC_F)
	top -= CONFIG_SYS_MALLOC_F_LEN;
#endif
top = rounddown(top-sizeof(struct global_data), 16);
return top;

bl board_init_f_init_reserve

其中 r0 = top
清除 global_data 的参数,然后多预留了 16字节的内存,而后得到了 malloc 的起始地址了,问题在于没有显示malloc边界的代码
gd->malloc_base = base;

bl board_init_f

其中 r0 = 0
common/board_f.c

// 这段代码清除了标志位和控制台标志,标志着uboot马上就要开始工作了
gd->flags = boot_flags;
gd->have_console = 0;
// 一个初始化函数序列
if (initcall_run_list(init_sequence_f))
	hang();

后续的分析较为复杂这里重开一章

Uboot初始化

注意:此时已经进行了ddr3的初始化,但是一依旧在内部内存里面遨游

setup_mon_len

// 记录uboot的总大小,用于后面重定向
gd->mon_len = (ulong)&__bss_end - (ulong)_start;

initf_malloc

// 这里的操作让malloc完整了
gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN;
// 当前地址在malloc的0地址
gd->malloc_ptr = 0;

initf_console_record

啥也没有做

arch_cpu_init

cpu上电初始化的操作,这里略过

initf_dm

arch_cpu_init_dm

协议驱动模型的初始化,略过

mark_bootstage

标记uboot的状态
bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, “board_init_f”);

board_early_init_f

啥也没有做,略过

timer_init

初始化了一个定时器,也不知道干啥用的,肯能是系统和定时器,这里记录重要的信息即可

gd->arch.tbl = 0;
gd->arch.tbu = 0;

board_postclk_init

// 设置 SOC LDO电压位1.175V
set_ldo_voltage(LDO_SOC, 1175);

get_clocks

资料在文档里面:README.fsl-clk
Enable to call get_clocks() in board_init_f() for non-PPC platforms and PCC 8xx platforms such as TQM866M and TQM885D.

gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK);

init_baud_rate

serial_init

这两个都不用看,代码的最终位置在配置文件里面

console_init_f

// 开启了控制台的标志,此时控制台应该可以使用了
gd->have_console = 1;
// 清除控制台缓存
print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT1_SERIAL);

display_options

这个应该就是控制台打印的第一个字符串了 lib/display_options.c

// 没兴趣看这个字符串在哪里偷偷的初始化的
printf ("\n\n%s\n\n", version_string);

display_text_info

// 这个信息还有点用,只不过不让显示
debug("U-Boot code: %08lX -> %08lX  BSS: -> %08lX\n",
		text_base, bss_start, bss_end);

print_cpuinfo

arch/arm/imx-common/cpu.c
看不懂干什么用的,不过应该不重要,这里贴出重要的代码

// 取出寄存器然后得到一些信息然后返回到 cpurev
cpurev = get_cpu_rev();
struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR;
struct scu_regs *scu = (struct scu_regs *)SCU_BASE_ADDR;
printf("CPU:   Freescale i.MX%s rev%d.%d",
	       get_imx_type((cpurev & 0xFF000) >> 12),
	       (cpurev & 0x000F0) >> 4,
	       (cpurev & 0x0000F) >> 0);
// 打印温度和时钟
printf(" at %dMHz\n", mxc_get_clock(MXC_ARM_CLK) / 1000000);
puts("Extended Commercial temperature grade ");
printf("(%dC to %dC)", minc, maxc);
// 复位原因
printf("Reset cause: %s\n", get_reset_cause());

show_board_info

调用了这个函数 checkboard 这个函数在板级代码里面

if (is_mx6ull_9x9_evk())
	puts("Board: MX6ULL 9x9 EVK\n");
else
	puts("Board: MX6ULL ALPHA\n");

init_func_i2c

初始化所有的i2c并打印提示信息

puts("I2C:   ");
#ifdef CONFIG_SYS_I2C
	i2c_init_all();
#else
	i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
	puts("ready\n");

dram_init

这部分早就初始化了,在这里主要是写入数据用来通知linux

gd->ram_size = imx_ddr_size();

setup_dest_addr

这里面重要的信息挺多

// 这句返回自己
gd->ram_size = board_reserve_ram_top(gd->ram_size);
gd->ram_top = CONFIG_SYS_SDRAM_BASE;
// 这句返回自己
gd->ram_top += get_effective_memsize();
// 这句返回自己
gd->ram_top = board_get_usable_ram_top(gd->mon_len);
gd->relocaddr = gd->ram_top;

reserve_round_4k

预留了4K的程序,也不知道要干什么

gd->relocaddr &= ~(4096 - 1);

reserve_mmu

保存mmu里面的信息,设置好mmu的信息,后续linux使用之

gd->arch.tlb_size = PGTABLE_SIZE;
gd->relocaddr -= gd->arch.tlb_size;
gd->relocaddr &= ~(0x10000 - 1);
gd->arch.tlb_addr = gd->relocaddr;

reserve_trace

reserve_uboot

gd->relocaddr -= gd->mon_len;
gd->relocaddr &= ~(4096 - 1);
gd->start_addr_sp = gd->relocaddr;

reserve_malloc

gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN;

reserve_board

// 这个bd_t我好像漏掉了,刚想起来这个bd是个指针,还需要预留一段内存,之后应该就是uboot的位置了
gd->start_addr_sp -= sizeof(bd_t);
gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t));

setup_machine

reserve_global_data

// 这个是新的gd的位置,紧接着旧的之后,这玩意忘了干啥的了,后续再看吧
gd->start_addr_sp -= sizeof(gd_t);
gd->new_gd = (gd_t *)map_sysmem(gd->start_addr_sp, sizeof(gd_t));

reserve_fdt

保存设备树,貌似目前还跟它没有任何关系

reserve_arch

reserve_stacks

// 保存堆栈信息
gd->start_addr_sp -= 16;
gd->start_addr_sp &= ~0xf;

setup_dram_config

// 初始化ddr的物理地址以及有效内存大小
gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
// gd->ram_size
gd->bd->bi_dram[0].size = get_effective_memsize();

common/memsize.c

return gd->ram_size;

show_dram_config

// 好像少了一个 DRAM:字符串,这个debug打印不出来的呀?略过吧
debug("\nDRAM:  ");
printf ("%lu", n);
if (m) {
	printf (".%ld", m);
}
printf (" %ciB%s", c, s);

display_new_sp

显示新的堆栈

reloc_fdt

设备树重定向,跟她没关系这部分略过

#define GD_FLG_SKIP_RELOC	0x00800	/* Don't relocate */

setup_reloc

启动重定向

// 设置重定向的偏移地址
gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
// 清除新的gd的位置,指针的指针指向自己
memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));

NULL

这个函数一定会让函数从调用这返回,那么返回之前调用的位置
bl board_init_f
后面就是uboot的重定向的代码了,这部分还需知识补充
NULL前面的初始化函数阵列里面有重定向代码但是并不执行那个,而是crt0.S里面的初始化代码(只有ARM才这样)

重定向准备

下列在内存的位置没有顺序(board_init_f之后的堆栈设置)

DDR3顶部地址

CONFIG_SYS_SDRAM_BASE = MMDC0_ARB_BASE_ADDR = 0x80000000

获得顶部地址

gd->ram_top = CONFIG_SYS_SDRAM_BASE;
gd->relocaddr = gd->ram_top;

去掉TLB页表 16K

#define PGTABLE_SIZE		(4096 * 4)
gd->arch.tlb_addr = gd->relocaddr = PGTABLE_SIZE = (4096 * 4);

去掉Uboot

gd->relocaddr -= gd->mon_len;
gd->relocaddr &= ~(4096 - 1);
gd->start_addr_sp = gd->relocaddr;

去掉malloc—4K

gd->start_addr_sp = gd->relocaddr;
gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN(4096);

去掉bd变量

gd->start_addr_sp -= sizeof(bd_t);

去掉新的gd变量

gd->new_gd = (gd_t *)map_sysmem(gd->start_addr_sp, sizeof(gd_t));

去掉16字节

gd->start_addr_sp -= 16;
gd->start_addr_sp &= ~0xf;

最终的sp

gd->start_addr_sp

uboot代码顺序

查看链接脚本即可:arch/arm/cpu/u-boot.lds
.txt
.vector
start.o
*.txt
.rel_dyn
uboot对全局变量以及函数的访问都是通过相对地址进行调用
格式固定:8个字节为一组,高4个字节为固定的0x17表示存储全局变量,不是则无效
.bss
位初始化变量
查看可执行文件代码
arm-linux-gnueabihf-size main

uboot重定向

// r9 就是 gd
#define DECLARE_GLOBAL_DATA_PTR		register volatile gd_t *gd asm ("r9")

// sp = gd->start_addr_sp
ldr	sp, [r9, #GD_START_ADDR_SP]
bic	sp, sp, #7
// r9 = gd->bd
ldr	r9, [r9, #GD_BD]
// new GD is below bd
sub	r9, r9, #GD_SIZE
// 获取绝对地址—链接地址
adr	lr, here
// r0 = gd->reloc_off 这个是重定向的偏移地址
ldr	r0, [r9, #GD_RELOC_OFF]
// 新的返回地址 ddr3 中
add	lr, lr, r0
// r0 = gd->relocaddr 这个是代码的重定向起始地址
ldr	r0, [r9, #GD_RELOCADDR]
// 开始重定向:有了目标地址、偏移地址
b	relocate_code

ENTRY(relocate_code)

arch/arm/lib/relocate.S

// r1是原uboot地址
ldr	r1, =__image_copy_start
// r4 = gd->relocaddr - __image_copy_start
subs	r4, r0, r1
// 如果 r0 = r1 那么已经重定向了
beq	relocate_done
// r2 = 原uboot结束位置
ldr	r2, =__image_copy_end
// 这个循环就把代码拷贝完毕
copy_loop:
	ldmia	r1!, {r10-r11}
	stmia	r0!, {r10-r11}
	cmp	r1, r2
	blo	copy_loop
// 取出rel段起始和结束
ldr	r2, =__rel_dyn_start
ldr	r3, =__rel_dyn_end
// 读出8个字节数据其中 r1 为低字节 r0 为高字节
// 如果 r1 = 23 那么为有效,重定向后的这个值需要加上偏移 r0
// r0 + 物理偏移 = r0
fixloop:
	ldmia	r2!, {r0-r1}
	and	r1, r1, #0xff
	cmp	r1, #23
	bne	fixnext
// 找到这个地址的值然后+偏移,之后回写到这个地址
add	r0, r0, r4
ldr	r1, [r0]
add	r1, r1, r4
str	r1, [r0]
// 如果是无效段,如果没有结束那么继续
cmp	r2, r3
blo	fixloop
// icache无效
mcr	p15, 0, r0, c7, c7, 0
// DSB
mcr	p15, 0, r0, c7, c10, 4
// 代码重定向完毕
#ifdef __ARM_ARCH_4__
	mov	pc, lr
#else
	bx	lr
#endif

代码结束会回到这个位置
arch/arm/lib/crt0.S

adr	lr, here
here:
	bl	relocate_vectors

bl relocate_vectors
arch/arm/lib/relocate.S

// 这三句代码非常重要,异常向量地址设置在重定向后的代码里面
ldr	r0, [r9, #GD_RELOCADDR]
ldr	r1, =V7M_SCB_BASE
str	r0, [r1, V7M_SCB_VTOR]
ldr	r0, [r9, #GD_RELOCADDR]
mcr     p15, 0, r0, c12, c0, 0
bx	lr

bl c_runtime_cpu_setup
arch/arm/cpu/armv7/start.S

// invalid cache DSB ISB
mcr	p15, 0, r0, c7, c5, 0
mcr     p15, 0, r0, c7, c10, 4
mcr     p15, 0, r0, c7, c5, 4
bx	lr

bl spl_relocate_stack_gd

// 清除bss段代码
ldr	r0, =__bss_start
ldr	r1, =__bss_end
mov	r2, #0x00000000
clbss_l:cmp	r0, r1
	strlo	r2, [r0]
	addlo	r0, r0, #4
	blo	clbss_l

开启LED灯

bl coloured_LED_init
bl red_led_on

board_init_r

// dest_addr uboot的最后一个阶段了
mov     r0, r9
ldr	r1, [r9, #GD_RELOCADDR]
ldr	pc, =board_init_r

板级第二阶段初始化

common/board_r.c
void board_init_r(gd_t *new_gd, ulong dest_addr)
主要是下面的初始化序列
init_fnc_t init_sequence_r[];

initr_trace

啥也没有做

initr_reloc

// 更新下重定向完毕的标志
gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT;

initr_caches

使能了缓存功能

initr_reloc_global_data

// uboot占用的flash大小
monitor_flash_len = _end - __image_copy_start;

initr_barrier

啥也没有做

initr_malloc

// 获取malloc的大小并初始化malloc功能
malloc_start = gd->relocaddr - TOTAL_MALLOC_LEN;
mem_malloc_init((ulong)map_sysmem(malloc_start, TOTAL_MALLOC_LEN),
		TOTAL_MALLOC_LEN);

initr_console_record

啥也没有做

bootstage_relocate

啥也没有做

initr_dm

// 这个模型目前还不知道干啥用的,后续可以了解下
gd->dm_root_f = gd->dm_root;
gd->dm_root = NULL;

initr_bootstage

标记一下当前阶段

bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r");

board_init

// 设置板级启动参数
gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;
// 启动网络纠错功能(外部因素)
setup_i2c(0, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info1);
// 启动网络纠错(芯片自带的功能)
setup_fec(CONFIG_FEC_ENET_DEV);
setup_usb();
board_qspi_init();

stdio_init_tables

// 初始化了这个设备列表 devs.list 简单的链表初始化
INIT_LIST_HEAD(&(devs.list));

initr_serial

里面有一大陀的初始化函数反汇编查看,都是空函数,这里只需要看这个函数

// 串口配置文件里面,包括串口列表都是在前面初始化
serial_assign(default_serial_console()->name);

initr_announce

power_init_board

initr_mmc

// 打印MMC内存的头
puts("MMC:   ");
// 后面的3段都是第一段的内部驱动,初始化了mmc,此部分略看
mmc_initialize(gd->bd);
INIT_LIST_HEAD (&mmc_devices);
ret = mmc_probe(bis);
do_preinit();

initr_env

这个初始化环境变量有点意思,好多默认参数都在这里面,下面是摘抄

// 环境变量的初始化,必然涉及默认环境变量的初始化
env_relocate();
if (gd->env_valid == 0)
bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM);
set_default_env("!bad CRC");
// 这句便是我要找的默认环境变量 include/env_default.h 好多重要信息都在这里面
if (himport_r(&env_htab, (char *)default_environment,
			sizeof(default_environment), '\0', flags, 0,
			0, NULL) == 0)
// 标记环境变量初始化完成
gd->flags |= GD_FLG_ENV_READY;
// 环境变量都存储在这个表里面
struct hsearch_data env_htab = {
	.change_ok = env_flags_validate,
};
// 然后获取一个环境变量,在默认环境变量里面找到它
load_addr = getenv_ulong("loadaddr", 16, load_addr);
"loadaddr="	__stringify(CONFIG_LOADADDR)	"\0"
// 这个变量很重要记住它
load_addr

initr_secondary_cpu

第二阶段的cpu初始化,实际上啥也没有做

stdio_add_devices

标准输入输出的设备添加,上面设备初始化,这里应该是真正的初始化

// 阴魂不散的i2c
i2c_init_all();

// 这个初始化重要些lcd的初始化就在这里
drv_video_init ();
// 下面的函数从环境变量获取信息
// 然后根据lcd的mode获取变量的参数,注意是所有的lcd初始化参数,写在了uboot的配置文件里面
video_init()
drivers/video/mxsfb.c
void *video_hw_init(void)
penv = getenv("videomode");
bpp = video_get_params(&mode, penv);
sprintf(panel.modeIdent, "%dx%dx%d",
		mode.xres, mode.yres, bpp);
printf("%s\n", panel.modeIdent);
mxs_lcd_init(&panel, &mode, bpp);
// 这是最终的lcd初始化函数,底层的初始化就在这里面了,但是没有调用到板级的初始化驱动,感觉这里初始化了一部分
mxs_lcd_init(&panel, &mode, bpp);
// 把标准输入输出放进到设备里面

drv_system_init

这次灌入的是标准设备的串行输入输出,只是一个串口当前的串口设备,这个应该是控制台了

serial_stdio_init

再次把所有串口设备灌入了设备之中,这是第二次?是第二次,但是只是存储而已

initr_jumptable

// 只是做了一个这样的事
gd->jt = malloc(sizeof(struct jt_funcs));

console_init_r

这是最后的控制台初始化驱动

//  设置跳转之后的打印驱动
gd->jt->getc  = serial_getc;
gd->jt->tstc  = serial_tstc;
gd->jt->putc  = serial_putc;
gd->jt->puts  = serial_puts;
gd->jt->printf = serial_printf;
// 获取环境变量设置的三个重要参数
stdinname  = getenv("stdin");
stdoutname = getenv("stdout");
stderrname = getenv("stderr");
inputdev  = search_device(DEV_FLAGS_INPUT,  stdinname);
outputdev = search_device(DEV_FLAGS_OUTPUT, stdoutname);
errdev    = search_device(DEV_FLAGS_OUTPUT, stderrname);
// 标记设备初始化完成
gd->flags |= GD_FLG_DEVINIT;

interrupt_init

中断的初始化只需要初始化中断堆栈指针即可,啥也不用做
IRQ_STACK_START_IN = gd->irq_sp + 8;

initr_enable_interrupts

使能了中断

enable_interrupts();

initr_ethaddr

初始化网络,这个很重要,之后就可以用网络了

// 这个环境变量应该是没有的
bd_t *bd = gd->bd;
eth_getenv_enetaddr("ethaddr", bd->bi_enetaddr);

board_late_init

// 设置环境变量—不重要
setenv("board_name", "EVK");
setenv("board_rev", "14X14");
board_late_mmc_env_init();
// 初始化看门狗
set_wdog_reset((struct wdog_regs *)WDOG1_BASE_ADDR);

这里面把imx默认的sd卡和参数都设置好了
arch/arm/include/asm/arch-mx7/imx-regs.h
#define BOOT_TYPE_SD		0x1

// 设置mmc设备号并保存
setenv_ulong("mmcdev", dev_no);
sprintf(mmcblk, "/dev/mmcblk%dp2 rootwait rw",
	mmc_map_to_kernel_blk(dev_no));
setenv("mmcroot", mmcblk);
// 这一句选择默认的sd卡设备作为启动设备:mmc dev 1
sprintf(cmd, "mmc dev %d", dev_no);
run_command(cmd, 0);

initr_net

puts("Net:   ");

// net/eth_legacy.c
eth_initialize();
// 下面的驱动非常重要
eth_common_init();
// 下面这个驱动会按照SMSC的方式进行初始化
#ifdef CONFIG_PHYLIB
phy_init();
#endif
// SMSC初始化
#ifdef CONFIG_PHY_SMSC
phy_smsc_init();
#endif
// SMSC支持的驱动都注册好了,注册的时候也会让 eth_devices 赋值
phy_register(&lan8710_driver);
phy_register(&lan911x_driver);
phy_register(&lan8700_driver);
phy_register(&lan8740_driver);

// 而后获取环境变量,有了这个就要开始初始化我们的网卡了
#define CONFIG_ETHPRIME			"FEC"
#ifdef	CONFIG_ETHPRIME
"ethprime="	CONFIG_ETHPRIME			"\0"
#endif

// 终于遇到了板级的初始化代码了 board/freescale/mx6ull_alpha/mx6ull_alpha.c
// 经过了这一番操作,网络应该称初始化好了,只不过不支持 lan8720 但是能用
if (board_eth_init != __def_eth_init)
int board_eth_init(bd_t *bis)

run_main_loop

这个是uboot的最后一阶段的代码了,摘抄之后贴录

// common/main.c void main_loop(void)
for (;;)
	main_loop();

// 标记阶段
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
// 进行命令循环里面超时会返回,实际上这个啥也没有做
cli_init();
u_boot_hush_start();
// 这个函数也是什么都没有做
run_preboot_environment_command();

延时进程

// 这个驱动会返回一个命令,比较重要
s = bootdelay_process();

// common/autoboot.c
const char *bootdelay_process(void)
s = getenv("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
// 打印启动信息
printf("Normal Boot\n");
// 获取启动的命令,这个命令执行,如果超时
s = getenv("bootcmd");
#ifdef	CONFIG_BOOTCOMMAND
	"bootcmd="	CONFIG_BOOTCOMMAND		"\0"
#endif
// 这个环境变量我只知道大概的意思
// 运行设备树,如果搜索到了mmc,然后判断是否有启动脚本(一般不用),否则就使用命令加载
// 先加载内核镜像文件,之后运行mmc启动,否则就使用网络启动
#define CONFIG_BOOTCOMMAND \
	   "run findfdt;" \
	   "mmc dev ${mmcdev};" \
	   "mmc dev ${mmcdev}; if mmc rescan; then " \
		   "if run loadbootscript; then " \
			   "run bootscript; " \
		   "else " \
			   "if run loadimage; then " \
				   "run mmcboot; " \
			   "else run netboot; " \
			   "fi; " \
		   "fi; " \
	   "else run netboot; fi"
#endif
// 这个变量没有初始化过应该为0,实际上这个里面啥也没有做
process_fdt_options(gd->fdt_blob);
// 保存延时的时间,之后返回bootcmd命令
stored_bootdelay = bootdelay;

命令实现

// 这个函数是一个倒计时的函数
autoboot_command(s);
// 判断是否有字符接收,如果有那么结束自举
if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay))
// 如果没有按钮按下,会有自举函数执行
run_command_list(s, -1, 0);

// 进入命令循环 common/cli.c
cli_loop();
// 分析命令驱动 hash 比较麻烦不分析
parse_file_outer();
// 分析命令驱动 顺序
cli_simple_loop();

// 上一个命令
static char lastcommand[CONFIG_SYS_CBSIZE + 1] = { 0, };
// 读取一行
len = cli_readline(CONFIG_SYS_PROMPT);
// 运行命令
rc = run_command_repeatable(lastcommand, flag);
// 运行命令 common/cli_simple.c
return cli_simple_run_command(cmd, flag);
// 最终命令的运行驱动 common/command.c
if (cmd_process(flag, argc, argv, &repeatable, NULL))
// 寻找命令的驱动,这个很重要
cmdtp = find_cmd(argv[0]);

// 这个非常重要 include/command.h
// 通过代码来查找命令所在的结构体
// 下面会重新开一章
typedef struct cmd_tbl_s	cmd_tbl_t;
cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
const int len = ll_entry_count(cmd_tbl_t, cmd);
return find_cmd_tbl(cmd, start, len);

寻找命令—起始位置

// 获取起始的结构体位置
cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
// 命令都存放在了结构体里面,一个特殊的段
#define ll_entry_start(_type, _list)					\
({									\
	static char start[0] __aligned(4) __attribute__((unused,	\
		section(".u_boot_list_2_"#_list"_1")));			\
	(_type *)&start;						\
})
// 查看链接脚本发现这个段
. = ALIGN(4);
	.u_boot_list : {
		KEEP(*(SORT(.u_boot_list*)));
	}
// 计算命令个数:总大小处以结构体大小就是cmd个数
#define ll_entry_count(_type, _list)					\
	({								\
		_type *start = ll_entry_start(_type, _list);		\
		_type *end = ll_entry_end(_type, _list);		\
		unsigned int _ll_result = end - start;			\
		_ll_result;						\
	})
// 这个函数直接返回命令
return find_cmd_tbl(cmd, start, len);
// 比较名字和字符串大小就找到了这个驱动
if (strncmp(cmd, cmdtp->name, len) == 0)
	if (len == strlen(cmdtp->name))

至此Uboot的所有重要代码分析完毕,后面准备对命令进行一波分析

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340