前面添加开发板的时候,在文件 arch/arm/Kconfig 添加:
config TARGET_MINI2440
bool "Support mini2440"
select CPU_ARM920T
select SUPPORT_SPL
所以如果选择mini2440 就会在菜单出现SPL支持,make menuconfig 看下:
选择Enable SPL,退出然后编译尝试下,出现错误 undefined reference to board_init_f
:
函数 board_init_f 在文件 arch/arm/lib/spl.c
有定义void __weak board_init_f(ulong dummy)
这里函数声明为weak类型的,说明我们可以定义自己的重名函数。事实上网上很多人都是这么做的,但是如果想用系统提供的 board_init_f ,该如何做?
通过分析arm/arm/lib/Makefile
可知这个函数依赖于宏SPL_BUILD 首先在 mini2440.h 里面加入宏定义
#define CONFIG_SPL_BUILD
编译还是有错:
drivers/mtd/onenand/onenand_spl.c: In function 'onenand_readw':
drivers/mtd/onenand/onenand_spl.c:34:15: error: 'CONFIG_SYS_ONENAND_BASE' undeclared (first use in this function)
先随便在 mini2440.h 定义一个数值等编译通过了再修改成正确的
#define CONFIG_SYS_ONENAND_BASE 0x0
编译,居然还有错!!! :
In file included from drivers/serial/serial.c:8:0:
drivers/serial/serial.c: In function 'serial_stub_putc':
include/common.h:872:19: error: expected identifier before 'do'
#define putc(...) do { } while (0)
看下 include/common.h:
/* stdout */
#if defined(CONFIG_SPL_BUILD) && !defined(CONFIG_SPL_SERIAL_SUPPORT)
#define putc(...) do { } while (0)
#define puts(...) do { } while (0)
#define printf(...) do { } while (0)
#define vprintf(...) do { } while (0)
#else
void putc(const char c);
void puts(const char *s);
int printf(const char *fmt, ...)
__attribute__ ((format (__printf__, 1, 2)));
int vprintf(const char *fmt, va_list args);
#endif
再看下 drivers/serial/serial.c:
static void serial_stub_putc(struct stdio_dev *sdev, const char ch)
{
struct serial_device *dev = sdev->priv;
dev->putc(ch);
}
common.h 的意思就是如果没有定义 SPL_SERIAL_SUPPORT
打印的系列函数就宏定义成空操作,但是在serial.c 中 dev->putc
经过编译预处理 也被替换,do
前面加上了 ->
肯定编译报错了。
怎么办?
在 mini2440.h
里面加入宏定义 CONFIG_SPL_SERIAL_SUPPORT
试下:
drivers/serial/built-in.o: In function `s3serial1_putc':
/tmp/u-boot-2016.01/drivers/serial/serial_s3c24x0.c:209: multiple definition of `s3serial1_putc'
drivers/built-in.o:/tmp/u-boot-2016.01/drivers/serial/serial_s3c24x0.c:209: first defined here
此时在链接阶段出现大量的serial 系列函数冲定义的错误,初步猜测是因为SPL 阶段的函数和u-boot阶段的函数符号重复导致,那么先不定义 CONFIG_SPL_SERIAL_SUPPORT
把 include/common.h 修改如下:
/* stdout */
#if 0 /*defined(CONFIG_SPL_BUILD) && !defined(CONFIG_SPL_SERIAL_SUPPORT) */
#define putc(...) do { } while (0)
.... ....
#endif
编译,依然有错:
arch/arm/lib/built-in.o: In function `_main':
/tmp/u-boot-2016.01/arch/arm/lib/crt0.S:86: undefined reference to `board_init_f_mem'
/tmp/u-boot-2016.01/arch/arm/lib/crt0.S:90: undefined reference to `board_init_f'
函数 board_init_f_mem
主要是初始化 结构体 global_data
,这里可以修改common/Makefile
:
# core
obj-y += init/
ifndef CONFIG_SPL_BUILD
obj-y += main.o
obj-y += exports.o
obj-y += hash.o
ifdef CONFIG_SYS_HUSH_PARSER
obj-y += cli_hush.o
endif
但是 board_init_f
到现在依然还提示未定义 ,经过分析发现前面的宏定义CONFIG_SPL_BUILD
是在配置了SPL之后默认就有的,不需要手动加入,需要加入的是宏定义 #define CONFIG_SPL_FRAMEWORK
;编译,结果还是有错:
/tmp/u-boot-2016.01/arch/arm/lib/crt0.S:133: undefined reference to `spl_relocate_stack_gd'
/tmp/u-boot-2016.01/arch/arm/lib/crt0.S:166: undefined reference to `board_init_r'
在 common/Makefile 加入一行:
ifdef CONFIG_SPL_BUILD
obj-y += spl/
obj-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o
obj-$(CONFIG_SPL_YMODEM_SUPPORT) += xyzModem.o
再次编译
common/built-in.o: In function `spl_init':
/tmp/u-boot-2016.01/common/spl/spl.c:157: undefined reference to `printf'
common/built-in.o: In function `board_boot_order':
/tmp/u-boot-2016.01/common/spl/spl.c:195: undefined reference to `spl_boot_device'
common/built-in.o: In function `board_init_r':
/tmp/u-boot-2016.01/common/spl/spl.c:347: undefined reference to `printf'
/tmp/u-boot-2016.01/common/spl/spl.c:380: undefined reference to `puts
是不是上面的把宏定义 CONFIG_SPL_SERIAL_SUPPORT
去掉导致的? 但是如果加上宏定义就会导致好多函数重定义,如果不加在这里就会 函数未定义!!
无奈,只好把上面的所有的未定义的全部注释掉,编译成功。
至此,SPL 初步编译通过。
生成的 u-boot-spl.bin 烧写在 nandflash 的前4K,主要作用就是初始化内存,把 u-boot 从nandflash里面读到内存里面,然后跳转到u-boot。这里流程不复杂,主要用到两个文件 : s3c2410_nand.c
s3c24x0 的nandflash驱动,和 nand_spl_simple.c
把生成的u-boot 反汇编一下:
arm-linux-objdump -D -m arm u-boot-spl > u-boot-spl.dis
可以看出也是从_start开始执行,跳转到reset,然后就是 cpu_init_crit
注意这里要加入条件编译控制,因为在SPL阶段初始化内存了,在u-boot启动阶段就不用初始化了。
然后就是_main函数,在这里调用了 board_init_f arch/arm/lib/spl.c
然后就调用 board_init_r common/spl/spl.c
注意这里的 board_init_r 跟u-boot里面调用的 还不是一个函数。
这里调用了 spl_load_image -> spl_nand_load_image
-> nand_init drivers/mtd/nand/nand_spl_simple.c
-> board_nand_init drivers/mtd/nand/s3c2410_nand.c
-> nand_spl_load_image drivers/mtd/nand/nand_spl_simple.c
-> nand_read_page -> this->read_buf
函数指针指向 nand_read_buf drivers/mtd/nand/s3c2410_nand.c
-> spl_set_header_raw_uboot 在这里设置u-boot的入口点,等待跳转spl_image.entry_point = CONFIG_SYS_UBOOT_START
函数都执行之后返回到 board_init_r ,调用 jump_to_image_no_args 进入 u-boot 入口点,开始执行u-boot:
image_entry_noargs_t image_entry =
(image_entry_noargs_t)(unsigned long)spl_image->entry_point;
image_entry();