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 举报,一经查实,本站将立刻删除。