• 沒有找到結果。

U-Boot 启动过程

在文檔中 目 录 (頁 143-148)

第 6 章 Bootloader

6.3 U-Boot 的调试

6.3.3 U-Boot 启动过程

break;

} return;

}

/** 显示启动进度函数,在比较重要的阶段,设置三个灯为亮的状态(1, 5, 15)*/

void show_boot_progress (int status) {

switch(status) {

case 1: csb226_set_led(0,1); break;

case 5: csb226_set_led(1,1); break;

case 15: csb226_set_led(2,1); break;

} return;

}

这样,在 U-Boot 启动过程中就可以通过 show_boot_progresss 指示执行进度。比如 hang() 函数是系统出错时调用的函数,这里需要根据特定的开发板给定显示的参数值。

void hang (void) {

puts ("### ERROR ### Please RESET the board ###\n");

#ifdef CONFIG_SHOW_BOOT_PROGRESS show_boot_progress(-30);

#endif

for (;;);

}

6.3.3 U-Boot 启动过程

尽管有了调试跟踪手段,甚至也可以通过串口打印信息了,但是不一定能够判断出错原 因。如果能够充分理解代码的启动流程,那么对准确地解决和分析问题很有帮助。

开发板上电后,执行 U-Boot 的第一条指令,然后顺序执行 U-Boot 启动函数。函数调用 顺序如图 6.3 所示。

看一下 board/smsk2410/u-boot.lds 这个链接脚本,可以知道目标程序的各部分链接顺序。

第一个要链接的是 cpu/arm920t/start.o,那么 U-Boot 的入口指令一定位于这个程序中。下面 详细分析一下程序跳转和函数的调用关系以及函数实现。

1.cpu/arm920t/start.S

这个汇编程序是 U-Boot 的入口程序,开头就是复位向量的代码。

tyw藏书

cpu_init_crit

memsetup

relocate

stack_setup

start_armboot( )

init_sequence[ ]

getenv( )

main_loop( ) reset

图 6.3 U-Boot 启动代码流程图

_start: b 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 //中断向量

/* the actual reset code */

reset: //复位启动子程序 /* 设置 CPU 为 SVC32 模式 */

mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0 /* 关闭看门狗 */

/* 这些初始化代码在系统重起的时候执行,运行时热复位从 RAM 中启动不执行 */

#ifdef CONFIG_INIT_CRITICAL bl cpu_init_crit

#endif

tyw藏书

relocate: /* 把 U-Boot 重新定位到 RAM */

adr r0, _start /* r0 是代码的当前位置 */

ldr r1, _TEXT_BASE /* 测试判断是从 Flash 启动,还是 RAM */

cmp r0, r1 /* 比较 r0 和 r1,调试的时候不要执行重定位 */

beq stack_setup /* 如果 r0 等于 r1,跳过重定位代码 */

/* 准备重新定位代码 */

ldr r2, _armboot_start ldr r3, _bss_start

sub r2, r3, r2 /* r2 得到 armboot 的大小 */

add r2, r0, r2 /* r2 得到要复制代码的末尾地址 */

copy_loop: /* 重新定位代码 */

ldmia r0!, {r3-r10} /*从源地址[r0]复制 */

stmia r1!, {r3-r10} /* 复制到目的地址[r1] */

cmp r0, r2 /* 复制数据块直到源数据末尾地址[r2] */

ble copy_loop

/* 初始化堆栈等 */

stack_setup:

ldr r0, _TEXT_BASE /* 上面是 128 KiB 重定位的 u-boot */

sub r0, r0, #CFG_MALLOC_LEN /* 向下是内存分配空间 */

sub r0, r0, #CFG_GBL_DATA_SIZE /* 然后是 bdinfo 结构体地址空间 */

#ifdef CONFIG_USE_IRQ

sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

sub sp, r0, #12 /* 为 abort-stack 预留 3 个字 */

clear_bss:

ldr r0, _bss_start /* 找到 bss 段起始地址 */

ldr r1, _bss_end /* bss 段末尾地址 */

mov r2, #0x00000000 /* 清零 */

clbss_l:str r2, [r0] /* bss 段地址空间清零循环... */

add r0, r0, #4 cmp r0, r1 bne clbss_l

/* 跳转到 start_armboot 函数入口,_start_armboot 字保存函数入口指针 */

ldr pc, _start_armboot

_start_armboot: .word start_armboot //start_armboot 函数在 lib_arm/board.c 中实现

/* 关键的初始化子程序 */

tyw藏书

cpu_init_crit:

…… //初始化 CACHE,关闭 MMU 等操作指令 /* 初始化 RAM 时钟。

* 因为内存时钟是依赖开发板硬件的,所以在 board 的相应目录下可以找到 memsetup.S 文件。

*/

mov ip, lr

bl memsetup //memsetup 子程序在 board/smdk2410/memsetup.S 中实现 mov lr, ip

mov pc, lr

2.lib_arm/board.c

start_armboot 是 U-Boot 执行的第一个 C 语言函数,完成系统初始化工作,进入主循环,

处理用户输入的命令。

void start_armboot (void) {

DECLARE_GLOBAL_DATA_PTR;

ulong size;

init_fnc_t **init_fnc_ptr;

char *s;

/* Pointer is writable since we allocated a register for it */

gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

/* compiler optimization barrier needed for GCC >= 3.4 */

__asm__ __volatile__("": : :"memory");

memset ((void*)gd, 0, sizeof (gd_t));

gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

memset (gd->bd, 0, sizeof (bd_t));

monitor_flash_len = _bss_start - _armboot_start;

/* 顺序执行 init_sequence 数组中的初始化函数 */

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) {

hang ();

} }

/*配置可用的 Flash */

size = flash_init ();

display_flash_config (size);

/* _armboot_start 在 u-boot.lds 链接脚本中定义 */

mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

tyw藏书

/* 配置环境变量,重新定位 */

env_relocate ();

/* 从环境变量中获取 IP 地址 */

gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

/* 以太网接口 MAC 地址 */

……

devices_init (); /* 获取列表中的设备 */

jumptable_init ();

console_init_r (); /* 完整地初始化控制台设备 */

enable_interrupts (); /* 使能例外处理 */

/* 通过环境变量初始化 */

if ((s = getenv ("loadaddr")) != NULL) {

load_addr = simple_strtoul (s, NULL, 16);

}

/* main_loop()总是试图自动启动,循环不断执行 */

for (;;) {

main_loop (); /* 主循环函数处理执行用户命令 -- common/main.c */

}

/* NOTREACHED - no way out of command loop except booting */

}

3.init_sequence[]

init_sequence[]数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下 列注释中。

init_fnc_t *init_sequence[] = {

cpu_init, /* 基本的处理器相关配置 -- cpu/arm920t/cpu.c */

board_init, /* 基本的板级相关配置 -- board/smdk2410/smdk2410.c */

interrupt_init, /* 初始化例外处理 -- cpu/arm920t/s3c24x0/interrupt.c */

env_init, /* 初始化环境变量 -- common/cmd_flash.c */

init_baudrate, /* 初始化波特率设置 -- lib_arm/board.c */

serial_init, /* 串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */

console_init_f, /* 控制台初始化阶段 1 -- common/console.c */

display_banner, /* 打印 u-boot 信息 -- lib_arm/board.c */

dram_init, /* 配置可用的 RAM -- board/smdk2410/smdk2410.c */

display_dram_config, /* 显示 RAM 的配置大小 -- lib_arm/board.c */

NULL, };

tyw藏书

在文檔中 目 录 (頁 143-148)