几个地址参数及uboot加载启动内核过程的理解

时间:2021-06-15 16:30:20
关于uBoot和Linux内核中几个地址参数及uboot加载启动内核过程的理解
    uboot 一般使用 mkimage工具先制作 一个启动映象文件来引导识别内核的, uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录参数所指定的信息,这样uboot才能识别这个映象是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么
 vim arch/arm/Makefile
 31 # defines filename extension depending memory management type.
 32 ifeq ($(CONFIG_MMU),)
 33 MMUEXT          := -nommu
 34 endif


121 #Default value
122 head-y          := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task    .o
123 textofs-y       := 0x00008000
124 textofs-$(CONFIG_ARCH_CLPS711X) := 0x00028000
125 # We don't want the htc bootloader to corrupt kernel during resume
126 textofs-$(CONFIG_PM_H1940)      := 0x00108000
127 # SA1111 DMA bug: we don't want the kernel to live in precious DMA-able memo    ry
128 ifeq ($(CONFIG_ARCH_SA1100),y)
129 textofs-$(CONFIG_SA1111) := 0x00208000
130 endif


222 # The byte offset of the kernel image in RAM from the start of RAM.
223 TEXT_OFFSET := $(textofs-y)

241 export  TEXT_OFFSET GZFLAGS MMUEXT


./scripts/mkuboot.sh
#!/bin/bash

#
# Build U-Boot image when `mkimage' tool is available.
#

MKIMAGE=$(type -path "${CROSS_COMPILE}mkimage")

if [ -z "${MKIMAGE}" ]; then
        MKIMAGE=$(type -path mkimage)
        if [ -z "${MKIMAGE}" ]; then
                # Doesn't exist
                echo '"mkimage" command not found - U-Boot images will not be built' >&2
                exit 1;
        fi
fi

# Call "mkimage" to create U-Boot image
${MKIMAGE} "$@"

./arch/arm/mach-s3c2410/Makefile.boot
  1 ifeq ($(CONFIG_PM_H1940),y)
  2         zreladdr-y              := 0x30108000
  3         params_phys-y   := 0x30100100
  4 else
  5         zreladdr-y              := 0x30008000
  6         params_phys-y   := 0x30000100
  7 endif

vim arch/arm/boot/Makefile
 14 MKIMAGE         := $(srctree)/scripts/mkuboot.sh

 16 ifneq ($(MACHINE),)
 17 include $(srctree)/$(MACHINE)/Makefile.boot  //即arch/arm/mach-s3c2410/Makefile.boot
 18 endif

 20 # Note: the following conditions must always be true:
 21 #   ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET)
 22 #   PARAMS_PHYS must be within 4MB of ZRELADDR
 23 #   INITRD_PHYS must be in RAM
 24 ZRELADDR    := $(zreladdr-y)  //内核加载地址ZRELADDR
 25 PARAMS_PHYS := $(params_phys-y)
 26 INITRD_PHYS := $(initrd_phys-y)
 27
 28 export ZRELADDR INITRD_PHYS PARAMS_PHYS
 29
 30 targets := Image zImage xipImage bootpImage uImage



 62 quiet_cmd_uimage = UIMAGE  $@
 63       cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel \
 64                    -C none -a $(LOADADDR) -e $(STARTADDR) \
 65                    -n 'Linux-$(KERNELRELEASE)' -d $< $@
 66
 67 ifeq ($(CONFIG_ZBOOT_ROM),y)
 68 $(obj)/uImage: LOADADDR=$(CONFIG_ZBOOT_ROM_TEXT)
 69 else
 70 $(obj)/uImage: LOADADDR=$(ZRELADDR)
 71 endif
 72
 73 $(obj)/uImage: STARTADDR=$(LOADADDR)


cmd_uimage展开相当于:
mkimage -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -n 'Linux-3.0" -d zImage uImage

-A:CPU类型
-O:操作系统
-T:用于指定image类型,比如Kernel
-C:采用的压缩方式
-a:内核加载地址
-e:内核入口地址
-d 无头信息的image文件名

Boot options  --->
  [ ] Flattened Device Tree support                            
  (0x0) Compressed ROM boot loader base address      
  (0x0) Compressed ROM boot loader BSS address
 
430:CONFIG_ZBOOT_ROM_TEXT=0x0
431:CONFIG_ZBOOT_ROM_BSS=0x0
arch/arm/boot/compressed/Makefile
ifeq ($(CONFIG_ZBOOT_ROM),y)
ZTEXTADDR       := $(CONFIG_ZBOOT_ROM_TEXT)  //自解压程序地址ZTEXTADDR
ZBSSADDR        := $(CONFIG_ZBOOT_ROM_BSS)
else
ZTEXTADDR       := 0
ZBSSADDR        := ALIGN(8)
endif

默认值ZTEXTADDR= 0x00000000表示不使用。(只能通过uBoot的gunzip解压加载)

include/configs/st2410.h -n
127:#define     CFG_LOAD_ADDR           0x33000000      /* default load address*///uImage存放地址

uBoot的do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
函数将检验存放到0x33000000地址处的经过mkimage格式化的uImage数据的头部
typedef struct image_header {
    uint32_t    ih_magic;    /* Image Header Magic Number    */
    uint32_t    ih_hcrc;    /* Image Header CRC Checksum    */
    uint32_t    ih_time;    /* Image Creation Timestamp    */
    uint32_t    ih_size;    /* Image Data Size        */
    uint32_t    ih_load;    /* Data     Load  Address        */
    uint32_t    ih_ep;        /* Entry Point Address        */
    uint32_t    ih_dcrc;    /* Image Data CRC Checksum    */
    uint8_t        ih_os;        /* Operating System        */
    uint8_t        ih_arch;    /* CPU architecture        */
    uint8_t        ih_type;    /* Image Type            */
    uint8_t        ih_comp;    /* Compression Type        */
    uint8_t        ih_name[IH_NMLEN];    /* Image Name        */
} image_header_t;
(ih_ep值为0x30008000,ih_load值为0x30008000)
如果头部各个域值和crc合法,那么do_bootm将调用如下gunzip解压函数对
0x33000000 +sizeof(image_header_t)地址处的压缩内核进行解压:

gunzip((void*)ntohl(hdr->ih_load),unc_len,(uchar *)data,(int*)&len);

1.hdr->ih_load   为输出数据地址0x30008000
2.unc_len        为gunzip解压输出数据上限值-8M,do_bootm函数前有定义#define CFG_BOOTM_LEN    0x800000,uint    unc_len = CFG_BOOTM_LEN;
3.data           为输入数据地址data=0x33000000 +sizeof(image_header_t);
4.Len            为输入数据长度len = ntohl(hdr->ih_size );
 解压完成后将会存储解压后数据的实际大小

压缩的Linux内核文件uImage,经由gunzip解压函数后,通过
do_bootm_linux (cmdtp, flag, argc, argv,addr, len_ptr, verify);
函数向Linux内核传递内核运行所需的3个参数,在bootm执行的流程中,可以看到会调用do_bootm_linux()在执行Linux内核,内核的起始地址如下:
void (*theKernel)(int zero, int arch, uint params);
image_header_t *hdr = &header;

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

header是uImage的头部,通过头部,得到内核映像起始的执行地址hdr->ih_ep为0x30008000,标识为theKernel。从中也可以看到,内核接受三个参数,第一个为0,第二个为系统的ID号,第三个是传入内核的参数。

在do_bootm_linux()的最后,会跳到内核去执行:

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

这样完成了Linux系统启动所需要3个参数的传递,至此uBoot的工作已经结束,Linux将在0x30008000地址处正式运行。