1. 系统移植
1. 嵌入式Linux构成与移植概述
- 嵌入式Linux移植定义: 移植就是将Bootloader的源代码、Linux内核源代码、文件系统中用户态程序代码根据硬件做少量修改,使其能够在目标硬件平台上运行起来的过程。
- Linux内核功能及本质:
2. Bootloader定义及重要性
- 定义: Bootloader是在操作系统内核运行之前运行,初始化硬件设备、建立内存空间映射,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。
- 为什么需要Bootloader:
- 在嵌入式系统中,Bootloader是一段短小的启动程序,因为没有BIOS那样的固件程序,因此整个系统的加载启动任务就完全由Bootloader来完成。
- 使Linux内核可以在系统主存中跑起来,系统必须符合Linux内核启动的必要条件。
3. Bootloader、Kernel、应用程序之间的关系
- Bootloader:
- 硬件上电后跳到固定位置执行相应代码
- 初始化相应的硬件设备
- 加载操作系统内核代码到内存
- 跳到内核代码起始位置执行
- Kernel (uImage):
- 内核自解压 (uImage)
- 初始化相应的硬件设备
- 初始化静态编译进内核的驱动模块
- 挂载根文件系统
- 直接执行第一个用户空间程序
- 第一个用户空间程序: 配置用户环境和执行服务进程
2. 开发板烧写并启动(先对emmc进行分区,了解整个系统在emmc上的启动过程)
1. emmc分区布局
1 2 3 4
| 0M - 1M(0x80) : Bootloader (U-Boot) 1M - 65M(0x20800) : Kernel (uImage + dtb) 65M - 819M : RootFS (ext4) 819M - 末尾 : 其他数据
|
2. 假设当前通过软件烧录uboot后启动在uboot中,(uboot支持emmc操作)
- 查看emmc信息
mmc list / mmc dev 0 / mmc info
- 按需擦除数据
- 分区(help fdisk)
- fdisk [part table counts] start:length….
fdisk 2 3 0x100000:0x4000000 0x4100000:2f200000 0x33300000:0 // 从bootloader地址之后开始分区的话
- 通过uboot中的 loadaddr 和 bdinfo 查看DRAM信息, 然后重新烧写uboot
tftp 0x48000000 ubootpak.bin
- Bytes transferred = 342960 (53bb0 hex)
update_mmc 2 2ndboot 0x48000000 0x200 53bb0
- 重启进入uboot
- Linux内核烧写
tftp 48000000 uImage
mmc write 48000000 0x800 0x3000 (写入的扇区位置和数量)
3. 配置NFS服务器
3. U-boot源码分析
1. U-boot
- U-Boot(Universal Boot Loader)是一个开源、跨平台的 Bootloader,主要用于嵌入式系统启动。它独立于操作系统,运行在裸机环境,支持多种 CPU 架构(ARM、PowerPC、MIPS、x86 等)和操作系统(Linux、VxWorks 等)。
- 特点:模块化设计,易于移植;支持多种存储、外设和网络协议;提供丰富的命令行操作。
2. U-boot编译
- 清理环境(防止残留配置):
- 加载目标板默认配置:
- 可选修改配置(菜单界面):
- 编译(指定交叉编译器):
- make CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)
- 查看产物:
- u-boot:ELF 格式,可调试,为可执行的ELF格式文件。
ubootpak.bin为u-boot通过objcopy转换得到的二进制文件(用于烧写)。
- u-boot-dtb.bin:带设备树版本(部分平台)。
3. 入口点文件分析
编译链接过程 (make V=1)
当执行 make V=1 时,可以看到详细的编译链接过程。例如:
make -f scripts/Makefile.build obj=examples/standalone arm-cortex_a9-linux-gnueabi-ld -pte --gc-sections -Bstatic -Ttext 0x43C0000 -o u-boot u-boot.lds arch/arm/cpu/slsiap/start.o --start-group arch/arm/cpu/slsiap/built-in.o arch/arm/cpu/slsiap/s5p6818/built-in.o arch/arm/cpu/slsiap/s5
这里关键的是 -Ttext 0x43C0000 -o u-boot u-boot.lds,它指定了U-boot的起始地址和链接脚本。
u-boot.lds 链接脚本
链接脚本定义了U-boot各段在内存中的布局。
1 2 3 4 5 6 7 8 9 10 11
| . = 0x00000000; . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata))) } .data : { } .text : { *(.__image_copy_start) arch/arm/cpu/slsiap/s5p6818/start.o (.text*) arch/arm/cpu/slsiap/s5p6818/vectors.o (.text*) *(.text*) }
|
. = 0x00000000;: 指定代码的加载起始地址为0x00000000 (通常是内存或Flash的起始地址)。
arch/arm/cpu/slsiap/s5p6818/start.o (.text*): 这表明start.o文件中的.text段(代码段)是U-boot的第一个执行部分。
start.S (入口点汇编文件)
start.o是由start.S汇编文件编译而来,其中包含了U-boot的真正入口点。
1 2 3 4 5 6 7 8 9 10
| .globl _start _start: b reset // 跳转到reset函数 ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq
|
_start: 这是U-boot的入口点符号。
b reset: 上电后CPU执行的第一个指令,直接跳转到reset函数。reset函数是U-boot初始化过程的起点。
4. U-boot启动过程
阶段一: 最底层初始化 (汇编代码)
U-Boot 启动的第一阶段:从复位到 C 运行环境
- 异常向量表设置
- CPU 模式切换
- 缓存与 MMU 初始化
- 代码重定位
- BSS 段清零
- 栈和全局数据结构(gd)初始化
- 跳转到 board_init_f()(进入 C 语言环境)
start.S主要代码分析:
- 异常向量表设置
ARM 处理器上电后,从 0x00000000 开始取指(或由 VBAR 指定的基地址),所以必须先建立异常向量表。
1 2 3 4 5 6 7 8 9 10
| .globl _stext _stext: b reset @ 复位时跳到 reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq
|
定义 _stext 作为代码的起始地址
ARM 异常类型:reset, undefined, SWI, prefetch abort, data abort, IRQ, FIQ
- 通过 ldr pc, xxx 跳转到对应处理函数地址
- 这一步的目的:保证 CPU 出现异常不会直接崩溃,有跳转入口。
- ARM 架构规定,当某种异常发生时,硬件会自动做两件事:
- 切换到对应的异常模式(如 Supervisor、IRQ、FIQ)。
- 把 PC 设置到异常向量表的固定偏移地址(通常是 0x00000000 或者 VBAR 里的值)。
- 例如:
- 发生 Reset → PC = 0x00000000 → 执行 b reset
- 发生 Undefined → PC = 0x00000004 → 执行 ldr pc, _undefined_instruction
- 发生 IRQ → PC = 0x00000018 → 执行 ldr pc, _irq
b handler 是 相对跳转,需要 handler 在当前 32MB 范围内。
ldr pc, _xxx 是 绝对跳转,通过取一个常量地址,可以跳到 U-Boot 任意地方。
这样设计是为了灵活,后面如果重定位代码,这个表不用改,直接改 _xxx 变量的值。
- reset:从复位到基本初始化
reset 是启动的第一个执行函数,主要工作:
- 保存启动参数
- 切换到 SVC 模式
- 关闭看门狗
- 初始化 CPU 关键寄存器
- 如果需要,跳到低级硬件初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| reset: bl save_boot_params @ 保存启动参数(调用 save_boot_params 函数,保存启动时的参数) /* 切换到 SVC32 模式 */ mrs r0, cpsr @ 读取当前的 CPSR(Current Program Status Register,当前程序状态寄存器) bic r0, r0, #0x1f @ 清除 CPSR 中的模式字段(低 5 位)以准备切换模式 orr r0, r0, #0xd3 @ 设置 CPSR 的模式为 SVC 模式 (SVC32),并禁用所有中断 msr cpsr, r0 @ 将修改后的 CPSR 写回,切换到 SVC 模式并禁用中断
/* 禁用看门狗 */ ldr r0, =0xC0019000 @ 加载看门狗控制寄存器的地址(地址是 0xC0019000) mov r1, #0 @ 将寄存器 r1 设置为 0,表示禁用看门狗 str r1, [r0] @ 将 r1 的值(0)存储到看门狗控制寄存器,禁用看门狗
#ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_cp15 @ 调用 cpu_init_cp15 函数,关闭 MMU、清除缓存等 bl cpu_init_crit @ 调用 cpu_init_crit 函数,执行板级低级初始化,如 PLL 和 DDR 配置 #endif
|
关键点:
- cpsr 切换 CPU 模式为 SVC(超级用户模式),同时关中断
- 关闭看门狗,避免启动过程中被复位
- cpu_init_cp15:处理 CP15 寄存器,关闭 MMU,invalidate I/D cache
- cpu_init_crit:执行板级初始化(比如 DRAM 控制器),确保后面可以使用内存
- 代码重定位(Relocation)-> 如果平台 开启 CONFIG_RELOC_TO_TEXT_BASE
很多 ARM 平台启动时,U-Boot 最初加载在 NOR Flash 或 SRAM,执行地址并不是最终运行地址。
为了能正确访问全局变量、函数指针,必须把 U-Boot 拷贝到 DRAM 的 TEXT_BASE。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| relocate_to_text: adr r0, _stext @ 获取当前代码位置(即 _stext 地址),并将其存入寄存器 r0 ldr r1, TEXT_BASE @ 加载目标地址(TEXT_BASE,即代码重定位后存放的位置) cmp r0, r1 @ 比较当前代码地址(r0)与目标地址(r1) beq clear_bss @ 如果当前地址已经是目标地址,则跳转到 clear_bss,避免重复搬迁
ldr r2, _bss_start_ofs @ 加载 BSS 段起始地址的偏移量(_bss_start_ofs) add r2, r0, r2 @ 计算拷贝结束地址(当前地址 + BSS 段的偏移量)
copy_loop_text: ldmia r0!, {r3-r10} @ 从 r0(当前代码地址)加载 8 个寄存器的内容,并将 r0 地址增加 8 字节 stmia r1!, {r3-r10} @ 将加载的寄存器内容存储到 r1(目标地址),并将 r1 地址增加 8 字节 cmp r0, r2 @ 比较当前地址(r0)与拷贝结束地址(r2) ble copy_loop_text @ 如果 r0 小于等于 r2,说明还没有到达结束地址,继续拷贝
ldr r1, TEXT_BASE @ 重新加载目标地址 TEXT_BASE mov pc, r1 @ 跳转到 DRAM 中的 TEXT_BASE 继续执行
|
- 比较当前执行地址和目标地址(TEXT_BASE)
- 如果不一样,循环拷贝整个 U-Boot 代码段
- 最后 mov pc, r1,跳转到 DRAM 里的代码继续执行
- 清空 BSS 段
BSS 段用于存放未初始化的全局变量,必须清零。
1 2 3 4 5 6 7 8 9 10 11 12 13
| clear_bss: ldr r0, _bss_start_ofs @ 加载 BSS 段的起始偏移地址 ldr r1, _bss_end_ofs @ 加载 BSS 段的结束偏移地址 ldr r4, TEXT_BASE @ 加载 TEXT_BASE 地址 add r0, r0, r4 @ 计算实际的 BSS 段起始地址(BSS 起始地址 + TEXT_BASE) add r1, r1, r4 @ 计算实际的 BSS 段结束地址(BSS 结束地址 + TEXT_BASE) mov r2, #0 @ 设置 r2 为 0,准备清空 BSS 段
clbss_l: str r2, [r0] @ 将 0 存储到 BSS 段中的当前地址 add r0, r0, #4 @ 移动到下一个 4 字节地址(每次清 4 字节) cmp r0, r1 @ 比较当前地址(r0)与 BSS 结束地址(r1) bne clbss_l @ 如果没有到达结束地址,继续清空 BSS 段
|
- 把 BSS 段所有字节清 0
- 避免后面 C 代码访问到脏数据
- 初始化栈和 GD(global data)结构
C 代码需要栈,U-Boot 还需要 gd_t 全局数据结构保存运行状态。
1 2 3 4 5
| ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) @ 设置栈指针 sp 为初始化的栈地址(CONFIG_SYS_INIT_SP_ADDR) bic sp, sp, #7 @ 确保栈指针 sp 8 字节对齐,清除 sp 的最低三位 sub sp, #GD_SIZE @ 给全局数据(GD)预留空间,调整栈指针,分配 GD 所需的内存 bic sp, sp, #7 @ 再次确保栈指针 8 字节对齐 mov r9, sp @ 将栈指针保存到 r9,r9 将作为全局数据(GD)的基地址
|
- CONFIG_SYS_INIT_SP_ADDR:通常是 DRAM 高地址(安全)
- GD_SIZE:U-Boot 的全局数据结构
- r9(IP 寄存器)专门存放 gd 指针
- board_init_f函数执行,跳转到 board_init_f 所在的board_f.c 文件中
board_init_f 阶段
- 运行位置:Flash 或 SRAM(未 relocation 前)
- 作用:
- 初始化 PLL/时钟。
- 初始化 DDR 控制器(重点)。
- 初始化 串口(用于输出 early log)。
- 计算 relocation 参数:
- gd->relocaddr:U-Boot 在 DRAM 的目标地址。
- gd->start_addr_sp:新的栈顶地址(在 DRAM)。
- gd->new_gd:新全局数据区域(DRAM)。
- 调用 jump_to_copy() → relocate_code()。
relocate_code 阶段
- 运行位置:仍然在 Flash/SRAM(搬家前)。
- 作用:
- 将 .text、.data 从 当前位置 复制到 gd->relocaddr(DRAM)。
- 清空 .bss 段(DRAM)。
- 切换栈到 DRAM(sp = gd->start_addr_sp)。
- 跳转到 board_init_r 的地址(现在在 DRAM 中)。
这一步才是真正把 U-Boot 搬到 DRAM 并切换执行位置。
- board_init_r函数执行,跳转到 board_init_r 所在的board_r.c 文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void main_loop(void) { const char *s;
modem_init();
cli_init();
run_preboot_environment_command();
s = bootdelay_process();
autoboot_command(s);
cli_loop(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| int cli_simple_run_command(const char *cmd, int flag) { char cmdbuf[CONFIG_SYS_CBSIZE]; char *token; char *sep; char finaltoken[CONFIG_SYS_CBSIZE]; char *str = cmdbuf; char *argv[CONFIG_SYS_MAXARGS + 1];
int argc, inquotes; int repeatable = 1; int rc = 0;
debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd); if (DEBUG_PARSER) { puts(cmd ? cmd : "NULL"); puts("\"\n"); }
clear_ctrlc();
if (!cmd || !*cmd) return -1;
if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { puts("## Command too long!\n"); return -1; } strcpy(cmdbuf, cmd);
debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); while (*str) {
for (inquotes = 0, sep = str; *sep; sep++) { if ((*sep == '\'') && (*(sep - 1) != '\\')) inquotes = !inquotes;
if (!inquotes && (*sep == ';') && (sep != str) && (*(sep - 1) != '\\')) break; }
token = str; if (*sep) { str = sep + 1; *sep = '\0'; } else { str = sep; } debug_parser("token: \"%s\"\n", token);
process_macros(token, finaltoken);
argc = cli_simple_parse_line(finaltoken, argv); if (argc == 0) { rc = -1; continue; }
if (cmd_process(flag, argc, argv, &repeatable, NULL)) rc = -1;
if (had_ctrlc()) return -1; }
return rc ? rc : repeatable; }
|
核心为:
- 调用命令处理函数:根据 argv[0] 查找命令表 cmd_tbl_t, 找到后调用对应的执行函数,比如 do_bootm()
- cmd_process(flag, argc, argv, &repeatable, NULL)
cmdtp = find_cmd(argv[0]); /* Look up command in command table */
for (cmdtp = table;
cmdtp != table + table_len;
cmdtp++) {
if (strncmp (cmd, cmdtp->name, len) == 0) {
if (len == strlen (cmdtp->name))
return cmdtp; /* full match */
cmdtp_temp = cmdtp; /* abbreviated command ? */
n_found++;
}
}
- if (argc > cmdtp->maxargs) /* found - check max args */
- rc = cmd_call(cmdtp, flag, argc, argv); /* If OK so far, then do the command */
- // 直接调用命令函数指针,相当于执行 do_bootm(cmdtp, flag, argc, argv)
- result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
U-Boot bootm 命令流程解析
bootm 命令是 U-Boot 用来启动 Linux 内核或其他操作系统映像的入口命令。内部核心是 do_bootm(),它会依次执行几个状态,每个状态对应不同的操作。
1 2 3 4 5 6 7
| do_bootm() └─> do_bootm_states() ├─ BOOTM_STATE_START ├─ BOOTM_STATE_FINDOS ├─ BOOTM_STATE_FINDOTHER ├─ BOOTM_STATE_LOADOS └─ BOOTM_STATE_OS_GO
|
- BOOTM_STATE_START → bootm_start()
- 初始化启动上下文 (bootm_headers 等结构体)。
- 检查镜像类型(uImage、FIT、Legacy 等)。
- 验证镜像合法性(如 magic number、checksum)。
- 确定后续加载流程是否需要解压。
关键点:
- 这里是整个 bootm 流程的“起点”,决定后续镜像解析路径。
- 如果启动 FIT 镜像,还会解析 FIT 描述符。
- BOOTM_STATE_FINDOS → bootm_find_os() → boot_get_kernel()
- 找到操作系统内核镜像在内存中的位置。
- 设置内核映像地址(image->load)和类型。
- 对不同内核格式(zImage、uImage)做处理:
- uImage:提取 header,获取 load address。
- zImage:直接获取 load 地址,通常包含自解压代码。
关键点:
- 这个阶段不执行实际加载,只是确定内核在哪,大小是多少。
- 对于 FIT 镜像,会解析 kernel 节点及其 load 地址。
- BOOTM_STATE_FINDOTHER → bootm_find_other()
- 查找其他启动必需镜像:
- ramdisk(initrd/initramfs)
- FDT(Device Tree Blob,硬件描述)
- 根据镜像类型和配置设置内存地址。
- 为内核启动准备参数。
关键点:
- 确保内核启动时能够找到根文件系统和硬件信息。
- FIT 镜像会在这里解析 DTB 节点、ramdisk 节点。
- BOOTM_STATE_LOADOS → bootm_load_os() → decomp_image()
- 将内核镜像从存储介质(flash、SD、TFTP 等)加载到内存。
- 如果内核是压缩的(如 gzip/zImage/uImage),调用 decomp_image() 解压到目标地址。
- 同时加载 ramdisk 和 FDT 到内存指定位置。
关键点:
- 这里是真正把内核和相关资源搬到内存执行区的步骤。
- decomp_image() 内部处理压缩算法和解压回调。
- BOOTM_STATE_OS_GO → bootm_os_get_boot_func() → 跳转内核
- 获取内核启动入口(kernel_entry)。
- 根据内核类型和架构,设置启动参数(如 ATAGs、FDT 指针、ramdisk 地址)。
- 调用函数指针,真正跳转到内核执行。
关键点:
- U-Boot 在这里退出,CPU 权限切换到内核环境。
- 后续流程由内核接管。
- 初始化本阶段要使用的硬件设备。
- 检测系统内存映射。
- 将内核映像和根文件系统映像从Flash读到RAM空间。
- 为内核设置启动参数。
- 调用内核。
系统上电后的完整流程 (示意):
系统上电 -> 设置为SVC工作模式 -> 关闭看门狗 -> 清空CACHE -> 禁止MMU -> 清空BSS段 -> 一系列硬件的初始化 -> 执行bootcmd中的命令 (加载linux内核) -> 设置为SVC工作模式 -> 检查CPUid是否支持 -> 创建页表 -> 开启MMU -> 创建子线程 -> 子线程中挂载指定的根文件系统 -> 启动用户空间1号进程 -> 开启后续用户空间进程 -> 启动一个shell -> 用户可以输入命令
imx6ull Uboot 启动详细函数调用流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
| u-boot:启动详细的代码调用流程 u-boot.lds:(arch/arm/cpu/u-boot.lds) |-->_start:(arch/arm/lib/vectors.S) |-->reset(arch/arm/cpu/armv7/start.S) |-->save_boot_params(arch/arm/cpu/armv7/start.S) |-->save_boot_params_ret(arch/arm/cpu/armv7/start.S) |-->cpu_init_cp15(arch/arm/cpu/armv7/start.S) |-->cpu_init_crit(arch/arm/cpu/armv7/start.S) |-->lowlevel_init(arch/arm/cpu/armv7/lowlevel_init.S) |-->_main(arch/arm/lib/crt0.S) |-->board_init_f_alloc_reserve(common/init/board_init.c) |-->board_init_f_init_reserve(common/init/board_init.c) |-->board_init_f(common/board_f.c) |-->initcall_run_list(include/initcall.h) |-->init_sequence_f[](common/board_f.c) |-->board_early_init_f(board/freescale/mx6ull_toto/mx6ull_toto.c) |-->timer_init(arch/arm/imx-common/timer.c) |-->init_baud_rate(common/board_f.c) |-->serial_init(drivers/serial/serial.c) |-->console_init_f(common/console.c) |-->... |-->relocate_code(arch/arm/lib/relocate.S) |-->relocate_vectors(arch/arm/lib/relocate.S) |-->board_init_r(common/board_r.c) |-->initcall_run_list(include/initcall.h) |-->init_sequence_r[](common/board_f.c) |-->initr_reloc(common/board_r.c) |-->serial_initialize(drivers/serial/serial-uclass.c) |-->serial_init(drivers/serial/serial-uclass.c) |-->initr_mmc(common/board_r.c) |-->mmc_initialize(drivers/mmc/mmc.c) |-->mmc_do_preinit(drivers/mmc/mmc.c) |-->mmc_start_init(drivers/mmc/mmc.c) |-->console_init_r(common/console.c) |-->interrupt_init(arch/arm/lib/interrupts.c) |-->initr_net(common/board_r.c) |-->eth_initialize(net/eth-uclass.c) |-->eth_common_init(net/eth_common.c) |-->phy_init(drivers/net/phy/phy.c) |-->uclass_first_device_check(drivers/core/uclass.c) |-->uclass_find_first_device(drivers/core/uclass.c) |-->device_probe(drivers/core/device.c) |-->device_of_to_plat(drivers/core/device.c) |-->drv->of_to_plat |-->fecmxc_of_to_plat(drivers/net/fec_mxc.c) |-->device_get_uclass_id(drivers/core/device.c) |-->uclass_pre_probe_device(drivers/core/uclass.c) |-->drv->probe(dev) U_BOOT_DRIVER(fecmxc_gem) = { .name = "fecmxc", .id = UCLASS_ETH, .of_match = fecmxc_ids, .of_to_plat = fecmxc_of_to_plat, .probe = fecmxc_probe, .remove = fecmxc_remove, .ops = &fecmxc_ops, .priv_auto = sizeof(struct fec_priv), .plat_auto = sizeof(struct eth_pdata), }; |-->fecmxc_probe(drivers/net/fec_mxc.c) |-->fec_get_miibus(drivers/net/fec_mxc.c) |-->mdio_alloc(drivers/net/fec_mxc.c) |-->bus->read = fec_phy_read; |-->bus->write = fec_phy_write; |-->mdio_register(common/miiphyutil.c) |-->fec_mii_setspeed(drivers/net/fec_mxc.c) |-->fec_phy_init(drivers/net/fec_mxc.c) |-->device_get_phy_addr(drivers/net/fec_mxc.c) |-->phy_connect(drivers/net/phy/phy.c) |-->phy_find_by_mask(drivers/net/phy/phy.c) |-->bus->reset(bus) |-->get_phy_device_by_mask(drivers/net/phy/phy.c) |-->create_phy_by_mask(drivers/net/phy/phy.c) |-->phy_device_create(drivers/net/phy/phy.c) |-->phy_probe(drivers/net/phy/phy.c) |-->phy_connect_dev(drivers/net/phy/phy.c) |-->phy_reset(drivers/net/phy/phy.c) |-->phy_config(drivers/net/phy/phy.c) |-->board_phy_config(drivers/net/phy/phy.c) |-->phydev->drv->config(phydev) static struct phy_driver lan8710_driver = { .name = "SMSC LAN8710/LAN8720", .uid = 0x0007c0f0, .mask = 0xffff0, .features = PHY_BASIC_FEATURES, .config = &genphy_config_aneg, .startup = &genphy_startup, .shutdown = &genphy_shutdown, }; |-->genphy_config_aneg(drivers/net/phy/phy.c) |-->phy_reset(需要手动调用)(drivers/net/phy/phy.c) |-->genphy_setup_forced(drivers/net/phy/phy.c) |-->genphy_config_advert(drivers/net/phy/phy.c) |-->genphy_restart_aneg(drivers/net/phy/phy.c) |-->uclass_post_probe_device(drivers/core/uclass.c) |-->uc_drv->post_probe(drivers/core/uclass.c) UCLASS_DRIVER(ethernet) = { .name = "ethernet", .id = UCLASS_ETH, .post_bind = eth_post_bind, .pre_unbind = eth_pre_unbind, .post_probe = eth_post_probe, .pre_remove = eth_pre_remove, .priv_auto = sizeof(struct eth_uclass_priv), .per_device_auto = sizeof(struct eth_device_priv), .flags = DM_UC_FLAG_SEQ_ALIAS, }; |-->eth_post_probe(net/eth-uclass.c) |-->eth_write_hwaddr(drivers/core/uclass.c) |-->... |-->run_main_loop(common/board_r.c) |-->main_loop(common/main.c) |-->bootdelay_process(common/autoboot.c) |-->autoboot_command(common/autoboot.c) |-->abortboot(common/autoboot.c) |-->printf("Hit any key to stop autoboot: %2d ", bootdelay); |-->cli_loop(common/cli.c)
|
图示
