从网上下载一个内核,需要如下步骤操作:
1 解压
2 打补丁(针对内核做的修改,diff工具生成)
3 配置(make menuconfig)
4 编译
这里重点说明配置和编译;
1 进入/arch/arm/configs 查看和自己目标板相似的配置文件: s3c2410_defconfig
2 进入内核根目录下面执行 make s3c2410_defconfig 进行初步配置,这一步完成后会在根目录生成一个.config文件
3 再 make menuconfig 进行详细配置,他去读.config文件,在这个基础上再我们手动配置详细的内容
选择每一项,输入Y表示编译进内核,输入M表示编译为模块,以后再动态加载,输入N表示不使用。
4 设置好之后按enter确认保存就配置好了
5 在根目录 make uImage 就可以编译了,自动生成uboot支持的文件 uImage.bin
在make uImage 最开始做的事是通过.config生成下面两个文件:
include/linux/autoconf.h 这个文件把.config里面的Y和M定义项都宏定义为1,用于对应C源码的预处理,源码都include这个文件
include/config/auto.conf 这个文件把.config里面的设置项详细列出是=y还是=m,用于对应子目录下的makefile选择性编译,所有makefile都包含这个文件
下面分析下makefile :
makefile从顶层开始,在根目录下,下面所有makefile都为顶层makefile服务的,查看顶层makefile可看到arch/arm目录下的makefile被包含进来:
include $(srctree)/arch/$(SRCARCH)/Makefile
然后看下之前的auto.conf也被包含进来:
ifeq ($(dot-config),1)
# Read in config
-include include/config/auto.conf
所以顶层makefile包含了他们。
在执行make uImage的时候,执行的arch/arm目录下的makefile,查找uImage:
zImage Image xipImage bootpImage uImage: vmlinux
可知uImage 依赖于 vmlinux,就是说生成uImage之前必须得生成vmlinux;
再查看vmlinux编译入口在顶层makefile里面:
all: vmlinux
然后查看vmlinux的依赖:
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
再列出其他的依赖:
vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds := arch/$(SRCARCH)/kernel/vmlinux.lds
export KBUILD_VMLINUX_OBJS := $(vmlinux-all)
再列出其他的依赖:
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
MMUEXT未定义,所以:
head-y := arch/arm/kernel/head.o arch/arm/kernel/init_task.o
init-y := init/
init-y := $(patsubst %/, %/built-in.o, $(init-y))
相当于: init/built-in.o
drivers-y := drivers/ sound/ firmware/
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
相当于: drivers/built-in.o sound/built-in.o firmware/built-in.o
net-y := net/
net-y := $(patsubst %/, %/built-in.o, $(net-y))
相当于: net/built-in.o
libs-y := lib/
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)
相当于: lib/lib.a lib/built-in.o
core-y := usr/
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
core-y := $(patsubst %/, %/built-in.o, $(core-y))
相当于: usr/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o
上面的.o就是组成内核的原材料,他们的存放规则根据arch/arm/kernel/vmlinux.lds链接文件来指定的,而arch/arm/kernel/vmlinux.lds是由arch/arm/kernel/vmlinux.lds.S生成的,其生成规则在scripts/Makefile.build的第236行开始定义;
看下makefile里面的链接规则:
# Rule to link vmlinux - also used during CONFIG_KALLSYMS
# May be overridden by arch/$(ARCH)/Makefile
quiet_cmd_vmlinux__ ?= LD $@
cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@ \
-T $(vmlinux-lds) $(vmlinux-init) \
--start-group $(vmlinux-main) --end-group \
$(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o FORCE ,$^)
--------- 注释:
cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@ 指定链接工具与参数
-T $(vmlinux-lds) $(vmlinux-init) 指定链接文件 ,开始放第一段 vmlinux-init := $(head-y) $(init-y)
--start-group $(vmlinux-main) --end-group 放主段vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
$(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o FORCE ,$^) 最后链接生成vmlinux.o文件;
这个是链接的.o文件顺序规则,而.o文件内的段链接规则就通过链接文件vmlinux.lds指定:
SECTIONS
{
#ifdef CONFIG_XIP_KERNEL
. = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);
#else
. = PAGE_OFFSET + TEXT_OFFSET;
#endif
.text.head : {
_stext = .;
_sinittext = .;
*(.text.head)
}
从上面可知最开始是存放所有文件的.text.head段,而第一个文件是head.o,所以最开始存放head.o的.text.head段,查看head.s文件:
.section ".text.head", "ax"
ENTRY(stext)
可以知道确实有这个段,那么这个就是内核的入口;
下面看下如何具体生成uImage的:
1 生成的vmlinux.o文件在内核根目录下面,vmlinux属于未压缩,带注释,调试信息、符号表的最初的内核,Image的大小约3.2MB;
2 将上面的vmlinux去除调试信息、注释、符号表等内容,生成arch/arm/boot/Image,这是不带多余信息的linux内核
命令:arm-linux-gnu-objcopy -O binary -R .note -R .comment -S vmlinux arch/arm/boot/Image
3. 将 arch/arm/boot/Image 用gzip -9 压缩生成arch/arm/boot/compressed/piggy.gz大小约1.5MB;
命令:gzip -f -9 < arch/arm/boot/compressed/../Image > arch/arm/boot/compressed/piggy.gz
4. 编译arch/arm/boot/compressed/piggy.S 生成arch/arm/boot/compressed/piggy.o大小约1.5MB,这里实际上是将piggy.gz通过piggy.S编译进piggy.o文件中。而piggy.S文件仅有6行,只是包含了文件piggy.gz;
命令:arm-linux-gnu-gcc -Wp,-MD,arch/arm/boot/compressed/.piggy.o.d -nostdinc -isystem /home/justin/crosstool/gcc-3.4.5-glibc-2.3.6/arm-linux-gnu/lib/gcc/arm-linux-gnu/3.4.5/include -D__KERNEL__ -Iinclude -mlittle-endian -D__ASSEMBLY__ -Wa,-L -gdwarf-2 -mapcs-32 -mno-thumb-interwork -D__LINUX_ARM_ARCH__=4 -march=armv4 -mtune=arm9tdmi -msoft-float -c -o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/piggy.S
5. 依据arch/arm/boot/compressed/vmlinux.lds 将arch/arm/boot/compressed/目录下的文件head.o 、piggy.o 、misc.o链接生成 arch/arm/boot/compressed/vmlinux,这个vmlinux是经过压缩且含有自解压代码的内核,大小约1.5MB;
命令:arm-linux-gnu-ld -EL --defsym zreladdr=0x30008000 --defsym params_phys=0x30000100 -p --no-undefined -X /home/justin/crosstool/gcc-3.4.5-glibc-2.3.6/arm-linux-gnu/lib/gcc/arm-linux-gnu/3.4.5/libgcc.a -T arch/arm/boot/compressed/vmlinux.lds arch/arm/boot/compressed/head.o arch/arm/boot/compressed/piggy.o arch/arm/boot/compressed/misc.o -o arch/arm/boot/compressed/vmlinux
6. 将arch/arm/boot/compressed/vmlinux去除调试信息、注释、符号表等内容,生成arch/arm/boot/zImage大小约1.5MB;这已经是一个可以使用的linux内核映像文件了;
命令:arm-linux-gnu-objcopy -O binary -R .note -R .comment -S arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage
上面六步总结:
最后将arch/arm/boot/zImage添加64Bytes的相关信息打包为arch/arm/boot/uImage大小约1.5MB;
命令:/bin/sh /home/farsight/Resources/kernel/linux-2.6.14/scripts/mkuboot.sh -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -n 'Linux-2.6.14' -d arch/arm/boot/zImage arch/arm/boot/uImage
这个的具体位置在arch/arm/boot/makefile里面:
quiet_cmd_uimage = UIMAGE $@
cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel \
-C none -a $(LOADADDR) -e $(STARTADDR) \
-n 'Linux-$(KERNELRELEASE)' -d $< $@
ifeq ($(CONFIG_ZBOOT_ROM),y)
$(obj)/uImage: LOADADDR=$(CONFIG_ZBOOT_ROM_TEXT)
else
$(obj)/uImage: LOADADDR=$(ZRELADDR)
endif
ifeq ($(CONFIG_THUMB2_KERNEL),y)
# Set bit 0 to 1 so that "mov pc, rx" switches to Thumb-2 mode
$(obj)/uImage: STARTADDR=$(shell echo $(LOADADDR) | sed -e "s/.$$/1/")
else
$(obj)/uImage: STARTADDR=$(LOADADDR)
endif
/home/farsight/Resources/kernel/linux-2.6.14/scripts/mkuboot.sh 表示执行mkimage工具(内部会去工具链或系统里面搜索这个工具),后面一堆都是mkimage参数,这些参数组成一个64Bytes头部和zImage组成uImage;
我们系统是SDRAM里面装载,ARM指令集,所以:
LOADADDR=$(ZRELADDR) STARTADDR=$(LOADADDR)
所以这个装载地址和入口地址,在我们的系统环境下(NANDFLASH启动,ARM指令集),内核默认就是相等的,都为ZRELADDR,而ZRELADDR是什么?看这个makefile的前面:
MKIMAGE := $(srctree)/scripts/mkuboot.sh
ifneq ($(MACHINE),)
include $(srctree)/$(MACHINE)/Makefile.boot
endif
# Note: the following conditions must always be true:
# ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)
# PARAMS_PHYS must be within 4MB of ZRELADDR
# INITRD_PHYS must be in RAM
ZRELADDR := $(zreladdr-y)
PARAMS_PHYS := $(params_phys-y)
INITRD_PHYS := $(initrd_phys-y)
可以看到:
ZRELADDR := $(zreladdr-y)
而包括
zreladdr-y定义在第三行包含的那个文件里面了,2410的路径为arm/arch/mach-s3c2410/Makefile.boot ,里面为:
zreladdr-y := 0x30008000 params_phys-y := 0x30000100
所以启动地址和装载地址都是0x30008000;之前mkImage参数有-C none,标记的是未压缩内核(因为makefile生成zImage是一个已经压缩了的内核),这个未压缩内核的标记只是给uboot看的,这样uboot就不会去解压了,而把解压工作交给内核自解压,看uboot代码判断:
switch (hdr->ih_comp) {
case IH_COMP_NONE: //未压缩内核
if(ntohl(hdr->ih_load) == addr) {
printf (" XIP %s ... ", name);
} else {
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
}
break;
case IH_COMP_GZIP: //zip压缩型内核,内核解压到加载地址
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
(uchar *)data, &len) != 0) {
puts ("GUNZIP ERROR - must RESET board to recover\n");
SHOW_BOOT_PROGRESS (-6);
do_reset (cmdtp, flag, argc, argv);
}
break;
1 如果nand read 下载到sdram的下载地址和bootm地址相同,那么这个地址对应的uImage的头部起始;
这里如果是未压缩内核则判断bootm地址是否和装载地址一致,如果一致则不移动内核数据到装载地址,如果不一致则移动,也就是说 在这个条件下:如果bootm地址 = 装载地址 ,那么 入口地址=装载地址-0x40(64字节头部)
如果bootm地址!=装载地址,那么入口地址=装载地址
而上面分析Makefile可知道入口地址和装载地址是相等的,那么必须满足:
下载地址 = bootm地址 != 装载地址 = 入口地址
2 如果nand read 下载到sdram的地址和bootm地址不同;假设nand read 下载到sdram的地址为0x30007fc0,而bootm地址为0x30008000,那么这个bootm地址刚好对应的是内核起始;此时bootm地址 = 装载地址,那么内核数据不移动;那么必须满足:
下载地址 +0x40 = bootm地址 = 装载地址 = 入口地址