U-boot源码分析
一、脚本
当前配置
//.config
CONFIG_SYS_ARCH="arm"
CONFIG_SYS_CPU="armv8"
CONFIG_SYS_SOC="tegra210"
CONFIG_TEGRA210=y
CONFIG_SYS_VENDOR="nvidia"
CONFIG_SYS_BOARD="p3450-0000" //对应 arch/arm/dts/tegra210-p3450-0000.dts
CONFIG_TARGET_P3450_0000=y
CONFIG_TEGRA=y
CONFIG_TEGRA_GP_PADCTRL=y
CONFIG_TEGRA_PINCTRL=y
CONFIG_SPL_SERIAL_SUPPORT=y //串口
CONFIG_DEFAULT_DEVICE_TREE="tegra210-p3450-0000" //默认的设备树
链接脚本
找到入口,第一个运行的代码
//u-boot.lds
OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start) //入口地址
SECTIONS
{
. = 0x00000000;
. = ALIGN(8);
.text :
{
*(.__image_copy_start)
arch/arm/cpu/armv8/start.o (.text*) //第一行代码所在文件
}
......
}
编译映射图
确认那些函数文件,最终编译到镜像里
//u-boot.map
目的 //确认那些函数文件,最终编译到镜像里
arch/arm/cpu/armv8/start.o(.text*)
.text 0x0000000080080000 0x1c0 arch/arm/cpu/armv8/start.o
0x0000000080080000 _start
0x0000000080080040 _TEXT_BASE
0x0000000080080064 save_boot_params_ret
0x0000000080080140 lowlevel_init
0x000000008008016c c_runtime_cpu_setup
.text 0x0000000080081000 0x390 arch/arm/cpu/armv8/built-in.o
0x0000000080081000 vectors
二、入口
//arch/arm/cpu/armv8/start.S 入口
.globl _start //声明_start为全局标号
_start:
b reset //上电(或复位)执行的第一条指令,
.align 3 // 8字节对齐(2^3 )
.globl _TEXT_BASE //声明_TEXT_BASE(代码段的基地址)为全局标号
_TEXT_BASE:
.quad CONFIG_SYS_TEXT_BASE /* 分配8字节空间 存放CONFIG_SYS_TEXT_BASE 按ctrl+] 可跳转到定义处 , 按ctrl+o可返回
5 215 include/generated/autoconf.h <<CONFIG_SYS_TEXT_BASE>>
#define CONFIG_SYS_TEXT_BASE 0x80080000 系统代码断的基地址
*/
reset:
/* Allow the board to save important registers */
b save_boot_params
#ifdef CONFIG_SYS_RESET_SCTRL //条件编译(如果CONFIG_SYS_RESET_SCTRL 有定义,则把里面指令编译进来)
bl reset_sctrl //调用reset_sctrl函数
#endif
adr x0, vectors //读标号(地址)vectors到x0,小范围读取,vectors代表异常向量表的首地址 (按ctrl+]跳转到定义处)
/*
* Could be EL3/EL2/EL1, Initial State:
* Little Endian, MMU Disabled, i/dCache Disabled
*/
switch_el x1, 3f, 2f, 1f /* 调用宏函数 switch_el 根据异常级别,跳到对应标号处 (光标移到switch_el上 -> 按ctrl+]跳转到定义处)
可在官方寄存器列表中搜 CurrentEL 见 https://developer.arm.com/documentation/ddi0595/2021-09?lang=en
.macro switch_el, xreg, el3_label, el2_label, el1_label
mrs \xreg, CurrentEL bit[3:2]: 00(EL0) 01(EL1) 10(EL2) 11(EL3)
cmp \xreg, 0xc
b.eq \el3_label
cmp \xreg, 0x8
b.eq \el2_label
cmp \xreg, 0x4
b.eq \el1_label
.endm
arch/arm/include/asm/macro.h
*/
3: set_vbar vbar_el3, x0
mrs x0, scr_el3
orr x0, x0, #0xf /* SCR_EL3.NS|IRQ|FIQ|EA */
msr scr_el3, x0
msr cptr_el3, xzr /* Enable FP/SIMD */
b 0f
2: set_vbar vbar_el2, x0
mov x0, #0x33ff
msr cptr_el2, x0 /* Enable FP/SIMD */
b 0f
1: set_vbar vbar_el1, x0 //把X0值写入vbar_el1(完成设置异常向量表基地址)
//Armv8-A 架构寄存器 https://developer.arm.com/documentation/ddi0595/2021-09?lang=en中 可查看VBAR_EL1的详细描述
mov x0, #3 << 20
msr cpacr_el1, x0 /* Enable FP/SIMD */
0:
/* Apply ARM core specific erratas */
bl apply_core_errata // 光标移到 apply_core_errata上 -> 按n键,
bl lowlevel_init //低级别初始化(处理器特有的)
master_cpu:
bl _main //跳转到C语言的main函数(汇编阶段结束)
//注:在u-boot.map中找_main -> arch/arm/lib/built-in.o -> 在arch/arm/lib文件夹下搜索_main找到
// ->arch/arm/lib/crt0_64.S
// 如果还找不到_main,可用qemu 放在,模拟运行调试跟踪
三、异常向量表
//arch/arm/cpu/armv8/exceptions.S
.align 11 //2^11=2048 整个异常向量表 2K对齐 -> 通过对齐,实现向量表空间的预留
//16个异常 ,每个异常32条指令 16*32*4=2048
.globl vectors
vectors: //标号地址0x1000(见u-boot.map)
//对齐 2^7= 1000 0000 =128字节 128/4= 32条指令的空间 (64位机指令是32位的,只是寄存器空间是64位的)
//每个异常向量,占32条指令空间。
.align 7 /*1: 同步异常 Current EL Synchronous Thread */
stp x29, x30, [sp, #-16]! //入栈:把x29, x30 存储到sp-16指向的空间后,sp自减16 (因寄存器是8字节,栈是向下生长故是 -16)
//类似前索引: *(sp-16) = x29,x30 sp=sp-16 (!使得sp能自更新) 把 x29,x30看成整体
//stp只支持2个寄存器,代替了复杂的stmfd (64位汇编,取消了批量操作指令)
bl _exception_entry //进入异常(进栈保存现场)
bl do_bad_sync //同步异常处理程序
b exception_exit //退出异常(出栈恢复现场)
/*
* Save (most of) the GP registers to the stack frame.
* This is the first part of the shared routine called into from all entries.
*/
_exception_entry:
//保存现场:把x1~x28 入栈
stp x27, x28, [sp, #-16]! //x27, x28入栈
stp x25, x26, [sp, #-16]! //x25, x26入栈
stp x23, x24, [sp, #-16]!
stp x21, x22, [sp, #-16]!
stp x19, x20, [sp, #-16]!
stp x17, x18, [sp, #-16]!
stp x15, x16, [sp, #-16]!
stp x13, x14, [sp, #-16]!
stp x11, x12, [sp, #-16]!
stp x9, x10, [sp, #-16]!
stp x7, x8, [sp, #-16]!
stp x5, x6, [sp, #-16]!
stp x3, x4, [sp, #-16]!
stp x1, x2, [sp, #-16]!
b _save_el_regs /* jump to the second part */ //报错特殊寄存器(ELR ESR
.align 7 /*2:中断异常 Current EL IRQ Thread */
stp x29, x30, [sp, #-16]!
bl _exception_entry
bl do_bad_irq
b exception_exit
/*
* Save exception specific context: ESR and ELR, for all exception levels.
* This is the second part of the shared routine called into from all entries.
*/
_save_el_regs:
/* Could be running at EL3/EL2/EL1 */
switch_el x11, 3f, 2f, 1f
3: mrs x1, esr_el3
mrs x2, elr_el3
b 0f
2: mrs x1, esr_el2
mrs x2, elr_el2
b 0f
1: mrs x1, esr_el1 //esr 记录异常产生的原因
mrs x2, elr_el1 //elr 记录异常的链接地址(类似lr)
0:
stp x2, x0, [sp, #-16]!
mov x0, sp
ret
.align 7 /*3:快速中断异常 Current EL FIQ Thread */
stp x29, x30, [sp, #-16]!
bl _exception_entry
bl do_bad_fiq
/* falling through to _exception_exit */
/*
* Restore the exception return address, for all exception levels.
* This is the first part of the shared routine called into from all entries.
*/
exception_exit: //出栈恢复数据(特殊机器esr elr和x0~x30通用的寄存器)
ldp x2, x0, [sp],#16
switch_el x11, 3f, 2f, 1f
3: msr elr_el3, x2
b _restore_regs
2: msr elr_el2, x2
b _restore_regs
1: msr elr_el1, x2
b _restore_regs /* jump to the second part */
.align 7 /*4:系统错误异常 Current EL Error Thread */
stp x29, x30, [sp, #-16]!
bl _exception_entry
bl do_bad_error
b exception_exit
/*
* Restore the general purpose registers from the exception stack, then return.
* This is the second part of the shared routine called into from all entries.
*/
_restore_regs:
//恢复现场:出栈,恢复x1~x30的内容
ldp x1, x2, [sp],#16
ldp x3, x4, [sp],#16
ldp x5, x6, [sp],#16
ldp x7, x8, [sp],#16
ldp x9, x10, [sp],#16
ldp x11, x12, [sp],#16
ldp x13, x14, [sp],#16
ldp x15, x16, [sp],#16
ldp x17, x18, [sp],#16
ldp x19, x20, [sp],#16
ldp x21, x22, [sp],#16
ldp x23, x24, [sp],#16
ldp x25, x26, [sp],#16
ldp x27, x28, [sp],#16
ldp x29, x30, [sp],#16
eret
.align 7 /*5:同步异常 Current EL (SP_ELx) Synchronous Handler */
stp x29, x30, [sp, #-16]!
bl _exception_entry
bl do_sync
b exception_exit
.align 7 /*6:中断异常 Current EL (SP_ELx) IRQ Handler */
stp x29, x30, [sp, #-16]!
bl _exception_entry
bl do_irq
b exception_exit
.align 7 /*7:快速中断异常 Current EL (SP_ELx) FIQ Handler */
stp x29, x30, [sp, #-16]!
bl _exception_entry
bl do_fiq
b exception_exit
.align 7 /*8:系统错误异常 Current EL (SP_ELx) Error Handler */
stp x29, x30, [sp, #-16]!
bl _exception_entry
bl do_error
b exception_exit
四、准备C语言的执行环境
//arch/arm/lib/crt0_64.S 准备C语言的执行环境(c runtiime environment)
#include <config.h>
#include <asm-offsets.h>
#include <asm/macro.h>
#include <linux/linkage.h>
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if defined(CONFIG_TPL_BUILD) && defined(CONFIG_TPL_NEEDS_SEPARATE_STACK)
ldr x0, =(CONFIG_TPL_STACK)
#elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr x0, =(CONFIG_SPL_STACK)
#elif defined(CONFIG_INIT_SP_RELATIVE)
adr x0, __bss_start
add x0, x0, #CONFIG_SYS_INIT_SP_BSS_OFFSET
#else
ldr x0, =(CONFIG_SYS_INIT_SP_ADDR) // 栈地址 初始化
#endif
bic sp, x0, #0xf /* 16-byte alignment for ABI compliance */
mov x0, sp
bl board_init_f_alloc_reserve // 预留空间分配( ctrl+] -> 跳转进common/init/board_init.c)
mov sp, x0
/* set up gd here, outside any C code */
mov x18, x0
bl board_init_f_init_reserve //板子预留空间初始化(堆 栈 全局数据区)
{//common/init/board_init.c 板子预留空间初始化(用后面的点灯法,可确认运行到这里)
-------------
| 栈 | <--stack_pointer
| Stack |
-------------
| |
| GD | //全局数据区
-------------
| 堆 |
| mallock | <--mallock_pointer
-------------
void board_init_f_init_reserve(ulong base)
{
struct global_data *gd_ptr;
/*
* clear GD entirely and set it up.
* Use gd_ptr, as gd may not be properly set yet.
*/
gd_ptr = (struct global_data *)base;
/* zero the area */
memset(gd_ptr, '\0', sizeof(*gd));
/* set GD unless architecture did it already */
#if !defined(CONFIG_ARM)
arch_setup_gd(gd_ptr);
#endif
if (CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE))
board_init_f_init_stack_protection_addr(base);
/* next alloc will be higher by one GD plus 16-byte alignment */
base += roundup(sizeof(struct global_data), 16);
/*
* record early malloc arena start.
* Use gd as it is now properly set for all architectures.
*/
#if CONFIG_VAL(SYS_MALLOC_F_LEN)
/* go down one 'early malloc arena' */
gd->malloc_base = base;
#endif
if (CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE))
board_init_f_init_stack_protection();
}
}
mov x0, #0
bl board_init_f //前置的板级初始化 (重定位前 定时器 时钟 串口 内存)
//ctrl+] 跳转 有多个,选哪一个
//u-boot.map 里搜,发现是在common目录下
//vscode下在 common目录下找发现有 common/board_f.c 和common/spl/spl.c
//再查看common 下.o 文件生成,发现只有board_f.c有生成board_f.o ,故确定编译的是board_f.c
//如果都生成.o 则用加打印跟踪方式,或点灯法
#if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(FRAMEWORK)
/*
* Clear BSS section
*/
ldr x0, =__bss_start /* this is auto-relocated! */
ldr x1, =__bss_end /* this is auto-relocated! */
clear_loop:
str xzr, [x0], #8
cmp x0, x1
b.lo clear_loop
/* call board_init_r(gd_t *id, ulong dest_addr) */
mov x0, x18 /* gd_t */
ldr x1, [x18, #GD_RELOCADDR] /* dest_addr */
b board_init_r //重定位后的板级初始化 见 Ctrl+] -> common/board_r.c
五、前置板级初始化(重定位前)
//common/board_f.c
void board_init_f(ulong boot_flags)
{
gd->flags = boot_flags;
gd->have_console = 0;
if (initcall_run_list(init_sequence_f)) //通过函数指针数组 完成序列初始化
hang();
}
static const init_fnc_t init_sequence_f[] = { //前置的初始化函数指针数组
setup_mon_len,
#ifdef CONFIG_OF_CONTROL //ctrl+] 发现有定义
//FDT,flatted device tree,扁平设备树
//uboot最终将其device tree编译成dtb文件,使用过程中通过解析该dtb来获取板级设备信息。
//uboot的dtb和kernel中的dtb是一致的
fdtdec_setup, //设备树的安装:获取dtb的地址,并且验证dtb的合法性
//-> reserve_fdt 和 reloc_fdt
//dtb镜像会添加到u-boot.bin的后面见 -> board_fdt_blob_setup -> fdt_blob = (ulong *)&_end;
#endif
#ifdef CONFIG_TRACE_EARLY
trace_early_init,
#endif
initf_malloc,
log_init,
initf_bootstage, /* uses its own timer, so does not need DM */
#ifdef CONFIG_BLOBLIST
bloblist_init,
#endif
setup_spl_handoff,
initf_console_record,
#if defined(CONFIG_HAVE_FSP)
arch_fsp_init,
#endif
arch_cpu_init, /* basic arch cpu dependent setup */
mach_cpu_init, /* SoC/machine dependent CPU setup */
initf_dm, //设备模型初始化: 初始化驱动树和uclass树,然后从平台数据和FDT中扫描和绑定可用的设备
arch_cpu_init_dm,
#if defined(CONFIG_BOARD_EARLY_INIT_F) // .config 中搜,发现有使用该配置项的
board_early_init_f, //板子的早期初始化(u-boot.map中搜发现在arch/arm/mach-tegra/中) -> board2.c
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
/* get CPU and bus clocks according to the environment variable */
get_clocks, /* get CPU and bus clocks (etc.) */
#endif
#if !defined(CONFIG_M68K)
timer_init, /* 定时器初始化 initialize timer */
#endif
#if defined(CONFIG_BOARD_POSTCLK_INIT)
board_postclk_init,
#endif
env_init, /* u-boot环境变量的初始化 initialize environment */
init_baud_rate, /*初始波特率 115200 initialze baudrate settings */
serial_init, /*串口初始化设置 serial communications setup */
//追踪:ctrl + ] 选哪一个(看最终编译的)
// u-boot.map -> drivers/serial/.built-in.o.cmd -> serial-uclass.o ns16550.o
// serial-uclass.c 会在前面 initf_dm中扫描建立串口设备模型
console_init_f, /* stage 1 init of console */
display_options, /* say that we are here */
display_text_info, /* show debugging info if required */
checkcpu,
#if defined(CONFIG_SYSRESET)
print_resetinfo,
#endif
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DTB_RESELECT)
embedded_dtb_select,
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
show_board_info,
#endif
INIT_FUNC_WATCHDOG_INIT
#if defined(CONFIG_MISC_INIT_F)
misc_init_f,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_SYS_I2C)
init_func_i2c,
#endif
#if defined(CONFIG_VID) && !defined(CONFIG_SPL)
init_func_vid,
#endif
announce_dram_init,
dram_init, /*内存初始化 configure available RAM banks */
#ifdef CONFIG_POST
post_init_f,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_SYS_DRAM_TEST)
testdram,
#endif /* CONFIG_SYS_DRAM_TEST */
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_POST
init_post,
#endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_PRAM
reserve_pram,
#endif
reserve_round_4k,
#ifdef CONFIG_ARM
reserve_mmu,
#endif
reserve_fdt, //为dtb分配新的内存地址空间
#if defined(CONFIG_M68K) || defined(CONFIG_MIPS) || defined(CONFIG_PPC) || \
defined(CONFIG_SH)
setup_board_part1,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K)
INIT_FUNC_WATCHDOG_RESET
setup_board_part2,
#endif
display_new_sp,
#ifdef CONFIG_OF_BOARD_FIXUP
fix_fdt,
#endif
INIT_FUNC_WATCHDOG_RESET
reloc_fdt, //重定位设备树
reloc_bootstage,
reloc_bloblist,
setup_reloc, //重定位的安装
#if defined(CONFIG_X86) || defined(CONFIG_ARC)
copy_uboot_to_ram,
do_elf_reloc_fixups,
#endif
clear_bss,
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
!CONFIG_IS_ENABLED(X86_64)
jump_to_copy,
#endif
NULL,
};
static int initf_dm(void) //设备树及其驱动模块初始化
{
#if defined(CONFIG_DM) && CONFIG_VAL(SYS_MALLOC_F_LEN) //ctrl+] 发现CONFIG_DM 是有定义的
int ret;
bootstage_start(BOOTSTATE_ID_ACCUM_DM_F, "dm_f");
ret = dm_init_and_scan(true); //扫描设备树建立设备模型
bootstage_accum(BOOTSTATE_ID_ACCUM_DM_F);
if (ret)
return ret;
#endif
#ifdef CONFIG_TIMER_EARLY
ret = dm_timer_init();
if (ret)
return ret;
#endif
return 0;
}
static int setup_reloc(void) //重定位
{
if (gd->flags & GD_FLG_SKIP_RELOC) {
debug("Skipping relocation due to flag\n");
return 0;
}
#ifdef CONFIG_SYS_TEXT_BASE
#ifdef ARM
gd->reloc_off = gd->relocaddr - (unsigned long)__image_copy_start;
#elif defined(CONFIG_M68K)
/*
* On all ColdFire arch cpu, monitor code starts always
* just after the default vector table location, so at 0x400
*/
gd->reloc_off = gd->relocaddr - (CONFIG_SYS_TEXT_BASE + 0x400);
#elif !defined(CONFIG_SANDBOX)
gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
#endif
#endif
memcpy(gd->new_gd, (char *)gd, sizeof(gd_t)); //数据搬移,重定位
debug("Relocation Offset is: %08lx\n", gd->reloc_off);
debug("Relocating to %08lx, new gd at %08lx, sp at %08lx\n",
gd->relocaddr, (ulong)map_to_sysmem(gd->new_gd),
gd->start_addr_sp);
return 0;
}
六、后置板级初始化(重定位后)
//common/board_r.c
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
if (initcall_run_list(init_sequence_r))
hang();
/* NOTREACHED - run_main_loop() does not return */
hang();
}
static init_fnc_t init_sequence_r[] = { //后置的初始化列表
initr_trace,
initr_reloc,
/* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARM
initr_caches,
/* Note: For Freescale LS2 SoCs, new MMU table is created in DDR.
* A temporary mapping of IFC high region is since removed,
* so environmental variables in NOR flash is not available
* until board_init() is called below to remap IFC to high
* region.
*/
#endif
initr_reloc_global_data,
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
initr_unlock_ram_in_cache,
#endif
initr_barrier,
initr_malloc,
log_init,
initr_bootstage, /* Needs malloc() but has its own timer */
initr_console_record,
#ifdef CONFIG_SYS_NONCACHED_MEMORY
initr_noncached,
#endif
#ifdef CONFIG_OF_LIVE
initr_of_live,
#endif
#ifdef CONFIG_DM
initr_dm, //设备树模块初始化
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32) || defined(CONFIG_RISCV) || \
defined(CONFIG_SANDBOX)
board_init, /* Setup chipselects */
#endif
/*
* TODO: printing of the clock inforamtion of the board is now
* implemented as part of bdinfo command. Currently only support for
* davinci SOC's is added. Remove this check once all the board
* implement this.
*/
#ifdef CONFIG_CLOCKS
set_cpu_clk_info, /* Setup clock information */
#endif
#ifdef CONFIG_EFI_LOADER
efi_memory_init,
#endif
initr_binman,
#ifdef CONFIG_FSP_VERSION2
arch_fsp_init_r,
#endif
initr_dm_devices, //设备树设备的舒适和
stdio_init_tables,
initr_serial, //串口初始化
initr_announce,
#if CONFIG_IS_ENABLED(WDT)
initr_watchdog, //看门狗初始化
#endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_NEEDS_MANUAL_RELOC
initr_manual_reloc_cmdtable,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_MIPS)
initr_trap,
#endif
#ifdef CONFIG_ADDR_MAP
initr_addr_map,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_R)
board_early_init_r,
#endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_POST
initr_post_backlog,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT)
/*
* Do early PCI configuration _before_ the flash gets initialised,
* because PCU resources are crucial for flash access on some boards.
*/
initr_pci, //pci初始化
#endif
#ifdef CONFIG_ARCH_EARLY_INIT_R
arch_early_init_r,
#endif
power_init_board,
#ifdef CONFIG_MTD_NOR_FLASH
initr_flash, //flash初始化
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86)
/* initialize higher level parts of CPU like time base and timers */
cpu_init_r,
#endif
#ifdef CONFIG_CMD_NAND
initr_nand,
#endif
#ifdef CONFIG_CMD_ONENAND
initr_onenand,
#endif
#ifdef CONFIG_MMC
initr_mmc,
#endif
initr_env, //环境变量初始化
#ifdef CONFIG_SYS_BOOTPARAMS_LEN
initr_malloc_bootparams,
#endif
INIT_FUNC_WATCHDOG_RESET
initr_secondary_cpu,
#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET)
mac_read_from_eeprom,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
/*
* Do pci configuration
*/
initr_pci,
#endif
stdio_add_devices,
initr_jumptable,
#ifdef CONFIG_API
initr_api,
#endif
console_init_r, /* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
console_announce_r,
show_board_info,
#endif
#ifdef CONFIG_ARCH_MISC_INIT
arch_misc_init, /* miscellaneous arch-dependent init */
#endif
#ifdef CONFIG_MISC_INIT_R
misc_init_r, /* miscellaneous platform-dependent init */
#endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDB
initr_kgdb,
#endif
interrupt_init, //中断初始化
#ifdef CONFIG_ARM
initr_enable_interrupts,
#endif
#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_M68K)
timer_init, /*定时器初始化 initialize timer */
#endif
#if defined(CONFIG_LED_STATUS)
initr_status_led,
#endif
/* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NET
initr_ethaddr, //网卡初始化
#endif
#if defined(CONFIG_GPIO_HOG)
gpio_hog_probe_all,
#endif
#ifdef CONFIG_BOARD_LATE_INIT
board_late_init,
#endif
#if defined(CONFIG_SCSI) && !defined(CONFIG_DM_SCSI)
INIT_FUNC_WATCHDOG_RESET
initr_scsi,
#endif
#ifdef CONFIG_BITBANGMII
initr_bbmii,
#endif
#ifdef CONFIG_CMD_NET
INIT_FUNC_WATCHDOG_RESET
initr_net,
#endif
#ifdef CONFIG_POST
initr_post,
#endif
#if defined(CONFIG_IDE) && !defined(CONFIG_BLK)
initr_ide,
#endif
#ifdef CONFIG_LAST_STAGE_INIT
INIT_FUNC_WATCHDOG_RESET
/*
* Some parts can be only initialized if all others (like
* Interrupts) are up and running (i.e. the PC-style ISA
* keyboard).
*/
last_stage_init,
#endif
#ifdef CONFIG_CMD_BEDBUG
INIT_FUNC_WATCHDOG_RESET
initr_bedbug,
#endif
#if defined(CONFIG_PRAM)
initr_mem,
#endif
#if defined(CONFIG_M68K) && defined(CONFIG_BLOCK_CACHE)
blkcache_init,
#endif
run_main_loop, //进入主循环
七、进入主循环
(无输入自启动,有输入则进入交互模式)
//common/main.c 进入主循环(无输入自启动;有输入则进入交互模式)
/* We come here after U-Boot is initialised and ready to process commands */
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
if (IS_ENABLED(CONFIG_VERSION_VARIABLE))
env_set("ver", version_string); /* set version variable */
cli_init();
if (IS_ENABLED(CONFIG_USE_PREBOOT))
run_preboot_environment_command();
if (IS_ENABLED(CONFIG_UPDATE_TFTP))
update_tftp(0UL, NULL, NULL);
s = bootdelay_process(); /* bootdelay延时时间到之前无输入,则加载bootcmd进入自启动模式;如果有输入则进入交互模式
common/autoboot.c
const char *bootdelay_process(void)
{
char *s;
int bootdelay;
bootcount_inc();
s = env_get("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
if (bootcount_error())
s = env_get("altbootcmd");
else
s = env_get("bootcmd");
if (IS_ENABLED(CONFIG_OF_CONTROL))
process_fdt_options(gd->fdt_blob);
stored_bootdelay = bootdelay;
return s;
}
*/
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s);
cli_loop();
panic("No CLI available");
}
八、跟踪示例
串口跟踪
线索 /*
common/board_f.c
init_sequence_f[]
board_early_init_f -> board2.c
clock_early_init -> tegra210/clock.c
board_init_uart_f -> board.c
CONFIG_TEGRA_ENABLE_UARTA
setup_uarts -> uart_configs -> FUNCMUX_UART1_UART1, 复用配置
serial_init -> drivers/serial/.built-in.o.cmd -> serial-uclass.c 和 ns16550.c
*/
方法 //1. u-boot.map 搜索 board_early_init_f 确认是在 arch/arm/mach-tegra/里
//2. vscode 里arch/arm/mach-tegra/ 右键 -> 在文件夹中查找 -> 确认是在哪个文件里
//3. 如果还有多个文件,再查看,看哪个生成.o 文件(有编译进来)
板子早期初始化
//arch/arm/mach-tegra/board2.c
#ifdef CONFIG_BOARD_EARLY_INIT_F //ctrl+] 发现include/generated/autoconf.h有,表示下面代码有编译进来
//和 .config里搜 发现 CONFIG_BOARD_EARLY_INIT_F=y 是一样的
int board_early_init_f(void)
{
#if IS_ENABLED(CONFIG_TEGRA_CLKRST) //ctrl+] 发现include/generated/autoconf.h有CONFIG_TEGRA_CLKRST,表示下面代码有编译进来
if (!clock_early_init_done())
clock_early_init(); //早期时钟初始化 ,ctrl+]跳转时为何选tegra210下的,因.config配置里是 CONFIG_SYS_SOC="tegra210"
//或u-boot.map里搜clock_early_init 发现是目录arch/arm/mach-tegra下的,再看对应的.o文件生成否,来确定
//->clock.c
#endif
#if defined(CONFIG_TEGRA_DISCONNECT_UDC_ON_BOOT)
#define USBCMD_FS2 (1 << 15)
{
struct usb_ctlr *usbctlr = (struct usb_ctlr *)0x7d000000;
writel(USBCMD_FS2, &usbctlr->usb_cmd);
}
#endif
pinmux_init();
board_init_uart_f(); //串口早期初始化
/* Initialize periph GPIOs */
gpio_early_init();
gpio_early_init_uart();
return 0;
}
#endif /* EARLY_INIT */
时钟的设置
//arch/arm/mach-tegra/tegra210/clock.c 时钟的设置 复杂且关键
void clock_early_init(void)
{
struct clk_rst_ctlr *clkrst =
(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
struct clk_pll_info *pllinfo = &tegra_pll_info_table[CLOCK_ID_DISPLAY];
u32 data;
tegra210_setup_pllp();
/*
* PLLC output frequency set to 600Mhz
* PLLD output frequency set to 925Mhz
*/
switch (clock_get_osc_freq()) {
case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */
clock_set_rate(CLOCK_ID_CGENERAL, 600, 12, 0, 8);
clock_set_rate(CLOCK_ID_DISPLAY, 925, 12, 0, 12);
break;
case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */
clock_set_rate(CLOCK_ID_CGENERAL, 600, 26, 0, 8);
clock_set_rate(CLOCK_ID_DISPLAY, 925, 26, 0, 12);
break;
case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */
clock_set_rate(CLOCK_ID_CGENERAL, 600, 13, 0, 8);
clock_set_rate(CLOCK_ID_DISPLAY, 925, 13, 0, 12);
break;
case CLOCK_OSC_FREQ_19_2:
clock_set_rate(CLOCK_ID_CGENERAL, 125, 4, 0, 0);
clock_set_rate(CLOCK_ID_DISPLAY, 96, 2, 0, 12);
break;
case CLOCK_OSC_FREQ_38_4:
clock_set_rate(CLOCK_ID_CGENERAL, 125, 8, 0, 0);
clock_set_rate(CLOCK_ID_DISPLAY, 96, 4, 0, 0);
break;
default:
/*
* These are not supported. It is too early to print a
* message and the UART likely won't work anyway due to the
* oscillator being wrong.
*/
break;
}
/* PLLC_MISC1: Turn IDDQ off. NOTE: T210 PLLC_MISC_1 maps to pll_misc */
clrbits_le32(&clkrst->crc_pll[CLOCK_ID_CGENERAL].pll_misc,
(1 << PLLC_IDDQ));
udelay(2);
/*
* PLLC_MISC: Take PLLC out of reset. NOTE: T210 PLLC_MISC maps
* to pll_out[1]
*/
clrbits_le32(&clkrst->crc_pll[CLOCK_ID_CGENERAL].pll_out[1],
(1 << PLLC_RESET));
udelay(2);
/* PLLD_MISC: Set CLKENABLE and LOCK_DETECT bits */
data = (1 << PLLD_ENABLE_CLK) | (1 << pllinfo->lock_ena);
writel(data, &clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_misc);
udelay(2);
}
串口初始化
//arch/arm/mach-tegra/board.c 串口初始化
enum {
/* UARTs which we can enable */
UARTA = 1 << 0,
UARTB = 1 << 1,
UARTC = 1 << 2,
UARTD = 1 << 3,
UARTE = 1 << 4,
UART_COUNT = 5,
};
#if IS_ENABLED(CONFIG_TEGRA_PINCTRL)
static int uart_configs[] = {
#else /* Tegra210 我们使用的配置 */
FUNCMUX_UART1_UART1, /* UARTA */
-1,
-1,
FUNCMUX_UART4_UART4, /* UARTD */
-1,
#endif
};
/**
* Set up the specified uarts
*
* @param uarts_ids Mask containing UARTs to init (UARTx)
*/
static void setup_uarts(int uart_ids)
{
static enum periph_id id_for_uart[] = {
PERIPH_ID_UART1,
PERIPH_ID_UART2,
PERIPH_ID_UART3,
PERIPH_ID_UART4,
PERIPH_ID_UART5,
};
size_t i;
for (i = 0; i < UART_COUNT; i++) {
if (uart_ids & (1 << i)) {
enum periph_id id = id_for_uart[i];
funcmux_select(id, uart_configs[i]);
clock_ll_start_uart(id);
}
}
}
#endif
void board_init_uart_f(void) //uart 初始化
{
#if IS_ENABLED(CONFIG_TEGRA_PINCTRL)
int uart_ids = 0; /* bit mask of which UART ids to enable */
#ifdef CONFIG_TEGRA_ENABLE_UARTA //ctrl+] 发现p3541-0000.h 中有定义 -> 使用的是UARTA
uart_ids |= UARTA;
#endif
#ifdef CONFIG_TEGRA_ENABLE_UARTB //ctrl+] 无
uart_ids |= UARTB;
#endif
#ifdef CONFIG_TEGRA_ENABLE_UARTC
uart_ids |= UARTC;
#endif
#ifdef CONFIG_TEGRA_ENABLE_UARTD
uart_ids |= UARTD;
#endif
#ifdef CONFIG_TEGRA_ENABLE_UARTE
uart_ids |= UARTE;
#endif
setup_uarts(uart_ids); //传入id号指定初始化的串口是UART
#endif
}
#if !CONFIG_IS_ENABLED(OF_CONTROL)
static struct ns16550_platdata ns16550_com1_pdata = {
.base = CONFIG_SYS_NS16550_COM1,
.reg_shift = 2,
.clock = CONFIG_SYS_NS16550_CLK,
.fcr = UART_FCR_DEFVAL,
};
U_BOOT_DEVICE(ns16550_com1) = {
"ns16550_serial", &ns16550_com1_pdata
};
#endif
驱动通用接口
//drivers/serial/serial-uclass.c 驱动通用接口
void serial_putc(char ch) //发送字符
{
if (gd->cur_serial_dev)
_serial_putc(gd->cur_serial_dev, ch);
}
void serial_puts(const char *str) //发送字符串
{
if (gd->cur_serial_dev)
_serial_puts(gd->cur_serial_dev, str);
}
int serial_getc(void) //接收字符
{
if (!gd->cur_serial_dev)
return 0;
return _serial_getc(gd->cur_serial_dev);
}
static void serial_stub_putc(struct stdio_dev *sdev, const char ch)
{
_serial_putc(sdev->priv, ch);
}
/**
* on_baudrate() - Update the actual baudrate when the env var changes
*
* This will check for a valid baudrate and only apply it if valid.
*/
static int on_baudrate(const char *name, const char *value, enum env_op op,
int flags)
{
int i;
int baudrate;
switch (op) {
case env_op_create:
case env_op_overwrite:
/*
* Switch to new baudrate if new baudrate is supported
*/
baudrate = simple_strtoul(value, NULL, 10);
/* Not actually changing */
if (gd->baudrate == baudrate)
return 0;
for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) {
if (baudrate == baudrate_table[i])
break;
}
if (i == ARRAY_SIZE(baudrate_table)) {
if ((flags & H_FORCE) == 0)
printf("## Baudrate %d bps not supported\n",
baudrate);
return 1;
}
if ((flags & H_INTERACTIVE) != 0) {
printf("## Switch baudrate to %d bps and press ENTER ...\n",
baudrate);
udelay(50000);
}
gd->baudrate = baudrate;
serial_setbrg();
udelay(50000);
if ((flags & H_INTERACTIVE) != 0)
while (1) {
if (getc() == '\r')
break;
}
return 0;
case env_op_delete:
printf("## Baudrate may not be deleted\n");
return 1;
default:
return 0;
}
}
U_BOOT_ENV_CALLBACK(baudrate, on_baudrate);
#if CONFIG_IS_ENABLED(SERIAL_PRESENT)
static int serial_post_probe(struct udevice *dev)
{
sdev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_DM;
sdev.priv = dev;
sdev.putc = serial_stub_putc;
sdev.puts = serial_stub_puts;
sdev.getc = serial_stub_getc;
sdev.tstc = serial_stub_tstc;
#if CONFIG_IS_ENABLED(SERIAL_RX_BUFFER)
/* Allocate the RX buffer */
upriv->buf = malloc(CONFIG_SERIAL_RX_BUFFER_SIZE);
#endif
stdio_register_dev(&sdev, &upriv->sdev);
#endif
return 0;
}
UCLASS_DRIVER(serial) = {
.id = UCLASS_SERIAL,
.name = "serial",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_probe = serial_post_probe, //探测串口并初始化
.pre_remove = serial_pre_remove,
.per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
};
串口驱动
//drivers/serial/ns16550.c 串口驱动
const struct dm_serial_ops ns16550_serial_ops = {
.putc = ns16550_serial_putc, //发送字符
.pending = ns16550_serial_pending,
.getc = ns16550_serial_getc, //接收字符
.setbrg = ns16550_serial_setbrg,
.setconfig = ns16550_serial_setconfig,
.getinfo = ns16550_serial_getinfo,
};
static const struct udevice_id ns16550_serial_ids[] = {
{ .compatible = "ns16550", .data = PORT_NS16550 },
{ .compatible = "ns16550a", .data = PORT_NS16550 },
{ .compatible = "ingenic,jz4780-uart", .data = PORT_JZ4780 },
{ .compatible = "nvidia,tegra20-uart", .data = PORT_NS16550 }, //英伟达是 基于现有框架 NS16550 改的
{ .compatible = "snps,dw-apb-uart", .data = PORT_NS16550 },
{}
};
U_BOOT_DRIVER(ns16550_serial) = {
.name = "ns16550_serial",
.id = UCLASS_SERIAL,
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
.of_match = ns16550_serial_ids,
.ofdata_to_platdata = ns16550_serial_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct ns16550_platdata),
#endif
.priv_auto_alloc_size = sizeof(struct NS16550),
.probe = ns16550_serial_probe,
.ops = &ns16550_serial_ops,
#if !CONFIG_IS_ENABLED(OF_CONTROL)
.flags = DM_FLAG_PRE_RELOC,
#endif
};