WDS1期第9课 uboot 5 之uboot启动内核 tftp下载内核 写入nand


在这里插入图片描述

bootcmd=nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0

1. nand read.jffs2 0x30007fc0 kernel 读出内核到SDRAM

 从kernel分区读出内核到SDRAM的0x30007fc0地址。
分区,PC上每个硬盘的前面都有分区表,分区一般不变。嵌入式linux没有这样分区,只是在源码里写死,有boot/env/kernel/sysfile,这些分区的地址范围非常重要。在include/configs/100ask24x0.h中,MTD(memory technology device内存技术设备)
bootloader 0~256k (从0开始)
params +128k 环境变量
kernel +2M 内核
root +剩余

#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \
                            "128k(params)," \
                            "2m(kernel)," \
                            "-(root)"

在这里插入图片描述也就是说从nand的0x00060000地址往后读取0x00200000Byte的内核数据 到 SDRAM的0x30007fc0地址。
对于nand命令,在这里common/cmd_nand.c实现,.jffs2表示长度不需要页或块对齐,最终调用nand_read_opts读取nand,

int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
...
    if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 &&
        strncmp(cmd, "dump", 4) != 0 &&
        strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 &&
        strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 &&
        strcmp(cmd, "biterr") != 0 &&
        strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 )
        goto usage;
...
  if (s != NULL &&
      (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) {
      if (read) {
          /* read */
          nand_read_options_t opts;
          memset(&opts, 0, sizeof(opts));
          opts.buffer = (u_char*) addr;
          opts.length = size;
          opts.offset = off;
          opts.quiet      = quiet;
          ret = nand_read_opts(nand, &opts);

2. bootm 0x30007fc0 启动内核

2.1 bootm xxx读出头,根据头是否移动真内核到加载地址ih_load

nand的kernel中存储内核的是uImage(头+真内核),头在bootm的实现里边用到,common/cmd_bootm.c,找到image_header_t这个东西,

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	ulong	iflag;
	ulong	addr;
	ulong	data, len, checksum;
	ulong  *len_ptr;
	uint	unc_len = CFG_BOOTM_LEN;
	int	i, verify;
	char	*name, *s;
	int	(*appl)(int, char *[]);
	image_header_t *hdr = &header;

头(64Byte)的格式为一个结构体,在include/image.h中,关注ih_load和ih_ep,
ih_load表示内核加载的地址,运行内核时放在哪里;ih_ep内核运行入口地址。讲道理内核可以放到SDRAM的任何地方(但要保证不破坏内存顶部数据),

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和ih_load可以保证内核正常启动。但为什么是0x30007FC0?
原因
 从flash读到的是uImage,放到SDRAM中的xxx去,bootm xxx从内存的那个地方启动内核,当读到的时候可以获取到ih_ep和ih_load,若真内核不在ih_load,则将真内核移到ih_load,然后跳到ih_ep执行真内核。
 在common/cmd_bootm.c中,首先读出头部,然后校验,根据头部移动真内核data到中去ih_load,若内核刚好就在ih_load这个地方,可以节省时间。对于2440开发板ih_load为0x30008000(真内核在这里),头部64Byte。从这里0x30007FC0启动uImage。加载64Byte的头,然后刚好到0x30008000运行真内核,省去移动真内核的时间。
0x30007FC0 + 64Byte = 0x30008000

memmove (&header, (char *)addr, sizeof(image_header_t));
...
checksum = ntohl(hdr->ih_hcrc);
...
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);

如果真内核加载地址ih_load和真内核data相等,则不移动,不等则要搬移到ih_load,

switch (hdr->ih_comp) {
case IH_COMP_NONE:
	if(ntohl(hdr->ih_load) == data) {
		printf ("   XIP %s ... ", name);
	} else {
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
		size_t l = len;
		void *to = (void *)ntohl(hdr->ih_load);
		void *from = (void *)data;

		printf ("   Loading %s ... ", name);

		while (l > 0) {
			size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
			WATCHDOG_RESET();
			memmove (to, from, tail);

2.2 从ih_load启动真内核

在common/cmd_bootm.c中,do_bootm_linux函数用于启动真内核,首先由uboot传入参数,然后跳到in_ep启动真内核。

do_bootm_linux  (cmdtp, flag, argc, argv,
   addr, len_ptr, verify);

do_bootm_linux的实现在lib_arm/armlinux.c中,传递参数,启动真内核theKernel(函数指针),

#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
    defined (CONFIG_CMDLINE_TAG) || \
    defined (CONFIG_INITRD_TAG) || \
    defined (CONFIG_SERIAL_TAG) || \
    defined (CONFIG_REVISION_TAG) || \
    defined (CONFIG_LCD) || \
    defined (CONFIG_VFD)
	setup_start_tag (bd);
...
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

分析theKernel,theKernel等于入口地址,然后执行theKernel启动真内核,

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
...
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

2.2.1 传递参数,在某个地址按照某格式写入参数

地址:0x30000100 2440开发板
格式称:tag

设置参数到内存,格式在函数内实现,lib_arm/armlinux.c中,
setup_start_tag (bd);
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline);
etup_end_tag (bd);

如setup_start_tag,

static void setup_start_tag (bd_t *bd)
{
	params = (struct tag *) bd->bi_boot_params;

	params->hdr.tag = ATAG_CORE;
	params->hdr.size = tag_size (tag_core);

	params->u.core.flags = 0;
	params->u.core.pagesize = 0;
	params->u.core.rootdev = 0;

	params = tag_next (params);
}

2.2.2 根据参数启动真内核

内核可能支持很多单板,根据机器id来判断,第二个参数为机器id,第三个参数传入启动参数所在的地址,

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

第二个参数在board/100ask24x0/100ask24x0.c中设置的,

else
{
    /* arch number of SMDK2440-Board */
    gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
}

tftp下载内核 写入nand

tftp 30000000 uImage 	# uImage在tftp的目录下,直接下载就行
nand erase 60000 400000 # offset size
nand write 30000000 60000 400000 # ram offset size

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值