imx6ull:bootz启动linux内核

images全局变量

  • 不管是 bootz 还是 bootm 命令,在启动 Linux 内核的时候都会用到一个重要的全局变量images
  • images 是 bootm_headers_t 类型的全局变量
    /*
     * Legacy and FIT format headers used by do_bootm() and do_bootm_()
     * routines.
     */
    typedef struct bootm_headers {
    	/*
    	 * Legacy os image header, if it is a multi component image
    	 * then boot_get_ramdisk() and get_fdt() will attempt to get
    	 * data from second and third component accordingly.
    	 */
    	image_header_t	*legacy_hdr_os;		/* image header pointer */
    	image_header_t	legacy_hdr_os_copy;	/* header copy */
    	ulong		legacy_hdr_valid;
    
    #if defined(CONFIG_FIT)
    	const char	*fit_uname_cfg;	/* configuration node unit name */
    
    	void		*fit_hdr_os;	/* os FIT image header */
    	const char	*fit_uname_os;	/* os subimage node unit name */
    	int		fit_noffset_os;	/* os subimage node offset */
    
    	void		*fit_hdr_rd;	/* init ramdisk FIT image header */
    	const char	*fit_uname_rd;	/* init ramdisk subimage node unit name */
    	int		fit_noffset_rd;	/* init ramdisk subimage node offset */
    
    	void		*fit_hdr_fdt;	/* FDT blob FIT image header */
    	const char	*fit_uname_fdt;	/* FDT blob subimage node unit name */
    	int		fit_noffset_fdt;/* FDT blob subimage node offset */
    
    	void		*fit_hdr_setup;	/* x86 setup FIT image header */
    	const char	*fit_uname_setup; /* x86 setup subimage node name */
    	int		fit_noffset_setup;/* x86 setup subimage node offset */
    #endif
    
    #ifndef USE_HOSTCC
    	image_info_t	os;		/* os image info */
    	ulong		ep;		/* entry point of OS */
    
    	ulong		rd_start, rd_end;/* ramdisk start/end */
    
    	char		*ft_addr;	/* flat dev tree address */
    	ulong		ft_len;		/* length of flat device tree */
    
    	ulong		initrd_start;
    	ulong		initrd_end;
    	ulong		cmdline_start;
    	ulong		cmdline_end;
    	bd_t		*kbd;
    #endif
    
    	int		verify;		/* getenv("verify")[0] != 'n' */
    
    #define	BOOTM_STATE_START	(0x00000001)
    #define	BOOTM_STATE_FINDOS	(0x00000002)
    #define	BOOTM_STATE_FINDOTHER	(0x00000004)
    #define	BOOTM_STATE_LOADOS	(0x00000008)
    #define	BOOTM_STATE_RAMDISK	(0x00000010)
    #define	BOOTM_STATE_FDT		(0x00000020)
    #define	BOOTM_STATE_OS_CMDLINE	(0x00000040)
    #define	BOOTM_STATE_OS_BD_T	(0x00000080)
    #define	BOOTM_STATE_OS_PREP	(0x00000100)
    #define	BOOTM_STATE_OS_FAKE_GO	(0x00000200)	/* 'Almost' run the OS */
    #define	BOOTM_STATE_OS_GO	(0x00000400)
    	int		state;
    
    #ifdef CONFIG_LMB
    	struct lmb	lmb;		/* for memory mgmt */
    #endif
    } bootm_headers_t;

     

  •  os 成员变量是 image_info_t 类型的,为系统镜像信息

    typedef struct image_info {
    	ulong		start, end;		/* start/end of blob */
    	ulong		image_start, image_len; /* start of image within blob, len of image */
    	ulong		load;			/* load addr for the image */
    	uint8_t		comp, type, os;		/* compression, type of image, os type */
    	uint8_t		arch;			/* CPU architecture */
    } image_info_t;

     

  • BOOTM_STATE_开头的宏定义表示 BOOT 的不同阶段

do_bootz函数

  • 对应于bootz命令,用于启动zImage镜像文件 
  • 调用bootz_start函数
  • 关闭中断
  • 设置 images.os.os 为 IH_OS_LINUX,也就是设置系统镜像为 Linux,表示我们要启动的是 Linux 系统!后面会用到 images.os.os 来挑选具体的启动函数。
  • 调用函数 do_bootm_states 来执行不同的 BOOT 阶段,这里要执行的 BOOT 阶段有:BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO和BOOTM_STATE_OS_GO。

 bootz_start函数

  • 调用函数 do_bootm_states 来执行 BOOTM_STATE_START 阶段
  • 设置 images 的 ep 成员变量,也就是系统镜像的入口点,使用 bootz 命令启动系统的时候就会设置系统在DRAM 中的存储位置,这个存储位置就是系统镜像的入口点
  • 调用 bootz_setup 函数,此函数会判断当前的系统镜像文件是否为 Linux 的镜像文件,并且会打印出镜像相关信息
  • 调用函数 bootm_find_images 查找 ramdisk 和设备树(dtb)文件,但是我们没有用到 ramdisk,因此此函数在这里仅仅用于查找设备树(dtb)文件

 bootz_setup函数 

  • zimage_header结构体描述了zImage头信息
struct zimage_header {
	uint32_t	code[9];
	uint32_t	zi_magic;        //Linux魔术
	uint32_t	zi_start;        //zImage起始地址
	uint32_t	zi_end;        //zImage结束地址
};
  • 宏 LINUX_ARM_ZIMAGE_MAGIC 就是 ARM Linux系统魔数。这个数等于0x016f2818,表示这个镜像是一个zImage。也就是说zImage格式的镜像中在头部的一个固定位置存放了这个数作为格式标记。如果我们拿到了一个image,去他的那个位置去取4字节判断它是否等于LINUX_ZIMAGE_MAGIC,则可以知道这个镜像是不是一个zImage。
  • 判断镜像文件中的魔数是否对应于 ARM 的 Linux 系统镜像,如果不是的话就直接返回

bootm_find_images函数

  • 查找ramdisk,没有用到不用管
  • 查找设备数dtb文件,找到以后就将设备树的起始地址和长度分别写到images 的 ft_addr 和 ft_len 成员变量中。我们使用 bootz 启动 Linux 的时候已经指明了设备树在DRAM 中的存储地址,因此 images.ft_addr就是调用bootz命令时指定的地址,长度根据具体的设备树文件而定。

 do_bootm_states函数

  • 函数 do_bootm_states 根据不同的 BOOT 状态执行不同的代码段,程序中通过位与的方式进行状态判断并执行相应的操作
  • 在 do_bootz 函数中会用到 BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 和
    BOOTM_STATE_OS_GO 这三个BOOT状态
  • bootz_start函数中会用到BOOTM_STATE_START这个 BOOT 状态

BOOTM_STATE_START

  • BOOTM_STATE_START 阶段调用函数 bootm_start。主要包括清空images、初始化images的verify成员以及设置状态为BOOTM_STATE_START
  •  bootm_os_get_boot_func 由参数 images->os.os 就是系统类型来查找系统启动函数,在 do_bootz 中设置 images.os.os= IH_OS_LINUX。函数返回值boot_fn就是找到的系统启动函数,这里找到的 Linux 系统启动函数为 do_bootm_linux

BOOTM_STATE_OS_PREP

  • BOOTM_STATE_OS_PREP 状态调用boot_fn即do_bootm_linux函数。

BOOTM_STATE_OS_FAKE_GO

  • 由于我们没用使能TRACE功能,因此宏 CONFIG_TRACE 也就没有定义,所以没有执行什么有用的操作

BOOTM_STATE_OS_GO

  • 调用函数 boot_selected_os 启动 Linux 内核,此函数第 4 个参数为 Linux 系统镜像头即images全局变量的地址,第 5 个参数就是 Linux 系统启动函数这里是do_bootm_linux
  • boot_selected_os 内部调用boot_fn即do_bootm_linux函数

bootm_os_get_boot_func函数

  •  该函数由系统类型来查找系统启动函数
  • 我们没有定义CONFIG_NEEDS_MANUAL_RELOC宏,忽略掉相关的代码
  •  boot_os 是个数组,这个数组里面存放着不同的系统对应的启动函数
    static boot_os_fn *boot_os[] = {
    	[IH_OS_U_BOOT] = do_bootm_standalone,
    #ifdef CONFIG_BOOTM_LINUX
    	[IH_OS_LINUX] = do_bootm_linux,
    #endif
        ...
    }

     

do_bootm_linux函数

  • 当参数flag等于BOOTM_STATE_OS_PREP时调用函数boot_prep_linux。主要用于处理环境变量bootargs,bootargs 保存着传递给 Linux kernel 的参数
  • 当参数flag等于BOOTM_STATE_OS_GO或者BOOTM_STATE_OS_FAKE_GO的话就执行 boot_jump_linux 函数

boot_jump_linux函数

(1)机器码machid

  • 变量machid保存机器码,当环境变量中有machid时就使用环境变量中machid对应的值,否则使用gd->bd->bi_arch_number中的值。
  • 如果不使用设备树的话这个机器 ID 会被传递给 Linux内核,Linux 内核会在自己的机器 ID 列表里面查找是否存在与 uboot 传递进来的 machid 匹配的项目,如果存在就说 Linux 内核支持这个机器,那么 Linux 就会启动
  • gd中的值是在启动阶段board_init_f函数中调用init_sequence_f中的setup_machine函数中被赋值的。但是由于我们使用fdt的方式,不用此方式所以没有定义CONFIG_MACH_TYPE宏,也就运行不到这里
  • 使用设备树的话这个 machid 就无效了,设备树存有一个“兼容性”这个属性,Linux 内核会比较“兼容性”属性
    的值(字符串)来查看是否支持这个机器

(2)内核镜像文件的第一行代码kernel_entry

  • 定义一个函数指针kernel_entry。函数有三个参数:zero,arch,params,第一个参数 zero 同样为 0;第二个参数为机器 ID;第三个参数 ATAGS(即传统的tag传参方式,可参考x210:uboot和系统移植扩展--tag方式传参) 或者设备树(DTB)首地址,ATAGS 是传统的方法,用于传递一些命令行信息啥的,如果使用设备树的话就要传递设备树(DTB)
  •  images->ep 保存着 Linux内核镜像的起始地址,而起始地址保存的是 Linux 内核第一行代码。所以Linux 内核镜像文件的第一行代码就是函数指针 kernel_entry指向的地方

(3)调用函数 announce_and_cleanup 来打印Starting kernel ...等一些信息并做一些清理工作

(4)调用 kernel_entry并传递参数

  • Linux 内核一开始是汇编代码,因此函数 kernel_entry 就是个汇编函数。向汇编函数传递参数要使用 r0、r1 和 r2(参数数量不超过 3 个的时候)
  • 如果使用设备树的话,r2 应该是设备树的起始地址,而设备树地址保存在 images
    的 ftd_addr 成员变量中
  • 如果不使用设备树的话,r2 应该是 uboot 传递给 Linux 的参数起始地址,也就是内存中那一大片tag的首地址(本人当时参考的整点原子的教程中写的是环境变量 bootargs 的值,应该是不正确的,bootargs 仅仅只是是众多传参tag中的一个)
  • 调用 kernel_entry 函数进入 Linux 内核,此行将一去不复返,uboot 的使命也就完成了

 bootz 命令层次调用逻辑关系

 

你可能感兴趣的:(嵌入式linux,uboot)