version: uboot-2016-11
搜索Makefile文件,当我们执行make操作时,相应的依赖项如下所示:
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check
-| u-boot.bin: u-boot-nodtb.bin FORCE
-| u-boot-nodtb.bin: u-boot FORCE $(call if_changed,objcopy) $(call DO_STATIC_RELA,u-boot,u-boot-nodtb.bin,$(CONFIG_SYS_TEXT_BASE))
-| u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
-| u-boot-init := $(head-y)
-| u-boot-main := $(libs-y)
-| arch/arm/Makefile:74: head-y := arch/arm/cpu/$(CPU)/start.o
-| ./Makefile:638 libs-y += lib/
-| u-boot.lds: $(LDSCRIPT) prepare FORCE
我们最终需要的是u-boot.bin,而u-boot.bin又由u-boot-init
,u-boot-main
,以及连接文件u-boot.lds
组成。
1.u-boot-init :包含了start.o,位于生成文件的头部位置,用于对系统进行初始化处理,另外有一些因为内存限制的原因需要预先处理的代码,也需要在start.o中实现。
2. u-boot-main *:包含了其他编译生成的文件,$(libs-y)在Makefile中有定义,根据宏定义会有些取舍。在libs-y的组成中我们会发现有一些宏需要确定,例如:VENDOR
,BOARDDIR
等。搜索变量,可以发现在顶层目录下的config.mk有类似定义的地方。
./config.mk:
ARCH := $(CONFIG_SYS_ARCH:"%"=%)
CPU := $(CONFIG_SYS_CPU:"%"=%)
BOARD := $(CONFIG_SYS_BOARD:"%"=%)
VENDOR := $(CONFIG_SYS_VENDOR:"%"=%)
SOC := $(CONFIG_SYS_SOC:"%"=%)
CPUDIR=arch/$(ARCH)/cpu$(if $(CPU),/$(CPU),)
BOARDDIR = $(VENDOR)/$(BOARD)
sinclude $(srctree)/arch/$(ARCH)/config.mk # include architecture dependend rules
sinclude $(srctree)/$(CPUDIR)/config.mk # include CPU specific rules
sinclude $(srctree)/$(CPUDIR)/$(SOC)/config.mk # include SoC specific rules
sinclude $(srctree)/board/$(BOARDDIR)/config.mk # include board specific rules
export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR
继续搜索CONFIG_SYS_
开头的宏,可以发现在board/samsung/jz2440(目标板)/Kconfig
中找到定义。
if TARGET_JZ2440//对应着CONFIG_TARGET_JZ2440,在jz2440_defconfig中定义
config SYS_BOARD
default "jz2440"
config SYS_VENDOR
default "samsung"
config SYS_SOC
default "s3c24x0"
config SYS_CONFIG_NAME
default "jz2440"
endif
可以确定的是当我们执行make jz2440_defconfig
时,执行了某些的操作使board/samsung/jz2440(目标板)/Kconfig
生效了,我们来分析一下,首先在Makefile能找到的唯一与jz2440_defconfig匹配的只有%config
:
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
#而在scripts/Kbuild.include中有这样的定义:
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj
#因此可以展开为:
jz2440_defconfig:
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig jz2440_defconfig
scripts/Makefile.build:
# Modified for U-Boot
-include include/config/auto.conf //在这里发现了我们想要的宏定义
#在auto.conf.cmd中有如下定义,$(deps_config)包含了源码目录下的所有Kconfig文件:
include/config/auto.conf: \
$(deps_config)
至此,可以判断,因为所有的Kconfig都被包含了,所以在make jz2440_defconfig
时定义的CONFIG_TARGET_JZ2440,使得board/samsung/jz2440(目标板)/Kconfig
中的配置生效了。
-
- u-boot.lds * :按着以下的路径寻找,找到的第一个即作为实际的连接文件。
ifndef LDSCRIPT//依次往下找
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.lds
endif
endif
arch/arm/cpu/u-boot.lds:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
/* 指定可执行image文件的全局入口点,通常这个地址都放在ROM(flash)0x0的位置。必须使编译器知道这个地址,通常都是修改此处来完成 */
. = 0x00000000;//从0x0位置开始
. = ALIGN(4);//代码以4字节对齐
.text :
{
*(.__image_copy_start)
/* u-boot将自己copy到RAM,此为需要拷贝的程序的start */
*(.vectors)
CPUDIR/start.o (.text*)
*(.text*)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }/*只读数据段*/
. = ALIGN(4);
.data : {/*代码段*/
*(.data*)
}
. = ALIGN(4);
. = .;
. = ALIGN(4);
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
/* .data 段结束后,紧接着存放u-boot自有的一些function,例如u-boot command等*/
}
. = ALIGN(4);
.__efi_runtime_start : {
*(.__efi_runtime_start)
}
.efi_runtime : {
*(efi_runtime_text)
*(efi_runtime_data)
}
.__efi_runtime_stop : {
*(.__efi_runtime_stop)
}
.efi_runtime_rel_start :
{
*(.__efi_runtime_rel_start)
}
.efi_runtime_rel : {
*(.relefi_runtime_text)
*(.relefi_runtime_data)
}
.efi_runtime_rel_stop :
{
*(.__efi_runtime_rel_stop)
. = ALIGN(4);
.image_copy_end :
{
*(.__image_copy_end)
}
.rel_dyn_start :
{
*(.__rel_dyn_start)
}
.rel.dyn : {
*(.rel*)
}
/* 动态链接符存放在的段,只要给这里面对额符号加上一定的偏移,拷贝到内存中代码*/
.rel_dyn_end :
{
*(.__rel_dyn_end)
}
/* 动态链接符段结束*/
.end :
{
*(.__end)
}
_image_binary_end = .;
/* bin文件结束 */
/*
* Deprecated: this MMU section is used by pxa at present but
* should not be used by new boards/CPUs.
*/
. = ALIGN(4096);
.mmutable : {
*(.mmutable)
}
/*
* Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c
* __bss_base and __bss_limit are for linker only (overlay ordering)
*/
/* bss段的描述 */
.bss_start __rel_dyn_start (OVERLAY) : {
KEEP(*(.__bss_start));
__bss_base = .;
}
.bss __bss_base (OVERLAY) : {
*(.bss*)
. = ALIGN(4);
__bss_limit = .;
}
.bss_end __bss_limit (OVERLAY) : {
KEEP(*(.__bss_end));
}
/* bss段的描述结束 */
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { *(.dynstr*) }
.dynamic : { *(.dynamic*) }
.plt : { *(.plt*) }
.interp : { *(.interp*) }
.gnu.hash : { *(.gnu.hash) }
.gnu : { *(.gnu*) }
.ARM.exidx : { *(.ARM.exidx*) }
.gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
}
从start.S开始分析,以下是start.S的主要流程:
reset:
|-- 进入保护模式
|-- 关闭看门狗
|-- 关中断
|-- #ifndef CONFIG_SKIP_LOWLEVEL_INIT
|-- lowlevel_init[lowlevel_init.S]: dram设置
|-- _main[crt0.S]
ps:当我们在内存中直接执行uboot时,由于此时dram肯定是可以用的,已经被当前运行的uboot初始化好了,不用也不可再进行初始化,否侧刚加载到内存中的uboot将会被擦除。此时需要定义宏CONFIG_SKIP_LOWLEVEL_INIT
跳过dram初始化。
main[arch/arm/lib/crt0.S]:
|--- board_init_f_alloc_reserve[common/init]
|--- board_init_f_init_reserve[common/init]
|--- board_init_f
|--- relocate_code & relocate_vectors & clear bss
|--- board_init_r
- board_init_f_alloc_reserve GD是16字节的对齐的,堆栈分配从上往下分配,因为GD是最后分配的,所以返回的地址即为GD的首地址(GD内部地址是从下往上的,所以首地址为最低位地址)。
ulong board_init_f_alloc_reserve(ulong top)
{
/* Reserve early malloc arena */
#if defined(CONFIG_SYS_MALLOC_F)
top -= CONFIG_SYS_MALLOC_F_LEN;//如果定义了CONFIG_SYS_MALLOC_F_LEN,则要相应的减去占用的长度
#endif
/* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
top = rounddown(top-sizeof(struct global_data), 16);//四舍五入为16 的倍数
return top;
}
|--CONFIG_SYS_INIT_SP_ADDR
|--CONFIG_SYS_MALLOC_F
|--top(GD)
|--
- board_init_f_init_reserve
有些架构的GD需要调用一次
arch_setup_gd()
,因此,在调用arch_setup_gd()
之前,统一使用gd_ptr
,从arch_setup_gd()
返回之后才可以使用gd->
。
void board_init_f_init_reserve(ulong base)
{
struct global_data *gd_ptr;
#ifndef _USE_MEMCPY
int *ptr;
#endif
/*
* 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 */
#ifdef _USE_MEMCPY
memset(gd_ptr, '\0', sizeof(*gd));
#else
for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
*ptr++ = 0;
#endif
/* set GD unless architecture did it already */
#if !defined(CONFIG_ARM) //arm架构不执行arch_setup_gd(),X86需要执行自己实现的arch_setup_gd()。
arch_setup_gd(gd_ptr);
#endif
/* 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 defined(CONFIG_SYS_MALLOC_F)
/* go down one 'early malloc arena' */
gd->malloc_base = base;
/* next alloc will be higher by one 'early malloc arena' size */
base += CONFIG_SYS_MALLOC_F_LEN; //照代码的逻辑,此时base = CONFIG_SYS_INIT_SP_ADDR;
#endif
}
- board_init_f:主要是实现了一些初始化工作,以及重定位的位置分配,其中在console_init_f之后才可以调用串口打印。代码中也是第一时间输出了cpu信息。
void board_init_f(ulong boot_flags)
{
...
if (initcall_run_list(init_sequence_f))
hang();
...
}
static init_fnc_t init_sequence_f[] = {
...
setup_mon_len,
board_early_init_f,
timer_init, /* initialize timer */
env_init, /* initialize environment */
init_baud_rate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
print_cpuinfo, /* display cpu info (and speed) */
dram_init, /* configure available RAM banks */
setup_dest_addr,
setup_machine,
reserve_global_data,
reserve_arch,
reserve_stacks,
setup_dram_config,
show_dram_config,
display_new_sp,
setup_reloc,
NULL,
};
-
- relocate_code & relocate_vectors & clear bss*:这一部分主要是根据board_init_f中定义的位置,将当前的uboot代码及变量搬到指定地点,并清除bss段。
ps:笔者在实际移植过程中遇到了一个问题,每次代码执行到这一部分,就卡死了。后来通过修改内存大小
PHYS_SDRAM_1_SIZE
实现了uboot的加载。猜测是因为在重定位过程中,与运行中的uboot在内存区域产生了冲突,导致uboot没能顺利完成重定位。
- relocate_code & relocate_vectors & clear bss*:这一部分主要是根据board_init_f中定义的位置,将当前的uboot代码及变量搬到指定地点,并清除bss段。
ps:笔者在实际移植过程中遇到了一个问题,每次代码执行到这一部分,就卡死了。后来通过修改内存大小
-
- board_init_r*:与board_init_f一样,在这个阶段有一系列的函数需要依次执行,在移植过程中,遇到具体问题可以具体去看。下面是比较重要的几个函数,在需要支持NOR,NAND,NET时需要去做适配。最后uboot进入main loop,接收用户命令并执行。
init_fnc_t init_sequence_r[] = {
board_init, /* Setup chipselects */
initr_flash, //NOR flash
initr_nand, //nand flash
initr_net, //net
run_main_loop,//main loop
}
- 编译器:arm-2014.05-29-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
一、准备环境
由于是移植到自己的开发板上,免不了需要做一些改动才能运行,我们将基于smdk2410进行修改工作,为了验证交叉编译环境是否可用,先编译一把smdk2410的u-boot,如果没有问题,说明编译环境是可以工作了。
二、拷贝smdk2410开发板相关文件,准备进行修改。
文件:
board/samsung/smdk2410 --> board/samsung/jz2440
include/configs/smdk2410.h --> include/configs/jz22440.h
drivers/mtd/nand/s3c2410_nand.c --> drivers/mtd/nand/s3c2440_nand.c
configs/smdk2410_defconfig --> configs/jz2440_defconfig
内容:
1.上述新复制的文件中所有smdk2410,s3c2410 都改为 jz2440 , s3c2440
2.arch/arm/Kconfig 中所有smdk2410,s3c2410 都改为 jz2440 , s3c2440
3../scripts/config_whitelist.txt 中所有有smdk2410,s3c2410 的都添加相应的 jz2440 , s3c2440,需要注意的是这个文件会对文件内容排序,所以要注意顺序,放到该放的位置。
==阶段成果:使用jz2440_defconfig可以编译成功。==
三、根据芯片手册,修改内容
arch/arm/cpu/arm920t/start.S
|-- lowlevel_init(board/samsung/jz2440/lowlevel_init.S)
|-- _main(arch/arm/lib/crt0.S)
_main执行顺序:
1.为调用board_init_f()设置初始环境。这个环境只提供一个堆栈和一个存储GD(“全局数据”)结构的位置,两者都位于一些可用的RAM (SRAM,锁定缓存……)中。在此上下文中,变量全局数据(无论是否初始化)都不可用;只有常量初始化的数据可用。在 board_init_f()被调用之前GD应该初始化为零
2.调用board_init_f ()。该函数为从系统RAM (DRAM、DDR…)执行准备硬件。由于系统RAM可能还不可用,因此board_init_f()必须使用当前GD来存储必须传递到后续阶段的任何数据。这些数据包括重新定位目标、未来堆栈和未来GD位置。
3.设置中间环境,其中堆栈和GD是由系统RAM中的board_init_f()分配的,但是BSS和初始化的非const数据仍然不可用。
4a.对于适当的U-Boot (不是SPL), 调用relocate_code(). 此函数将U-Boot从当前位置重新定位到由board_init_f()计算的重新定位目的地。
4b.对于SPL, board_init_f()只返回(到crt0)。SPL中没有代码重定位。
5.设置调用board_init_r()的最终环境。这个环境有BSS(初始化为0)、非const数据(初始化为它们的预期值)和系统RAM中的堆栈(SPL将堆栈和GD移动到RAM中是可选的,请参见CONFIG_SPL_STACK_R)。GD保留了board_init_f()设置的值。
6.对于适当的U-Boot (不是SPL),一些cpu此时还有一些内存方面的工作要做,因此调用c_runtime_cpu_setup。
7.切换到 board_init_r().
调试记录:
1.修改完寄存器相关的值后,加载uboot,没有得到输出
解决:在jz2440.h中添加宏定义#define CONFIG_SKIP_LOWLEVEL_INIT
,由于不添加宏,则会调用lowlevel_init将内存初始化,那么被加载到内存的u-boot.bin也会被清除,所以需要跳过。
2.系统挂死,添加DEBUG宏后,最后的打印显示board_init_f结束,但是board_init_r还未进入。
3.修改内存总大小之后,uboot启动成功,顺利进入main loop。
四、dm9000 配置
1.打开宏配置
#define CONFIG_DRIVER_DM9000 /*DM9000*/
#define CONFIG_DM9000_BASE 0x20000000
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
2.修改board_eth_init
int board_eth_init(bd_t *bis)
{
int rc = 0;
#ifdef CONFIG_CS8900
rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
#endif
#ifdef CONFIG_DRIVER_DM9000
rc = dm9000_initialize(bis);
#endif
return rc;
}
五、NOR flash配置
笔者开发板使用的是MX29LV160DB,与兼容AMD。
首先,根据数据手册可知其内部被分为了35个SECT,所以需要在头文件中修改宏定义的值:
#define CONFIG_SYS_MAX_FLASH_SECT (35)
其次,因为是与AMD兼容的,所以架构中AMD的代码大部分都可以直接使用,需要修改的地方是读到device的id后的匹配部分:
这部分官方代码中没有定义,需要我们手动添加一下:
//drivers/mtd/jedec_flash.c
...
#define MX29LV160DB 0x2249
...
static const struct amd_flash_info jedec_table[] = {
#在表中添加如下字段
#ifdef CONFIG_SYS_FLASH_LEGACY_512Kx16
{
.mfr_id = (u16)MX_MANUFACT,
.dev_id = MX29LV160DB,
.name = "MXIC MX29LV160DB",
.uaddr = {
[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
},
.DevSize = SIZE_2MiB,
.CmdSet = CFI_CMDSET_AMD_LEGACY,
.NumEraseRegions= 4,
.regions = {
ERASEINFO(0x10000, 31),
ERASEINFO(0x08000, 1),
ERASEINFO(0x02000, 2),
ERASEINFO(0x04000, 1),
}
},
#endif
}
ps:笔者的JZ2440开发板需要将拨码开关拨到nor flash上才能使用nor flash,所以如果是nand flash启动方式的话,nor flash的id都读不到。
六、NAND FLASH配置
1.注释掉CONFIG_SYS_S3C2440_NAND_HWECC宏,使用软件ECC。
2.从s3c2410_nand.c拷贝过来的s3c2440_nand.c,需要将相关寄存器修改一下,下面是修改的宏定义,文件中其他设计到这些宏的地方都需要改。
#define S3C2440_NFCONT_EN (1<<0)
#define S3C2440_NFCONT_nFCE (1<<1)
#define S3C2440_NFCONT_INITECC (1<<4)
#define S3C2440_NFCONF_TACLS(x) ((x)<<12)
#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
#define S3C2440_ADDR_NALE 0x8
#define S3C2440_ADDR_NCLE 0xc
七、TF卡配置
庆幸的是大部分需要移植的工作在driver/mmc/s3c_sdi.c中已经做好了
1.在jz2440.h中调价以下宏定义
#define CONFIG_CMD_MMC
#define CONFIG_GENERIC_MMC
#define CONFIG_S3C_SDI
2.添加检测卡的函数实现
static int s3cmmc_priv_getcd(struct mmc *mmc)
{
ulong mmc_cd;
struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
mmc_cd = readl(&gpio->gpgcon) & ~(0x3<<16);
writel(mmc_cd,&gpio->gpgcon);
mmc_cd = (readl(&gpio->gpgdat)>>8) & 0x1;
if(mmc_cd)
return 0;
else
return 1;
}
int s3cmmc_initialize(...)
{
...
if(!priv)
return -ENOMEM;
+ priv->getcd = s3cmmc_priv_getcd;
...
}
3.执行fatls mmc 0 可以查看tf卡中的文件
4.执行fatload mmc 0 32000000 可以将文件加载到内存0x32000000的地方
- 编译器:arm-2014.05-29-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
- 内核版本:linux-4.20.9
一、准备环境
内核中有线程的s3c2410_defconfig配置文件,我们将在其基础上进行移植。为了验证交叉编译环境是否可用,先用s3c2410_defconfig进行一次编译,如果没有问题,说明编译环境是可以工作了。
笔者在成功编译之前,根据提示先安装了几个文件:
二、拷贝smdk2410开发板相关文件,准备进行修改。
文件:
kernel/linux-4.20.9/arch/arm/mach-s3c24xx/mach-smdk2440.c --> kernel/linux-4.20.9/arch/arm/mach-s3c24xx/mach-jz2440.c
内容:
1.上述新复制的文件中所有smdk2440都改为 jz2440
2.boot/u-boot-2016.11/arch/arm/include/asm/mach-types.h 中添加 MACH_TYPE_JZ2440 = 1995。
3.kernel/linux-4.20.9/arch/arm/tools/mach-types添加一行"jz2440 MACH_JZ2440 JZ2440 1995"
==阶段成果:kernel启动成功,在文件系统出挂死。==
调试记录:
1.机器码不匹配,添加机器码MACH_TYPE_JZ2440 = 1995。
2.系统挂死,没有打印,修改bootargs中的console=ttySAC0,115200,主要是波特率之前没有,通过打开串口调试的宏发现波特率为0,修改后正常输出。
三、自制文件系统
1.按照网上教程操作即可
2.需要将编译链下面的库拷贝到lib目录下
3.可以从别处拷贝/etc目录的内容放在/etc下面
==阶段成果:文件系统启动成功。==
调试记录:
1.报错:exitcode=0x00000004,使用readelf -A vmlinux查看CPU架构是V4T,而查看busybox显示是V5TE。
解决方法:
<1>make menuconfig 中指定架构 (-mcpu=arm920t) Additional CFLAGS
<2>任然同样的错,确定架构没有问题了,通过上网查找,发现有人是拷贝的armv4t中的库,故重新拷贝库文件,顺利启动。
四、移植RTC
1.当前版本的内核,rtc-s3c.c只支持设备树,所以直接编译是匹配不到RTC设备的。
2.将老版本的linux内核中的rtc-s3c.c拷贝过来,替换掉即可。