当前位置: 首页 > news >正文

[IMX][UBoot] 08.启动流程 (4) - 平台后期初始化阶段 - board_init_r

文章链接

UBoot 启动流程 (1) - 基本流程

UBoot 启动流程 (2) - 平台前期初始化阶段 - board_init_f

UBoot 启动流程 (3) - UBoot 程序重定位 - relocate_code

UBoot 启动流程 (4) - 平台后期初始化阶段 - board_init_r

UBoot 启动流程 (5) - UBoot 运行阶段 - main_loop

UBoot 启动流程 (6) - bootz 命令启动 Linux


目录

1.初始化 Trace - initr_trace()

2.标记 uboot 重定位完成 - initr_reloc()

3.使能数据缓存 D-Cache - initr_caches()

4.重定位部分 gd 成员 - initr_reloc_global_data()

5.初始化 malloc - initr_malloc()

6.创建启动阶段信息的副本 - bootstage_relocate()

7.记录当前启动阶段 - initr_bootstage()

8.外设初始化 - board_init()

9.STDIO 初始化 - stdio_init_tables()

10.串口初始化 - initr_serial()

11.打印 uboot 已在内存中运行的调试信息 - initr_announce()

12.初始化 eMMC - initr_mmc()

13.初始化环境变量 - initr_env()

14.添加其他 stdio 设备 - stdio_add_devices()

15.初始化跳转表 - initr_jumptable()

16.初始化控制台 - console_init_r()

17.中断初始化 - interrupt_init()

18.中断使能 - initr_enable_interrupts()

19.从环境变量中获取 MAC 地址 - initr_ethaddr()

20.设置启动模式 - board_late_init()

21.初始化以太网 - initr_net()

22.启动命令行或 Linux 内核 - run_main_loop()


// 函数调用栈
reset()|--> _main()|--> board_init_f() // 初始化核心硬件,如内存、串口等|--> relocate_code() // 将 uboot 从内存的低地址处拷贝至内存的高地址中|--> board_init_r() // 初始化剩余复杂外设,如显示、以太网等|--> run_main_loop() // 启动 uboot 命令行

重定位完成后,此时已在内存中为 malloc() 预留了空间,硬件资源不再受限,例如,栈的大小为 16MB、可以使用堆内存等,因此可以初始化以太网、显示等复杂外设,这部分工作由 board_init_r() 完成:

// arch/arm/lib/crt0.S
ENTRY(_main)...mov r0, r9                  /* gd_t */ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */ldr pc, =board_init_r       /* this is auto-relocated! */
  • mov r0, r9 将 R9 中保存的 gd 地址存入 R0,即将 gd 作为第一个参数传递给 board_init_r();

  • ldr r1, [r9, #GD_RELOCADDR] 将重定位后的 uboot 起始地址存入 R1,作为 board_init_r() 的第二个参数;

  • ldr pc, =board_init_r 调用 board_init_r() 初始化剩余复杂外设,并启动 uboot 命令行或 Linux 内核;

board_init_r() 和 board_init_f() 类似,需要执行初始化的函数保存在数组 init_sequence_r 中:

// common/board_r.c
init_fnc_t init_sequence_r[] = {initr_trace,initr_reloc,.../* Light up LED2 */imx6_light_up_led2,run_main_loop,
};

board_init_r() 通过 initcall_run_list() 依次调用 init_sequence_r 中的初始化函数:

// common/board_r.c
void board_init_r(gd_t *new_gd, ulong dest_addr)
{...// 依次调用 init_sequence_r 中的初始化函数if (initcall_run_list(init_sequence_r))

1.初始化 Trace - initr_trace()

在 uboot 中使用 trace 需要配置 CONFIG_TRACE 宏 (默认不使用 trace)

board_init_f() 进行平台早期初始化时,在内存中为 trace 预留了 16MB 大小的空间:

// common/board_f.c
static int reserve_trace(void)
{
#ifdef CONFIG_TRACE// 在内存中为 trace 预留空间gd->relocaddr -= CONFIG_TRACE_BUFFER_SIZE;// 为 trace 分配内存,大小为 CONFIG_TRACE_BUFFER_SIZE = 16 << 20 = 16MBgd->trace_buff = map_sysmem(gd->relocaddr, CONFIG_TRACE_BUFFER_SIZE);debug("Reserving %dk for trace data at: %08lx\n",CONFIG_TRACE_BUFFER_SIZE >> 10, gd->relocaddr);
#endifreturn 0;
}

在 board_init_r() 阶段中调用 trace_init() 初始化 trace:

// common/board_r.c
static int initr_trace(void)
{
#ifdef CONFIG_TRACEtrace_init(gd->trace_buff, CONFIG_TRACE_BUFFER_SIZE); // 初始化 trace
#endifreturn 0;
}
--------------------------------------------------------// lib/trace.c
int __attribute__((no_instrument_function)) trace_init(void *buff,size_t buff_size) // buff_size = 16MB
{ulong func_count = gd->mon_len / FUNC_SITE_SIZE; // 计算可跟踪的函数个数size_t needed; // 保存 trace 所需的内存...// 初始化 trace headerhdr = (struct trace_hdr *)buff;...hdr->func_count = func_count; // 可以跟踪多少个函数hdr->call_accum = (uintptr_t *)(hdr + 1);/* Use any remaining space for the timed function trace */hdr->ftrace = (struct trace_call *)(buff + needed);hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace);add_textbase(); // 设置代码段的基地址puts("trace: enabled\n");hdr->depth_limit = 15; // 限制最大调用深度trace_enabled = 1; // 标记 trace 已使能trace_inited = 1; // 标记 trace 初始化已完成return 0;
}

2.标记 uboot 重定位完成 - initr_reloc()

设置 gd->flags,标记 uboot 程序重定位已完成:

// common/board_r.c
static int initr_reloc(void)
{/* tell others: relocation done */gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT;return 0;
}

GD_FLG_RELOC 中的 FLG 是 Flag 的缩写,表示这个宏是一个标志位

3.使能数据缓存 D-Cache - initr_caches()

指令缓存 I-Cache 在 cpu_init_cp15() 初始化 CP15 协处理器时已经使能

initr_caches() 负责使能数据缓存 D-Cache,具体操作由 enable_caches() 完成:

// common/board_r.c
static int initr_caches(void)
{/* Enable caches */enable_caches(); // 使能数据缓存 D-Cachereturn 0;
}
----------------------------------------------------void enable_caches(void)
{// 设置缓存的写入策略: Write-Through or Write-Back
#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)enum dcache_option option = DCACHE_WRITETHROUGH;
#elseenum dcache_option option = DCACHE_WRITEBACK;
#endif/* Avoid random hang when download by usb */invalidate_dcache_all(); // 清除目前所有已缓存的 D-Cache/* Enable D-cache. I-cache is already enabled in start.S */dcache_enable(); // 使能 D-Cache// 配置 OCRAM 和 ROM 的写入策略:Write-Through or Write-Back/* Enable caching on OCRAM and ROM */mmu_set_region_dcache_behaviour(ROMCP_ARB_BASE_ADDR,ROMCP_ARB_END_ADDR,option);mmu_set_region_dcache_behaviour(IRAM_BASE_ADDR,IRAM_SIZE,option);
}

ARM SoC 可以配置向缓存写入数据的策略:

  • Write-Through:写通,数据同时写入缓存和主存,可以保证数据一致性,但性能较差;

  • Write-Back:回写,数据先写入缓存,当缓存被替换时才写回主存,性能较高,但需要处理一致性问题;

4.重定位部分 gd 成员 - initr_reloc_global_data()

计算 uboot 代码段 .text 的长度,重定位环境变量的保存地址、设备树的地址:

// common/board_r.c
static int initr_reloc_global_data(void)
{...monitor_flash_len = _end - __image_copy_start; // 计算代码段的长度.../** Some systems need to relocate the env_addr pointer early because the* location it points to will get invalidated before env_relocate is* called.  One example is on systems that might use a L2 or L3 cache* in SRAM mode and initialize that cache from SRAM mode back to being* a cache in cpu_init_r.*/gd->env_addr += gd->relocaddr - CONFIG_SYS_MONITOR_BASE; // 环境变量保存地址重定位.../** The fdt_blob needs to be moved to new relocation address* incase of FDT blob is embedded with in image*/gd->fdt_blob += gd->reloc_off; // 设备树地址重定位...return 0;
}

5.初始化 malloc - initr_malloc()

  • 设置 malloc() 可分配内存的起始地址 malloc_start;

  • 调用 map_sysmem() 将物理地址直接转换为虚拟地址;

  • 调用 mem_malloc_init() 初始化 malloc() 所使用的内存;

// common/board_r.c
static int initr_malloc(void)
{ulong malloc_start;#ifdef CONFIG_SYS_MALLOC_F_LENdebug("Pre-reloc malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,gd->malloc_ptr / 1024);
#endif/* The malloc area is immediately below the monitor copy in DRAM */malloc_start = gd->relocaddr - TOTAL_MALLOC_LEN; // 设置 malloc() 分配内存的起始地址mem_malloc_init((ulong)map_sysmem(malloc_start, TOTAL_MALLOC_LEN),TOTAL_MALLOC_LEN); // 初始化 malloc() 所使用的内存return 0;
}

6.创建启动阶段信息的副本 - bootstage_relocate()

启动阶段的相关信息保存在结构体 record 中,启动信息中包含了各个阶段的开始时间、名称等信息:

// common/bootstage.c
struct bootstage_record {ulong time_us;uint32_t start_us; // 当前阶段的开始时间const char *name; // 当前阶段的名称int flags;      /* see enum bootstage_flags */enum bootstage_id id;
};
-----------------------------------------------------// uboot/common/bootstage.c
static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} };

uboot 程序重定位后,为了防止原始数据被覆盖,需要在内存中重新为其分配空间 (创建副本):

// common/bootstage.c
int bootstage_relocate(void)
{.../** Duplicate all strings.  They may point to an old location in the* program .text section that can eventually get trashed.*/for (i = 0; i < BOOTSTAGE_ID_COUNT; i++) // 在内存中重新分配空间if (record[i].name) // 未使用 malloc() 分配内存,因此新的数据在堆中分配空间record[i].name = strdup(record[i].name);return 0;
}

7.记录当前启动阶段 - initr_bootstage()

记录当前启动阶段 board_init_r() 的相关信息:

// common/board_r.c
static int initr_bootstage(void)
{/* We cannot do this before initr_dm() */bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r");|--> bootstage_add_record(id, name, flags, timer_get_boot_us())|--> rec->time_us = mark; // 当前时间戳|--> rec->name = name; // 当前阶段的名称|--> rec->flags = flags;|--> rec->id = id; // 当前阶段的编号return 0;
}

8.外设初始化 - board_init()

初始化开发板上的外设,例如 I2C、SPI、USB 等:

  • 获取启动参数的保存地址;

  • 设置 LED 与 IO 引脚的功能复用与电气特性;

  • 调用 iox74lv_init() 初始化 74LV595 驱动芯片 (该芯片用于驱动数码管或继电器);

  • 调用 setup_i2c() 初始化 I2C 模块;

  • 调用 setup_fec() 初始化网口;

  • 调用 setup_usb() 初始化 USB 模块;

  • 调用 board_qspi_init() 初始化 QSPI 模块;

  • 调用 setup_gpmi_nand() 初始化 NAND Flash;

// board/freescale/mx6ullevk/mx6ullevk.c
int board_init(void)
{/* Address of boot parameters */gd->bd->bi_boot_params = PHYS_SDRAM + 0x100; // 获取启动参数的地址// 配置 LED 引脚imx_iomux_v3_setup_multiple_pads(leds_pads, ARRAY_SIZE(leds_pads));// 配置 IO 引脚imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));iox74lv_init(); // 初始化 74LV595 驱动芯片#ifdef CONFIG_SYS_I2C_MXCsetup_i2c(0, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info1); // I2C 初始化
#endif#ifdef  CONFIG_FEC_MXCsetup_fec(CONFIG_FEC_ENET_DEV); // 网口初始化
#endif#ifdef CONFIG_USB_EHCI_MX6setup_usb(); // USB 初始化
#endif#ifdef CONFIG_FSL_QSPIboard_qspi_init(); // QSPI 初始化
#endif#ifdef CONFIG_NAND_MXSsetup_gpmi_nand(); // NAND Flash 初始化
#endifreturn 0;
}

9.STDIO 初始化 - stdio_init_tables()

重定位设备名称 stdio_names,并初始化标准 I/O 使用的设备链表:

// common/stdio.c
int stdio_init_tables(void)
{
#if defined(CONFIG_NEEDS_MANUAL_RELOC)/* already relocated for current ARM implementation */ulong relocation_offset = gd->reloc_off;int i;/* relocate device name pointers */for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {stdio_names[i] = (char *) (((ulong) stdio_names[i]) +relocation_offset);}
#endif /* CONFIG_NEEDS_MANUAL_RELOC *//* Initialize the list */INIT_LIST_HEAD(&(devs.list)); // 初始化标准 I/O 设备链表return 0;
}

注意:这里的设备指的是 stdio 使用的设备,如显示器、串口等

10.串口初始化 - initr_serial()

串口设备类型 serial_device 提供了统一的接口,包括初始化方法 start()、输出方法 puts() 等:

// include/serial.h
struct serial_device {/* enough bytes to match alignment of following func pointer */char    name[16];int (*start)(void);int (*stop)(void);void    (*setbrg)(void);int (*getc)(void);int (*tstc)(void);void    (*putc)(const char c);void    (*puts)(const char *s);
#if CONFIG_POST & CONFIG_SYS_POST_UARTvoid    (*loop)(int);
#endifstruct serial_device    *next;
};

IMX 的串口设备为 mxc_serial_drv:

// drivers/serial/serial_mxc.c
static struct serial_device mxc_serial_drv = {.name   = "mxc_serial",.start  = mxc_serial_init,.stop   = NULL,.setbrg = mxc_serial_setbrg,.putc   = mxc_serial_putc,.puts   = default_serial_puts,.getc   = mxc_serial_getc,.tstc   = mxc_serial_tstc,
};

initr_serial() 调用 serial_initialize() 初始化所有已编译的串口设备:

// common/board_r.c
static int initr_serial(void)
{serial_initialize();return 0;
}
---------------------------------------------------// drivers/serial/serial.c
void serial_initialize(void)
{amirix_serial_initialize();arc_serial_initialize();...mxc_serial_initialize(); // 初始化 IMX 系列的串口...

IMX 的串口设备初始化函数为 mxc_serial_initialize():

// uboot/drivers/serial/serial_mxc.c
void mxc_serial_initialize(void)
{serial_register(&mxc_serial_drv); // 注册串口设备
}

serial_register() 检查是否需要对串口设备中的函数进行重定位,然后将传入的串口设备与全局串口设备绑定:

// drivers/serial/serial.c
void serial_register(struct serial_device *dev)
{// 重定位串口设备的成员函数
#ifdef CONFIG_NEEDS_MANUAL_RELOCif (dev->start)dev->start += gd->reloc_off;if (dev->stop)dev->stop += gd->reloc_off;if (dev->setbrg)dev->setbrg += gd->reloc_off;if (dev->getc)dev->getc += gd->reloc_off;if (dev->tstc)dev->tstc += gd->reloc_off;if (dev->putc)dev->putc += gd->reloc_off;if (dev->puts)dev->puts += gd->reloc_off;
#endifdev->next = serial_devices; // 将串口设备加入设备链表serial_devices = dev; // 绑定全局串口设备
}

11.打印 uboot 已在内存中运行的调试信息 - initr_announce()

打印调试信息,指示 uboot 程序目前已在内存中运行:

// common/board_r.c
static int initr_announce(void)
{debug("Now running in RAM - U-Boot at: %08lx\n", gd->relocaddr);return 0;
}

12.初始化 eMMC - initr_mmc()

initr_mmc() 调用 mmc_initialize() 初始化所有 SD/eMMC 设备,并将其加入设备链表:

// common/board_r.c
static int initr_mmc(void)
{puts("MMC:   ");mmc_initialize(gd->bd); // 初始化 SD/eMMC 设备return 0;
}
----------------------------------// drivers/mmc/mmc.c
int mmc_initialize(bd_t *bis)
{static int initialized = 0;int ret;if (initialized)    /* Avoid initializing mmc multiple times */return 0;initialized = 1; // 标记 SD/eMMC 设备已初始化INIT_LIST_HEAD (&mmc_devices); // 初始化 SD/eMMC 设备链表cur_dev_num = 0; // 设置当前 SD/eMMC 设备的编号ret = mmc_probe(bis);|--> board_mmc_init() // 初始化 SD/eMMC 设备的引脚、初始化 SD/eMMC 控制器if (ret)return ret;...do_preinit(); // 简单测试每个 SD/eMMC 设备是否可以正常工作return 0;
}

IMX6ULL 有 FSL_SDHC:0 和 FSL_SDHC:1 两个 SD/eMMC 设备,分别为 SD 卡和 eMMC 存储器

13.初始化环境变量 - initr_env()

环境变量重定位,获取设备树的地址以及 Linux 内核的加载地址:

// common/board_r.c
static int initr_env(void)
{/* initialize environment */if (should_load_env()) // 检查环境变量是否需要重定位env_relocate(); // 环境变量重定位elseset_default_env(NULL);
#ifdef CONFIG_OF_CONTROL // 是否启用设备树setenv_addr("fdtcontroladdr", gd->fdt_blob); // 将设备树的地址加入环境变量
#endif/* Initialize from environment */load_addr = getenv_ulong("loadaddr", 16, load_addr); // 获取 Linux 内核的加载地址...return 0;
}

14.添加其他 stdio 设备 - stdio_add_devices()

初始化时一般将串口作为 stdio 的默认设备,但 stdio 也可以使用其他设备,如终端、显示器、键盘等

IMX6ULL 在这里调用 drv_video_init() 将 LCD 加入 stdio 设备:

// common/stdio.c
int stdio_add_devices(void)
{...
# if defined(CONFIG_LCD)drv_lcd_init (); // 初始化 LCD
# endif...return 0;
}

drv_lcd_init() 初始化 LCD 使用的内存,并调用 stdio_register() 将 LCD 注册为 stdio 设备:

// common/lcd.c
int drv_lcd_init(void)
{struct stdio_dev lcddev;int rc;lcd_base = map_sysmem(gd->fb_base, 0); // 为 LCD 分配内存lcd_init(lcd_base); // 初始化 LCD 的显存、终端等/* Device initialization */memset(&lcddev, 0, sizeof(lcddev));strcpy(lcddev.name, "lcd"); // 设置 LCD 的设备名称lcddev.ext   = 0;           /* No extensions */lcddev.flags = DEV_FLAGS_OUTPUT;    /* Output only */lcddev.putc  = lcd_stub_putc;       /* 'putc' function */lcddev.puts  = lcd_stub_puts;       /* 'puts' function */rc = stdio_register(&lcddev); // 将 LCD 注册为 stdio 设备return (rc == 0) ? 1 : rc;
}

15.初始化跳转表 - initr_jumptable()

跳转表 jumptable 中使用 EXPORT_FUNC() 宏定义了一系列函数:

// include/exports.h
struct jt_funcs {
#define EXPORT_FUNC(impl, res, func, ...) res(*func)(__VA_ARGS__);
#include <_exports.h>
#undef EXPORT_FUNC
};
-------------------------------------------------------------------// include/_exports.hEXPORT_FUNC(get_version, unsigned long, get_version, void)EXPORT_FUNC(getc, int, getc, void)...EXPORT_FUNC(printf, int, printf, const char*, ...)...EXPORT_FUNC(getenv, char  *, getenv, const char*)EXPORT_FUNC(setenv, int, setenv, const char *, const char *)...

EXPORT_FUNC() 宏的第一个参数不使用,第二个参数为返回类型,第三个参数为函数名,剩下的为传参,其展开形式如下:

EXPORT_FUNC(getc, int, getc, void)
// 宏展开如下:
int(* getc)(void);

通过 gd->jt 可以调用这些函数,例如 gd->jt->getenv()

initr_jumptable() 调用 jumptable_init() 为这些函数分配内存:

// common/board_r.c
static int initr_jumptable(void)
{jumptable_init(); // 初始化跳转表return 0;
}
------------------------------------------// common/exports.c
void jumptable_init(void)
{gd->jt = malloc(sizeof(struct jt_funcs)); // 为跳转表分配内存
#include <_exports.h> // 包含相关函数的声明
}

16.初始化控制台 - console_init_r()

初始化输入 IN、输出 OUT、错误输出 ERR 所使用的控制台/终端 (IMX6ULL 使用串口作为 I/O 设备):

// common/console.c
int console_init_r(void)
{struct stdio_dev *inputdev = NULL, *outputdev = NULL;int i;struct list_head *list = stdio_get_list();struct list_head *pos;struct stdio_dev *dev;.../* Scan devices looking for input and output devices */list_for_each(pos, list) { // 绑定 I/O 设备:串口dev = list_entry(pos, struct stdio_dev, list); // 查找 stdio 设备// 绑定输入设备:串口if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {inputdev = dev;} // 绑定输出设备:串口if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {outputdev = dev;}if(inputdev && outputdev)break;}/* Initializes output console first */if (outputdev != NULL) { // 设置普通输出和错误输出所使用的文件console_setfile(stdout, outputdev);console_setfile(stderr, outputdev);...}/* Initializes input console */if (inputdev != NULL) { // 设置输入所使用的文件console_setfile(stdin, inputdev);...}#ifndef CONFIG_SYS_CONSOLE_INFO_QUIETstdio_print_current_devices(); // 打印当前的控制台类型
#endif /* CONFIG_SYS_CONSOLE_INFO_QUIET *//* Setting environment variables */for (i = 0; i < 3; i++) { // 设置环境变量setenv(stdio_names[i], stdio_devices[i]->name);}// 标记初始化已完成gd->flags |= GD_FLG_DEVINIT;    /* device initialization completed */...// 将之前缓存在 Buffer 中的数据输出print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL);return 0;
}

17.中断初始化 - interrupt_init()

通过内联汇编向 CPSR 寄存器写值,设置中断栈的起始地址:

// arch/arm/lib/interrupts.c
int interrupt_init (void)
{unsigned long cpsr;/** setup up stacks if necessary*/IRQ_STACK_START = gd->irq_sp - 4;IRQ_STACK_START_IN = gd->irq_sp + 8;FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;// 设置 CPSR 寄存器,设置中断栈的地址__asm__ __volatile__("mrs %0, cpsr\n": "=r" (cpsr):: "memory");__asm__ __volatile__("msr cpsr_c, %0\n""mov sp, %1\n":: "r" (IRQ_MODE | I_BIT | F_BIT | (cpsr & ~FIQ_MODE)),"r" (IRQ_STACK_START): "memory");__asm__ __volatile__("msr cpsr_c, %0\n""mov sp, %1\n":: "r" (FIQ_MODE | I_BIT | F_BIT | (cpsr & ~IRQ_MODE)),"r" (FIQ_STACK_START): "memory");__asm__ __volatile__("msr cpsr_c, %0":: "r" (cpsr): "memory");return arch_interrupt_init(); // 空函数
}

18.中断使能 - initr_enable_interrupts()

通过内联汇编配置 CPSR 寄存器,使能 IRQ 和 FIQ:

// common/board_r.c
static int initr_enable_interrupts(void)
{enable_interrupts();return 0;
}
--------------------------------------------// arch/arm/lib/interrupts.c
void enable_interrupts (void)
{unsigned long temp;// 中断 IRQ 和 FIQ__asm__ __volatile__("mrs %0, cpsr\n""bic %0, %0, #0x80\n""msr cpsr_c, %0": "=r" (temp):: "memory");
}

19.从环境变量中获取 MAC 地址 - initr_ethaddr()

从环境变量中获取 MAC 地址,保存至 bd->bi_enetaddr 中:

// common/board_r.c
static int initr_ethaddr(void)
{bd_t *bd = gd->bd;/* kept around for legacy kernels only ... ignore the next section */eth_getenv_enetaddr("ethaddr", bd->bi_enetaddr); // 从环境变量中读取 MAC 地址
#ifdef CONFIG_HAS_ETH1eth_getenv_enetaddr("eth1addr", bd->bi_enet1addr);
#endif
#ifdef CONFIG_HAS_ETH2...return 0;
}

20.设置启动模式 - board_late_init()

设置开发板的启动模式和启动信息,设置 LCD 屏幕的型号:

// board/freescale/mx6ullevk/mx6ullevk.c
int board_late_init(void)
{// 设置启动模式、说明信息add_board_boot_modes(board_boot_modes); // 设置开发板名称setenv("board_name", "EVK"); // 设置开发板的版本if (is_mx6ull_9x9_evk())setenv("board_rev", "9X9");elsesetenv("board_rev", "14X14");#ifdef CONFIG_ENV_IS_IN_MMC // 环境变量是否存储在 eMMC 设备中board_late_mmc_env_init();
#endif// 复位看门狗set_wdog_reset((struct wdog_regs *)WDOG1_BASE_ADDR);// 设置 LCD 屏幕型号select_display_dev();return 0;
}

21.初始化以太网 - initr_net()

初始化 MAC 控制器并复位 PHY:

// common/board_r.c
static int initr_net(void)
{puts("Net:   ");eth_initialize(); // 初始化 MAC 控制器
#if defined(CONFIG_RESET_PHY_R)debug("Reset Ethernet PHY\n");reset_phy(); // PHY 复位
#endifreturn 0;
}

22.启动命令行或 Linux 内核 - run_main_loop()

run_main_loop() 是一个死循环,不断调用 main_loop():

// common/board_r.c
static int run_main_loop(void)
{.../* main_loop() can return to retry autoboot, if so just run it again */for (;;)main_loop();return 0;
}

在 main_loop() 中进行倒计时,并检查倒计时结束前是否有按键按下:

  • 若没有按键按下,则会启动 Linux 内核;

  • 若检测到按键按下,则会启动 uboot 命令行;

// common/main.c
void main_loop(void)
{const char *s;// 记录当前启动阶段bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");...// 初始化命令行cli_init();...// 设置定时时长并读取启动命令s = bootdelay_process();...// 检查倒计时是否结束// 如果倒计时结束则会启动 Linux 内核// 如果在倒计时结束前检测到按键输入,则会返回并启动 uboot 命令行autoboot_command(s);// 启动 uboot 命令行cli_loop();
}

下一篇:UBoot 启动流程 (5) - UBoot 运行阶段 - main_loop

http://www.dtcms.com/a/264822.html

相关文章:

  • 深入解析外观模式(Facade Pattern):简化复杂系统的优雅设计
  • 如何系统性评估运维自动化覆盖率:方法与关注重点
  • 拐点的可导性的图像区别
  • 回顾JAVA中的锁机制
  • 解决在Pom文件中写入依赖坐标后, 刷新Maven但是多次尝试都下载不下来
  • Maven工具学习使用(十三)——Maven Wrapper命令解析与使用
  • 告别 ifconfig:openEuler 网络配置的现代化之路
  • Linux 启动过程流程图--ARM版
  • 高速公路闲置土地资源化利用:广西浦北互通3MW分布式光伏监控实践
  • STEP 7-MicroWIN SMART软件安装及运行故障全方位解决
  • 【51单片机单595点阵8按键调节速度方向花样】2022-6-18
  • 使用OpenCV训练自有模型的实践
  • 飞算科技:以自主技术创新推动行业数字化升级
  • Java学习第五部分——API部分
  • 【DICOM后处理】qt+vs 实现DICOM数据四视图显示
  • LeetCode--39.组合总和
  • Oracle 数据塑形:行列转换与集合运算
  • QT记事本3——下拉框comboBox、下拉框编码值传给QTextStream类
  • 【BERT_Pretrain】Wikipedia_Bookcorpus数据预处理(二)
  • Electron 快速上手
  • vscode vim插件示例json意义
  • C++ 第四阶段 文件IO - 第一节:ifstream/ofstream操作
  • JavaScript---查询数组符合条件的元素
  • 解决 npm install canvas@2.11.2 失败的问题
  • 【公司环境下发布个人NPM包完整教程】
  • SPI、I2C和UART三种串行通信协议的--------简单总结
  • NLP:文本张量表示方法
  • 【安全工具】SQLMap 使用详解:从基础到高级技巧
  • 【字节跳动】数据挖掘面试题0001:打车场景下POI与ODR空间关联查询
  • C++实现状态机