当前位置:   article > 正文

u-boot-2012.04.01 移植笔记_readinitb": unknown word.

readinitb": unknown word.

目标:把 u-boot-2012.04.01 移植到Jz2440开发板,并启动Linux内核

一、下载、建立source insight工程、编译、烧写、如果无运行分析原因
  1. 从官方网下载 u-boot-2012.04.01,并解压
    tar xjf u-boot-2012.04.01.tar.bz2
    
    • 1
  2. 进入u-boot-2012.04.01目录,并搜索查看该uboot是否支持s3c2440这款SOC (Jz2440开发板使用的SOC是s3c2440)
    cd u-boot-2012.04.01
    find -name "*2440*"
    
    • 1
    • 2
    搜索的结果如下:
    在这里插入图片描述
    由搜索的结果可知,uboot里只有s3c2440.h这个头文件,并没有支持s3c2440这款SOC;那么我们搜索一下与s3c2440相近的s3c2410, find -name "*2410*" ,搜索的结果如下:
    在这里插入图片描述
    由所有的结果可知,board/samsung/目录下有支持smdk2410的单板目录;
  3. 因此我们可知通过配置编译smdk2410,把编译出来了的u-boot下载到Jz2440开发板,看看是否可以正常运行
    (1)配置smdk2410 :make smdk2410_config
    (2)编译:make
  4. 把编译生成的u-boot.bin 下载到开发板,观察是否能够正常启动;在这里插入图片描述
    从上图可知,把u-boot.bin 下载到Nor Flash 后重启开发板,串口没有输出,uboot 并不能正常启动。所以这个uboot不支持我们的jz2440开发板,那么接下通过修改uboot的源码使它支持jz2440开发板。
二、分析uboot启动过程:
  1. 修改代码之前,需要阅读代码分析u-boot的启动过程,这里做一个取巧的办法:
    编译uboot,关心最后一条链接命令,从连接命令中得知u-boot的组成。
    在这里插入图片描述
        从上图可知:arm-linux-ld是链接命令,-T u-boot.lds是指定链接脚本是u-boot.lds,-Ttext 0x0是指定代码段运行地址是0,放在程序最前面的是arch/arm/cpu/arm920t/start.o。
  2. 查看链接脚本u-boot.lds,如下图所示: 链接地址是0,只能在nor flash上运行。
    在这里插入图片描述
    那么从哪里开始运行呢?从arch/arm/cpu/arm920t/start.o 开始运行,所以程序的入口应该在arch/arm/cpu/arm920t/start.S 中,接下来分析这个 start.S,了解u-boot 的启动过程。
  3. 分析 start.S
    (1)回顾以前u-boot的启动过程:
        a.初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH;
        b.如果bootloader比较大,要把它重定位到SDRAM;
        c.把内核从NAND FLASH读到SDRAM;
        d.设置"要传给内核的参数";
        e.跳转执行内核

    (2)start.S 的运行过程:
        (2.1) set the cpu to SVC32 mode (设置CPU为SVC32 模式)
        (2.2) turn off the watchdog(关看门狗
        (2.3) mask all IRQs by setting all bits in the INTMR (屏蔽所有中断
        (2.4) 设置时钟比例 (设置FCLK、HCLK和PCLK时钟的比例)
        (2.5) 设置内存控制器:
            ① 跳转到cpu_init_crit函数进行CPU初始化(刷新指令cache 和 数据cache、关闭MMU 和caches)
            ② 再跳转到底层初始化函数lowlevel_init(初始化内存,也就是设置内存控制器)

        (2.6) 设置栈,调用C函数board_init_f (用C函数前必须先设置好栈,栈地址是往地址低处增长的
        (2.7) 调用函数数组init_sequence里的各个函数
          (2.7.1) board_early_init_f : 设置系统时钟、设置GPIO
          …
        (2.8) 重定位代码:
          (2.8.1) 从NOR FLASH把代码复制到SDRAM
          (2.8.2) 程序的链接地址是0,访问全局变量、静态变量、调用函数时是使"基于0地址编译得到的地址"
                  现在把程序复制到了SDRAM
                  需要修改代码,把"基于0地址编译得到的地址"改为新地址
          (2.8.3) 程序里有些地址在链接时不能确定,要到运行前才能确定:fixabs
        (2.9) clear_bss
        (2.10) 调用C函数board_init_r:第2阶段的代码
         …
三、分析uboot启动过程之内存分布
  1. 在uboot启动过程分析中的可知uboot 运行的第一个C函数是 board_init_f,单片机/SOC在执行C 函数之前,必须设置栈。

    /* Set stackpointer in internal RAM to call board_init_f */
    call_board_init_f:
    	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR) // sp = 0x30000000 + 0x1000 - 128 = 0x3000 0F80‬
    	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */
    	ldr	r0,=0x00000000
    	bl	board_init_f
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    从以上代码可知,在执行C函数 board_init_f 之前设置 sp =CONFIG_SYS_INIT_SP_ADDR,那么CONFIG_SYS_INIT_SP_ADDR等于多少呢?在include/configs/smdk2410.h中 由以下代码:

    /*-----------------------------------------------------------------------
     * Physical Memory Map
     */
    #define CONFIG_NR_DRAM_BANKS	1          /* we have 1 bank of DRAM */
    #define PHYS_SDRAM_1		0x30000000 /* SDRAM Bank #1 */
    #define PHYS_SDRAM_1_SIZE	0x04000000 /* 64 MB */
    
    /* additions for new relocation code, must be added to all boards */
    #define CONFIG_SYS_SDRAM_BASE	PHYS_SDRAM_1
    #define CONFIG_SYS_INIT_SP_ADDR	(CONFIG_SYS_SDRAM_BASE + 0x1000 - \
    				GENERATED_GBL_DATA_SIZE)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可知:CONFIG_SYS_INIT_SP_ADDR = CONFIG_SYS_SDRAM_BASE + 0x1000 - GENERATED_GBL_DATA_SIZE
                                       = 0x30000000 + 0x1000 - GENERATED_GBL_DATA_SIZE

    此时,GENERATED_GBL_DATA_SIZE 就是 gd_t 结构体占的内存大小。这里可通过u-boot 的反 汇编可直接知道 CONFIG_SYS_INIT_SP_ADDR 的值。
    (在uboot顶层目录下输入命令:arm-linux-objdump -D u-boot > u-boot.dis )
    在这里插入图片描述
    由上图可知,sp的值存储在0x498 这个地址里, 由下图可知,这个地址存储的值是0x30000f80,
    在这里插入图片描述
    总结:sp = 0x30000f80;GENERATED_GBL_DATA_SIZE = 128,即 gd_t 结构体的大小是128B。

  2. 设置好sp后,对sp进行8字节对齐(sp对齐后仍然是0x30000f80),然后把r0 = 0作为参数传入函数board_init_f,下面分析board_init_f函数。

    void board_init_f(ulong bootflag)
    {
    	bd_t *bd;
    	init_fnc_t **init_fnc_ptr;
    	gd_t *id;
    	ulong addr, addr_sp;
    #ifdef CONFIG_PRAM
    	ulong reg;
    #endif
    
    	bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");
    
    	/* Pointer is writable since we allocated a register for it */
    	gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);  // gd = (gd_t *)0x30000f80
    	/* compiler optimization barrier needed for GCC >= 3.4 */
    	__asm__ __volatile__("": : :"memory");
    
    	memset((void *)gd, 0, sizeof(gd_t));
    
    	gd->mon_len = _bss_end_ofs;  // _bss_end_ofs = __bss_end__ - _start,从反汇编里可知:_bss_end_ofs = 0x000ae4e0 = 697KB
    #ifdef CONFIG_OF_EMBED
    	/* Get a pointer to the FDT */
    	gd->fdt_blob = _binary_dt_dtb_start;
    #elif defined CONFIG_OF_SEPARATE
    	/* FDT is at end of image */
    	gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE);
    #endif
    	/* Allow the early environment to override the fdt address */
    	gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
    						(uintptr_t)gd->fdt_blob);
    
    	for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
    		if ((*init_fnc_ptr)() != 0) {
    			hang ();
    		}
    	}
    
    #ifdef CONFIG_OF_CONTROL
    	/* For now, put this check after the console is ready */
    	if (fdtdec_prepare_fdt()) {
    		panic("** CONFIG_OF_CONTROL defined but no FDT - please see "
    			"doc/README.fdt-control");
    	}
    #endif
    
    	debug("monitor len: %08lX\n", gd->mon_len);
    	/*
    	 * Ram is setup, size stored in gd !!
    	 */
    	debug("ramsize: %08lX\n", gd->ram_size);
    #if defined(CONFIG_SYS_MEM_TOP_HIDE)
    	/*
    	 * Subtract specified amount of memory to hide so that it won't
    	 * get "touched" at all by U-Boot. By fixing up gd->ram_size
    	 * the Linux kernel should now get passed the now "corrected"
    	 * memory size and won't touch it either. This should work
    	 * for arch/ppc and arch/powerpc. Only Linux board ports in
    	 * arch/powerpc with bootwrapper support, that recalculate the
    	 * memory size from the SDRAM controller setup will have to
    	 * get fixed.
    	 */
    	gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
    #endif
    
    	addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; // gd->ram_size = 64MB,所以此时的 addr = 0x30000000 + 0x04000000 = 0x34000000  
    
    #ifdef CONFIG_LOGBUFFER
    #ifndef CONFIG_ALT_LB_ADDR
    	/* reserve kernel log buffer */
    	addr -= (LOGBUFF_RESERVE);
    	debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN,
    		addr);
    #endif
    #endif
    
    #ifdef CONFIG_PRAM
    	/*
    	 * reserve protected RAM
    	 */
    	reg = getenv_ulong("pram", 10, CONFIG_PRAM);
    	addr -= (reg << 10);		/* size is in kB */
    	debug("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
    #endif /* CONFIG_PRAM */
    
    #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
    	/* reserve TLB table */
    	addr -= (4096 * 4);  // 保留16kB 给 TLB table,所以 addr = 0x34000000 -16k = 0x33ffc000
    
    	/* round down to next 64 kB limit */
    	addr &= ~(0x10000 - 1); // 64kB对齐 addr = 0x33ffc000 & ~0xFFFF = 0x33ff0000
    
    	gd->tlb_addr = addr; // gd->tlb_addr = 0x33ff0000
    	debug("TLB table at: %08lx\n", addr);
    #endif
    
    	/* round down to next 4 kB limit */
    	addr &= ~(4096 - 1); // 4kq对齐: addr仍然等于  0x33ff0000
    	debug("Top of RAM usable for U-Boot at: %08lx\n", addr);
    
    #ifdef CONFIG_LCD  //没有配置LCD
    #ifdef CONFIG_FB_ADDR
    	gd->fb_base = CONFIG_FB_ADDR;
    #else
    	/* reserve memory for LCD display (always full pages) */
    	addr = lcd_setmem(addr);
    	gd->fb_base = addr;
    #endif /* CONFIG_FB_ADDR */
    #endif /* CONFIG_LCD */
    
    	/*
    	 * reserve memory for U-Boot code, data & bss
    	 * round down to next 4 kB limit
    	 */
    	addr -= gd->mon_len;  // addr = 0x33ff0000 - 0x000ae4e0 = 0x33F41B20
    	addr &= ~(4096 - 1);  // 4k对齐 addr = 0x33F41B20 & ~0xfff = 0x33F41000
        
    	debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
    
    #ifndef CONFIG_SPL_BUILD
    	/*
    	 * reserve memory for malloc() arena
    	 */
    	addr_sp = addr - TOTAL_MALLOC_LEN; // addr_sp = 0x33F41000 - 4MB = 0x33B41000‬
    	debug("Reserving %dk for malloc() at: %08lx\n",
    			TOTAL_MALLOC_LEN >> 10, addr_sp);
    	/*
    	 * (permanently) allocate a Board Info struct
    	 * and a permanent copy of the "global" data
    	 */
    	addr_sp -= sizeof (bd_t);//存放bd_t(bd_info) 结构体
    	bd = (bd_t *) addr_sp;  
    	gd->bd = bd;  //
    	debug("Reserving %zu Bytes for Board Info at: %08lx\n",
    			sizeof (bd_t), addr_sp);
    
    #ifdef CONFIG_MACH_TYPE
    	gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
    #endif
    
    	addr_sp -= sizeof (gd_t);//存放gd_t(global_data)结构体
    	id = (gd_t *) addr_sp;
    	debug("Reserving %zu Bytes for Global Data at: %08lx\n",
    			sizeof (gd_t), addr_sp);
    
    	/* setup stackpointer for exeptions */
    	gd->irq_sp = addr_sp;
    #ifdef CONFIG_USE_IRQ
    	addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
    	debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
    		CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
    #endif
    	/* leave 3 words for abort-stack    */
    	addr_sp -= 12; //addr_sp = addr_sp -12
    
    	/* 8-byte alignment for ABI compliance */
    	addr_sp &= ~0x07; // 8字节对齐
    #else
    	addr_sp += 128;	/* leave 32 words for abort-stack   */
    	gd->irq_sp = addr_sp;
    #endif
    
    	debug("New Stack Pointer is: %08lx\n", addr_sp);
    
    #ifdef CONFIG_POST
    	post_bootmode_init();
    	post_run(NULL, POST_ROM | post_bootmode_get(0));
    #endif
    
    	gd->bd->bi_baudrate = gd->baudrate;
    	/* Ram ist board specific, so move it to board code ... */
    	dram_init_banksize();
    	display_dram_config();	/* and display it */
    
    	gd->relocaddr = addr;  // addr = 0x33F41000
    	gd->start_addr_sp = addr_sp; // addr_sp = 预留12字节内存后的地址
    	gd->reloc_off = addr - _TEXT_BASE;
    	debug("relocation Offset is: %08lx\n", gd->reloc_off);
    	memcpy(id, (void *)gd, sizeof(gd_t));// 把全局变量gd 复制到Id这个地址里
    
    	relocate_code(addr_sp, id, addr);
        
    	/* NOTREACHED - relocate_code() does not return */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183

    (1) 从上面代码的14行gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07) 可知,gt结构体存放在0x30000f80这个地址里,大小128B;board_init_f函数对这块内存的操作:先清0,然后设置gd 结构体的各个成员;

    (2)上面的32~36行代码的作用是:各种硬件的初始化;硬件的初始化函数保存在init_sequence这个指针数组里,假如初始化失败,则进入一个死循环函数hang
    在这里插入图片描述
    以下是init_sequence里存放的各个硬件初始化函数:

    init_fnc_t *init_sequence[] = {
    #if defined(CONFIG_ARCH_CPU_INIT)
    	arch_cpu_init,		/* basic arch cpu dependent setup */
    #endif
    #if defined(CONFIG_BOARD_EARLY_INIT_F)
    	board_early_init_f, //单板早期初始化
    #endif
    #ifdef CONFIG_OF_CONTROL
    	fdtdec_check_fdt,
    #endif
    	timer_init,		/* initialize timer */
    #ifdef CONFIG_FSL_ESDHC
    	get_clocks,
    #endif
    	env_init,		/* initialize environment */
    	init_baudrate,		/* initialze baudrate settings */
    	serial_init,		/* serial communications setup */
    	console_init_f,		/* stage 1 init of console */
    	display_banner,		/* say that we are here */
    #if defined(CONFIG_DISPLAY_CPUINFO)
    	print_cpuinfo,		/* display cpu info (and speed) */
    #endif
    #if defined(CONFIG_DISPLAY_BOARDINFO)
    	checkboard,		/* display board info */
    #endif
    #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
    	init_func_i2c,
    #endif
    	dram_init,		/* configure available RAM banks */
    	NULL,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    (3) 上面的65行:addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size,把 addr设置为SDRAM(64MB)的最高地址0x34000000;

    (4) 86~92行:然后给TLB table 分配16kB的内存空间(64kB对齐后,实际给TLB table分配了64kB空间)此时的 addr = 0x33ff0000;
    在这里插入图片描述
    (5) 114~115行:从20行可知gd->mon_len = _bss_end_ofs,_bss_end_ofs = __bss_end__ - _start( 即uboot代码的大小 ) 从反汇编可知:_bss_end_ofs = 0x000ae4e0 = 697KB。所以,144~115行代码的作用是为之后的uboot重定位分配内存空间,此时的addr = 0x33f41000
    在这里插入图片描述
    (6) 123行:给 malloc() 函数分配4MB的堆空间,此时 addr_sp = 0x33B41000; 在这里插入图片描述
    (7) 130~132行:为 bd_t结构体分配内存空间,并且把该结构体的地址保存在 gd->bd 中;
    在这里插入图片描述
    (8) 140~146行:为 gd_t结构体分配内存空间,并且把用id记录该结构体的地址,同时也把结构的地址保存在gd->irq_sp 中;
    在这里插入图片描述
    (9) 153~156行:为abort(数据访问终止模式)分配 12 字节的内存空间,并且进行8字节对齐;
    在这里插入图片描述
    (10) 174~180行:把addr、addr_sp等板寸到gd结构体,同时使用memcpy函数,把gd结构体复制到id这个地址里面;接着把 addr_sp、id、addr作为参数传入代码重定位函数relocate_code中;
    在这里插入图片描述
    总结:① 经过上面的一步步分析,可得出下图的uboot内存分布图,其中下图红色字体addr、id、addr_sp所指的内存的地址值就是传入
            elocate_code函数的值,用于代码的重定位。
          ② 对于relocate_code函数在汇编代码中:addr_sp 保存在 r0,id 保存在 r1,addr 保存在 r2(r2是目标地址)。

四、分析uboot启动过程之代码重定位

1. 代码重定位原理:
     由前面的分析可知,uboot的连接地址为0,所以对于Jz2440开发板而言,uboot只能从NOR Flash运行。由NOR Flash可以像内存一样读,但不能像内存一样写,如果有某个全局变量存储在NOR Flash中,执行程序时只能对该变量进行读操作,不能进行写操作,此时程序就好出现问题,因此需代码重定位(即把NOR Flash的代码复制到SDRAM)。uboot程序的连接地址为0,复制到SDRAM中为何能执行?因为把程序复制到SDRAM后还需要修改代码,改变里面变量、函数的地址。例如,访问nor flash上某个变量的地址是0x100,把涉及的程序复制到0x32000000里面去,想访问同一个变量,使用0x32000100来访问。因此,如果某段程序涉及多个变量,就需要在把这段程序从nor flash复制到SDRAM后,修改程序里变量、函数的地址。

3. 重定位的旧地址在哪里?
    在链接时加上pie选项(arm-linux-ld -pie)地址信息就会生成,然后存在段里面。在链接脚本里面存储地址信息的段是.rel.dyn和.dynsym,如下图所示:
在这里插入图片描述
    uboot在代码重定位时会根据存储在.rel.dyn和.dynsym段里的旧地址信息修改代码,把旧地址改为新地址,程序就可以复制到任何地址位置去运行了。

3. 重定位代码分析:
    从前面对 board_init_f 函数的分析可知,代码重定位的函数是 relocate_code,传入该函数的参数是addr_sp、id、addr,在u-boot-2012.04.01内存分布图可知他们指向内存的位置,当调用汇编函数时这三个参数分别对应r0、r1、r2,代码如下:

	.globl	relocate_code
relocate_code:
	mov	r4, r0	/* save addr_sp */
	mov	r5, r1	/* save addr of gd */
	mov	r6, r2	/* save addr of destination */
  • 1
  • 2
  • 3
  • 4
  • 5
  • addr : uboot的重定位地址,即把uboot代码复制到SDRAM的地址;
  • id:SDRAM中存放gd结构体的地址;
  • addr_sp:栈的地址。

4. 代码重定位步骤:
(1)从NOR FLASH把代码复制到SDRAM;
(2)程序的链接地址是0,访问全局变量、静态变量、调用函数时是使"基于0地址编译得到的地址"
     现在把程序复制到SDRAM,需要把"基于0地址编译得到的地址"修改为新地址
(3)程序里有些地址在链接时不能确定,要到运行前才能确定:fixabs

5.重定位的实现:
(1)保存传入relocate_code函数的参数,把栈地址r0保存到r4,gd结构体的地址r1保存到r5,uboot重定位的地址r2保存到r6;

/*
 * void relocate_code (addr_sp, gd, addr_moni)
 *
 * This "function" does not return, instead it continues in RAM
 * after relocating the monitor code.
 *
 */
	.globl	relocate_code
relocate_code:
	mov	r4, r0	/* save addr_sp */
	mov	r5, r1	/* save addr of gd */
	mov	r6, r2	/* save addr of destination */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

(2)重新设置栈,把r4的值赋给sp (在执行board_init函数之前设置的栈为0x30000f80,现重定位重新设置sp);adr r0,_start里面_start是第
     一条语句的当前地址,当前地址和r6目的地址比较,如果相等,就不需要拷贝代码(重定位),直接跳过清除bss段;如果不相等,就把代
     码从flash拷贝到内存里,从r0这个地地址拷贝到r6这个地址;add r2,r0,r3的意思是r2=r0+r3,r2是源代码的结束地址,而r0是源代码的
     开始地址,而r3是代码段的长度 (_bss_start_ofs = __bss_start - _start,在二进制文件里面不包含bss段)

	/* Set up the stack						    */
stack_setup:
	mov	sp, r4
   /* adr 与 ldr伪指令的区别:
    * adr r0, _start   得到的是 _start 的当前执行位置,由 pc+offset 决定
    * ldr r0, =_start  得到的是绝对的地址,链接时决定
	*/
	adr	r0, _start 
	cmp	r0, r6
	beq	clear_bss		/* skip relocation */
	mov	r1, r6			/* r1 <- scratch for copy_loop */
	ldr	r3, _bss_start_ofs
	add	r2, r0, r3		/* r2 <- source end address	    */ 
	
copy_loop:
	ldmia	r0!, {r9-r10}		/* copy from source address [r0]    */ //将r0处的两个32位数据拷到r9-r10中,然后r0+=8
	stmia	r1!, {r9-r10}		/* copy to   target address [r1]    */  //将拷出来的两个数据放入r1(重定位地址)处,然后r1+=8
	cmp	r0, r2			/* until source end address [r2]    */ // uboot 重定位到0x33F41000这个地址
	blo	copy_loop
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

(3)拷贝代码到重定位地址后修改代码 (把基于0地址编译得到的地址修改为新地址)

#ifndef CONFIG_SPL_BUILD
	/*
	 * fix .rel.dyn relocations
	 */
	ldr	r0, _TEXT_BASE		/* r0 <- Text base */ //r0=text(代码)段基地址=0
	sub	r9, r6, r0		/* r9 <- relocation offset */ //r9= 重定位后的偏移值=0x33F41000
	ldr	r10, _dynsym_start_ofs	/* r10 <- sym table ofs */ //_dynsym_start_ofs =__dynsym_start - _start=0x73608                                         
	add	r10, r10, r0		/* r10 <- sym table in FLASH */ //所以r10=动态符号表的起始偏移值=0x73608
	ldr	r2, _rel_dyn_start_ofs	/* r2 <- rel dyn start ofs */ //r2=__rel_dyn_start - _start=0x6b568
	add	r2, r2, r0		/* r2 <- rel dyn start in FLASH */   //所以r2=相对动态信息的起始偏移值=0x6b568
	ldr	r3, _rel_dyn_end_ofs	/* r3 <- rel dyn end ofs */ // _rel_dyn_end_ofs=__rel_dyn_end - _start=0x73608
	add	r3, r3, r0		/* r3 <- rel dyn end in FLASH */    //所以r3=相对动态信息的结束偏移值=0x00073608
fixloop:
	ldr	r0, [r2]		/* r0 <- location to fix up, IN FLASH! */ //从反汇编可知,r2=0x6b568地址处的内容= 0x20,所以r0 = 0x20
	add	r0, r0, r9		/* r0 <- location to fix up in RAM */  //r0=0x33F41000+0x20=0x33F41020
	ldr	r1, [r2, #4]    // r2 + 4 =0x6b56C ,地址0x6b56C的内容是0x17,所以r1 = 0x17
	and	r7, r1, #0xff   // r7 = 0x17& 0xff = 0x17
	cmp	r7, #23			/* relative fixup? */  // 0x 17的十进制就是 23 ,因此r7与23 相等,下一条指令将跳转到fixabs
	beq	fixrel
	cmp	r7, #2			/* absolute fixup? */
	beq	fixabs
	/* ignore unknown type of fixup */
	b	fixnext
fixabs:
	/* absolute fix: set location to (offset) symbol value */
	mov	r1, r1, LSR #4		/* r1 <- symbol index in .dynsym */
	add	r1, r10, r1		/* r1 <- address of symbol in table */
	ldr	r1, [r1, #4]		/* r1 <- symbol value */
	add	r1, r1, r9		/* r1 <- relocated sym addr */
	b	fixnext
fixrel:
	/* relative fix: increase location by offset */
	ldr	r1, [r0]     // r0 = 0x33F41020, 可知知道这个地址的内容即是反汇编0x20地址的内容0x000001e0
	add	r1, r1, r9   // r1 = r1 + r9 = 0x33F41000 + 0x000001e0 = 0x33F411e0 
fixnext:
	str	r1, [r0]    //改变链接地址里的内容, [0x33F41020]=0x33F411e0  (之前为0x1e0)
	add	r2, r2, #8		/* each rel.dyn entry is 8 bytes */  //r2等于下一个相对动态信息(0x24)的地址
	cmp	r2, r3     //若没到尾部__rel_dyn_end,便继续执行: fixloop
	blo	fixloop
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

总结:(图解代码重定位过程
    在nor flash上存储的程序有如图右所示的rel_dyn_start这一段,里面的6b568地址存储的值是0x20,在nor flash上的0x20地址里存放的是某个地址值。rel_dyn_start这一段表明你想让程序能够运行,需要修改像0x20地址里面的地址值。nor flash上0地址的代码要复制到SDRAM的33f41000地址来,SDRAM地址33f41000偏移值为0x20的地方存储的值跟nor flash上0x20地址的值完全一样,我们把代码复制到SDRAM后,要去修改SDRAM上的代码,把里面用到的地址全部改为新地址,rel_dyn_start这一段表明用到哪些地址,表明要修改地址0x20这个地址里的值( 地址值),设这个值在nor flash的0x20地址上是V,在SDRAM 的33f4,1000地址的偏移值0x20的地址值改为v+0x33f41000,下面的0x00000017是标志位,表示是直接把0x20里面的值取出来,加上一个偏移,得到一个新的值后,写到SDRAM的33f41000地址的偏移值0x20的地址里面去。

这样修改的作用:
    假设程序从0x04这个地址开始运行,即执行ldr pc, _undefined_instruction这条指令,它先去_undefined_instruction这个地址里取一个值undefined_instruction,然后赋值给PC跳转到undefined_instruction这个地址里执行程序。

.globl _start
_start:	b	start_code
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq

_undefined_instruction:	.word undefined_instruction
_software_interrupt:	.word software_interrupt
_prefetch_abort:	.word prefetch_abort
_data_abort:		.word data_abort
_not_used:		.word not_used
_irq:			.word irq
_fiq:			.word fiq

	.balignl 16,0xdeadbeef
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

    在反汇编可知:ldr pc, _undefined_instruction对应的指令是ldr pc, [pc, #20],0x20 这个地址里存储的值是0x000001e0,也就是undefined_instruction这个函数的地址;在SDRAM中,假设执行到地址0x33f41000偏移值为0x04的地方,它就会先去0x33f41000偏移值为0x20的地方取值,然后跳转到0x33f41000偏移值为0x1e0的地方(0x33f41000+0x1e0)执行undefined_instruction函数。
在这里插入图片描述
程序里有些地址在链接时不能确定,要到运行前才能确定的情况:例如:printf
    写mian函数时用到printf,假若程序是动态链接,printf是在库函数里面实现的,运行时才知道printf函数的地址,汇编上会写成ldr pc ,[addr]去某个地方(addr)读值,这个地方本来的值应该是printf的地址,但是一开始不知道,就写为0,在运行前确定printf地址(是操作系统帮我们做的),把地址写入addr,再赋值给pc,从而执行程序。

    uboot重定位之后,清空bss段,然后调用C函数board_init_r进入uboot的第二阶段。

五、修改u-boot-2012.04.01代码支持jz2440开发板

1. 新建一个单板:
(1) 在board/samsung/目录下新建一个单板:

make distclean
cd board/samsung/
cp -rf smdk2410 smdk2440
  • 1
  • 2
  • 3

(2) 修改smdk2440的目录下的Makefile,把smdk2410.c 重命名为smdk2440.c
 a.把Makefile 里的smdk2410.o 改为 smdk2440.o,如下图所示:
改为
 b. 重命名smdk2410.c为 smdk2440.c:mv smdk2410.c smdk2440.c
(3) 进入include/configs/目录下,添加smdk2440.h 配置文件:

cd include/configs/
cp smdk2410.h smdk2440.h
  • 1
  • 2

(4) 修改boards.cfg:

仿照
smdk2410                     arm         arm920t     -                   samsung        s3c24x0
添加:
smdk2440                     arm         arm920t     -                   samsung        s3c24x0
  • 1
  • 2
  • 3
  • 4

(5) 配置、编译、烧写:

//在顶层目录下
make smdk2440_config
make
  • 1
  • 2
  • 3

    这样编译出来的u-boot烧写到开发板上,串口肯定不会有输出,因为相比于之前,只是添加、修改了文件名,并没有修改代码.。

六、调试

1.阅读代码分析,在start.S启动文件中发现不足,如下图所示:
  在这里插入图片描述
  从图中可知:uboot里先以60MHZ的时钟计算参数来设置内存控制器,但是MPLL 还未设置;
  处理措施: 把MPLL的设置放到start.S里,取消board_early_init_f里对MPLL的设置,同时根据新设置的时钟参数设置内存控制器。

(1) 把上图设置时钟比例参数的代码删掉,添加如下代码:

/* 2. 设置时钟 */
        ldr r0, =0x4c000014
        //  mov r1, #0x03;            // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
        mov r1, #0x05;            // FCLK:HCLK:PCLK=1:4:8
        str r1, [r0]

        /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
        mrc p15, 0, r1, c1, c0, 0       /* 读出控制寄存器 */ 
        orr r1, r1, #0xc0000000         /* 设置为“asynchronous bus mode” */
        mcr p15, 0, r1, c1, c0, 0       /* 写入控制寄存器 */

#define S3C2440_MPLL_400MHZ     ((0x5c<<12)|(0x01<<4)|(0x01))
        /* MPLLCON = S3C2440_MPLL_200MHZ */
        ldr r0, =0x4c000004
        ldr r1, =S3C2440_MPLL_400MHZ
        str r1, [r0]

        /* 启动ICACHE */
        mrc p15, 0, r0, c1, c0, 0   @ read control reg
        orr r0, r0, #(1<<12)
        mcr p15, 0, r0, c1, c0, 0   @ write it back
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

(2) 同时修改设置内存控制器参数的代码,在lowlevel_init.S中最后有如下代码:

SMRDATA:
    .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
    .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
    .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
    .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
    .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
    .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
    .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
    .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
    .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
    .word 0x32
    .word 0x30
    .word 0x30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

更改为如如下代码:

SMRDATA:
    .long 0x22011110     //BWSCON
    .long 0x00000700     //BANKCON0
    .long 0x00000700     //BANKCON1
    .long 0x00000700     //BANKCON2
    .long 0x00000700     //BANKCON3  
    .long 0x00000740     //BANKCON4
    .long 0x00000700     //BANKCON5
    .long 0x00018005     //BANKCON6
    .long 0x00018005     //BANKCON7
    .long 0x008C04F4     // REFRESH
    .long 0x000000B1     //BANKSIZE
    .long 0x00000030     //MRSRB6
    .long 0x00000030     //MRSRB7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

(3) 取消board_early_init_f里对MPLL的设置,修改/board/samsung/smdk2440/smdk2440.c中board_early_init_f函数代码如下:

int board_early_init_f(void)
{
	struct s3c24x0_clock_power * const clk_power =
					s3c24x0_get_base_clock_power();
	struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
#if 0  //注释对MPLL的设置
	/* to reduce PLL lock time, adjust the LOCKTIME register */
	writel(0xFFFFFF, &clk_power->locktime);

	/* configure MPLL */
	writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,
	       &clk_power->mpllcon);

	/* some delay between MPLL and UPLL */
	pll_delay(4000);
#endif
	/* configure UPLL */
	writel((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV,
	       &clk_power->upllcon);

	/* some delay between MPLL and UPLL */
	pll_delay(8000);

	/* set up the I/O ports */
	writel(0x007FFFFF, &gpio->gpacon);
	writel(0x00044555, &gpio->gpbcon);
	writel(0x000007FF, &gpio->gpbup);
	writel(0xAAAAAAAA, &gpio->gpccon);
	writel(0x0000FFFF, &gpio->gpcup);
	writel(0xAAAAAAAA, &gpio->gpdcon);
	writel(0x0000FFFF, &gpio->gpdup);
	writel(0xAAAAAAAA, &gpio->gpecon);
	writel(0x0000FFFF, &gpio->gpeup);
	writel(0x000055AA, &gpio->gpfcon);
	writel(0x000000FF, &gpio->gpfup);
	writel(0xFF95FFBA, &gpio->gpgcon);
	writel(0x0000FFFF, &gpio->gpgup);
	writel(0x002AFAAA, &gpio->gphcon);
	writel(0x000007FF, &gpio->gphup);

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

(4) 做了以上修改后,重新make编译成功,烧写u-boot.bin到Nor Flash,重启开发板,串口输出如下:
在这里插入图片描述
    从上图可知,串口输出的是乱码,究其原因应该是波特率设置的问题。

2. 查看波特率的设置,解决乱码的问题
(1) 查看串口波特率的设置,在arch\arm\lib中有函数board_init_f,其中有一个结构体init_sequence:

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
		if ((*init_fnc_ptr)() != 0) {
			hang ();
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5

source insight 里跳转找到这个数组:

init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
	arch_cpu_init,		/* basic arch cpu dependent setup */
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
	board_early_init_f,
#endif
#ifdef CONFIG_OF_CONTROL
	fdtdec_check_fdt,
#endif
	timer_init,		/* initialize timer */
#ifdef CONFIG_FSL_ESDHC
	get_clocks,
#endif
	env_init,		/* initialize environment */
	init_baudrate,		/* initialze baudrate settings */
	serial_init,		/* serial communications setup */
	console_init_f,		/* stage 1 init of console */
	display_banner,		/* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
	print_cpuinfo,		/* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
	checkboard,		/* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
	init_func_i2c,
#endif
	dram_init,		/* configure available RAM banks */
	NULL,
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

init_sequence里是各种的初始化,找到串口初始化函数 serial_init,跳转到/drivers/serial/serial_s3c24x0.c里:

int serial_init(void)
{
	return serial_init_dev(UART_NR);
}
  • 1
  • 2
  • 3
  • 4

接着跳转到函数:serial_init_dev

static int serial_init_dev(const int dev_index)
{
	struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);

#ifdef CONFIG_HWFLOW
	hwflow = 0;	/* turned off by default */
#endif

	/* FIFO enable, Tx/Rx FIFO clear */
	writel(0x07, &uart->ufcon);
	writel(0x0, &uart->umcon);

	/* Normal,No parity,1 stop,8 bit */
	writel(0x3, &uart->ulcon);
	/*
	 * tx=level,rx=edge,disable timeout int.,enable rx error int.,
	 * normal,interrupt or polling
	 */
	writel(0x245, &uart->ucon);

#ifdef CONFIG_HWFLOW
	writel(0x1, &uart->umcon);	/* rts up */
#endif

	/* FIXME: This is sooooooooooooooooooo ugly */
#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)
	/* we need auto hw flow control on the gsm and gps port */
	if (dev_index == 0 || dev_index == 1)
		writel(0x10, &uart->umcon);
#endif
	_serial_setbrg(dev_index);

	return (0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

接着跳转到:_serial_setbrg

void _serial_setbrg(const int dev_index)
{
	struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
	unsigned int reg = 0;
	int i;

	/* value is calculated so : (int)(PCLK/16./baudrate) -1 */
	reg = get_PCLK() / (16 * gd->baudrate) - 1;

	writel(reg, &uart->ubrdiv);
	for (i = 0; i < 100; i++)
		/* Delay */ ;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里终于看到有时钟相关的函数:get_PCLK() ,其中通过get_HCLK()函数获得PLCK时钟,然后跳转到 arch/arm/cpu/arm920t/s3c24x0/speed.c 文件的get_HCLK() :

/* return HCLK frequency */
ulong get_HCLK(void)
{
	struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
#ifdef CONFIG_S3C2440
	switch (readl(&clk_power->clkdivn) & 0x6) {
	default:
	case 0:
		return get_FCLK();
	case 2:
		return get_FCLK() / 2;
	case 4:
		return (readl(&clk_power->camdivn) & (1 << 9)) ?
			get_FCLK() / 8 : get_FCLK() / 4;
	case 6:
		return (readl(&clk_power->camdivn) & (1 << 8)) ?
			get_FCLK() / 6 : get_FCLK() / 3;
	}
#else
	return (readl(&clk_power->clkdivn) & 2) ? get_FCLK() / 2 : get_FCLK();
#endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

从source insight中可以发现#ifdef CONFIG_S3C2440 这一句是黑色的,说明没有定义这个CONFIG_S3C2440
处理措施:在include/configs/smdk2440.h: 去掉CONFIG_S3C2410 ,换成CONFIG_S3C2440,如下所示:
在这里插入图片描述
然后,make重编译,发现编译有错误如下:
在这里插入图片描述
(2) 分析图错误,使代码编译通过以测试串口是否可以正常输出:
    错误原因:应该是此时的uboot 还不支持Nand Flash,所以此时暂时去掉对Nand Flash的支持
    解决措施:把关于nand的编译选项去掉,查看drivers/mtd/nand/Makefile,找到只一句:
        在这里插入图片描述
    说明编译s3c2410_nand.c的前提是定义CONFIG_NAND_S3C2410,在源码中搜索grep -nR "CONFIG_NAND_S3C2410"
    发现在smdk2440.h中找到:
        在这里插入图片描述
    上图说明只有定义了CONFIG_CMD_NANDCONFIG_NAND_S3C2410才被定义;因此,让CONFIG_CMD_NAND不被定义即刻。
    从smdk2440.h搜索到在101行有: #define CONFIG_CMD_NAND,把它注释掉即可。

(3) make重新编译,发现有如下错误:
在这里插入图片描述
    同样的方法,去掉u-boot对yaffs文件系统的支持,另外为了减少编译后u-boot的大小,这里我们去掉对所有文 件系统的支持,把smdk2440.h对文件系统的配置全部注释掉,如下图所示:
             在这里插入图片描述
(4) 重编译 :

make distclean   (注:不清理工程,重新配置编译,直接make编译会出错)
make smdk2440_config
make
  • 1
  • 2
  • 3

(5) ls u-boot.bin
在这里插入图片描述
  由上图可知编译出来的u-boot变小了很多,只有大约213kB。

(6) 把编译出来的u-boot.bin重新烧写到开发板,串口输出正常,如下图所示:
    在这里插入图片描述
  上图可知,串口输出虽然正常了,但u-boot还无法正常启动,下一步继续修改代码完善uboot。

七、修改代码支持NAND启动

    原来的代码在链接时加了”-pie”选项, 使得u-boot.bin里多了”(.rel)”, “*(.dynsym)”使得程序非常大,不利于从NAND启动(重定位之前的启动代码应该少于4K)。
1. 去"-pie"选项:
    在文件:arch/arm/config.mk的第75行,去掉:LDFLAGS_u-boot += -pie
2.把之前编写好的init.c文件放到board/samsung/smdk2440目录,init.c文件的代码如下:

/*那么如何判断是NOR启动还是NAND启动呢? 可以利用NOR的特点:
 *1.NOR可以像内存一样读,但不可以像内存一样写;
 *2.可以通过向0地址写数据已区分是NAND启动还是NOR启动;
 *(因为NAND启动时,0地址是RAM,可读可写;但NOR启动时,0地址是NOR FLASH,可读不可写)
 *返回值:1:Nor启动; 0:NAND启动
 */
#define NFCONF  (*((volatile unsigned long *)0x4E000000))
#define NFCONT  (*((volatile unsigned long *)0x4E000004))
#define NFCMMD  (*((volatile unsigned char *)0x4E000008))
#define NFADDR  (*((volatile unsigned char *)0x4E00000c))
#define NFDATA  (*((volatile unsigned char *)0x4E000010))
#define NFSTAT  (*((volatile unsigned long *)0x4E000020))

/* GPIO */
#define GPHCON              (*(volatile unsigned long *)0x56000070)
#define GPHUP               (*(volatile unsigned long *)0x56000078)

/* UART registers*/
#define ULCON0              (*(volatile unsigned long *)0x50000000)
#define UCON0               (*(volatile unsigned long *)0x50000004)
#define UFCON0              (*(volatile unsigned long *)0x50000008)
#define UMCON0              (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0            (*(volatile unsigned long *)0x50000010)
#define UTXH0               (*(volatile unsigned char *)0x50000020)
#define URXH0               (*(volatile unsigned char *)0x50000024)
#define UBRDIV0             (*(volatile unsigned long *)0x50000028)

#define TXD0READY   (1<<2)


/* NAND flash 设置时序(需要对比s3c2440与NAND手册的NAND时序)
 *
 */
void nand_init_ll(void)
{
	/*设置NANDFlash时序*/
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
    NFCONF &= ~((3<<12)|(7<<8)|(7<<4));
	NFCONF |= (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);

	/*使能NANDflash 控制器,
	 *禁止片选(开发板没正式使用片选,避免错误的操作)
	 *初始化 ECC 编码器/译码器*/
	NFCONT = (1<<0)|(1<<1)|(1<<4);
}

static void nand_cmd(unsigned char cmd)
{
    volatile int i;
    NFCMMD = cmd;
    for(i = 0; i < 10; i++);/*等待*/
}

static void nand_addr(unsigned int addr)
{
    unsigned int col = addr % 2048;
    unsigned int page = addr / 2048;
    volatile int i;
    
    NFADDR = col & 0xff;
    for(i = 0; i < 10; i++);/*等待*/
    NFADDR = (col >> 8) & 0xff;
    for(i = 0; i < 10; i++);/*等待*/
    NFADDR = page & 0xff;
    for(i = 0; i < 10; i++);/*等待*/
    NFADDR = (page >> 8) & 0xff;
    for(i = 0; i < 10; i++);/*等待*/
    NFADDR = (page >> 16) & 0xff;
    for(i = 0; i < 10; i++);/*等待*/
}

static void nand_wait_ready(void)
{
    while(!(NFSTAT & 0x01));
}

static unsigned char nand_data(void)
{
    return NFDATA;
}
static void nand_select(void)
{
    NFCONT &= ~(1<<1);
}

static void nand_deselect(void)
{
    NFCONT |= (1<<1);
}

void nand_read_ll(unsigned int addr, unsigned char *buf,unsigned int len)
{
    int col = addr % 2048;
    int i = 0;
     /*1.片选*/
    nand_select();
    while(i < len)
    {
        /*2.发出读命令00h*/
        nand_cmd(0x00);
        /*3.发出地址(分5个地址周期)*/
        nand_addr(addr);
        /*4.发出读命令30h*/
        nand_cmd(0x30);
        /*5.等待就绪(判断状态)*/
        nand_wait_ready();
        /*6.读数据*/
        for(; (col < 2048) && (i < len); col++)
        {
            buf[i] = nand_data();
            i++;
            addr++;
        }
        col = 0;
    }
        
    /*7.取消选中*/
    nand_deselect();
}

static int isBootFromNorFlash(void)
{
    volatile unsigned int *p = (volatile unsigned int*)0;
    unsigned int val;
    
    val = *p;
    *p  = 0x12345678;
    if(*p == val) /*写前写后值一样,证明无法向0地址写数据,是NOR启动*/
    {
        return 1;
    }else  /*否则是NAND启动*/
    {
        *p = val;  /*恢复数据*/
        return 0;
    }
        
}

void copy_code_to_sdram(unsigned char *src, unsigned char *dest,unsigned int len)
{
    unsigned int i=0;
    /*如果是NOR启动*/
    if(isBootFromNorFlash()) /*那么如何判断是NOR启动还是NAND启动呢? */ 
    {            
        while(i < len)
        {
            dest[i] = src[i];
            i++;
        }
    }else /*否则是NAND启动*/
    {
        nand_init_ll();/*初始化NAND*/
        nand_read_ll((unsigned int )src,dest,len);
    }
}
/*c语言如何引用连接脚本的 __bss_start、__bss_end等变量呢?
 * extern int __bss_start,__bss_end;
 * int *p = &__bss_start;
 *
 */
void clean_bss(void)
{
    extern int __bss_start,__bss_end__;
    int *p = &__bss_start;
    
    for(;p < &__bss_end__;p++)
    {
        *p = 0;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172

3. 修改board/samsung/smdk2440目录下的Makefile,把init.c 文件编译进uboot:
    
4. 修改start.S文件
    由前面的分析可知,代码重定位函数在board_init_f函数中,board_init_f做了一系列的硬件初始化后最后才执行代码重定位函数relocate_code,这样relocate_code函数很有可能不在代码的前4k范围内(NAND Flash启动硬件自动把NAND Flash的前4k代码复制到SRAM中),无法重定位。所以必须确保代码重定位函数在代码的前4k范围内。
(1) 因此,在u-boot添加自己的代码重定位函数,在start.S 有如下代码:


/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
     ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
     bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
     ldr r0,=0x00000000
     bl  board_init_f
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

更改为:

	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)  // 0x30000000 + 0x1000 - 128 
	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */
	
    bl nand_init_ll
    mov r0,#0
   	ldr r1, _TEXT_BASE
	
	ldr r2, _bss_start_ofs
	
	bl copy_code_to_sdram
	bl clean_bss
    ldr pc,=call_board_init_f
/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
	ldr	r0,=0x00000000
	bl	board_init_f
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

(2) 注释掉u-boot自身的重定位代码:

/*------------------------------------------------------------------------------*/

/*
 * void relocate_code (addr_sp, gd, addr_moni)
 *
 * This "function" does not return, instead it continues in RAM
 * after relocating the monitor code.
 *
 */
 #if 0
	.globl	relocate_code
relocate_code:
	mov	r4, r0	/* save addr_sp */
	mov	r5, r1	/* save addr of gd */
	mov	r6, r2	/* save addr of destination */

	/* Set up the stack						    */
stack_setup:
	mov	sp, r4
   /* adr 与 ldr伪指令的区别:
    * adr r0, _start   得到的是 _start 的当前执行位置,由 pc+offset 决定
    * ldr r0, =_start  得到的是绝对的地址,链接时决定
	*/
	adr	r0, _start 
	cmp	r0, r6
	beq	clear_bss		/* skip relocation */
	mov	r1, r6			/* r1 <- scratch for copy_loop */
	ldr	r3, _bss_start_ofs
	add	r2, r0, r3		/* r2 <- source end address	    */ 

copy_loop:
	ldmia	r0!, {r9-r10}		/* copy from source address [r0]    *///将r0处的两个32位数据拷到r9-r10中,然后r0+=8
	stmia	r1!, {r9-r10}		/* copy to   target address [r1]    */  //将拷出来的两个数据放入r1(重定位地址)处,然后r1+=8
	cmp	r0, r2			/* until source end address [r2]    */// uboot 重定位到0x33F41000这个地址
	blo	copy_loop

#ifndef CONFIG_SPL_BUILD
	/*
	 * fix .rel.dyn relocations
	 */
	ldr	r0, _TEXT_BASE		/* r0 <- Text base *///r0=text段基地址=0
	sub	r9, r6, r0		/* r9 <- relocation offset */ //r9= 重定位后的偏移值=0x33F41000
	ldr	r10, _dynsym_start_ofs	/* r10 <- sym table ofs */ //_dynsym_start_ofs =__dynsym_start - _start=0x73608                                         
	add	r10, r10, r0		/* r10 <- sym table in FLASH */ //所以r10=动态符号表的起始偏移值=0x73608
	ldr	r2, _rel_dyn_start_ofs	/* r2 <- rel dyn start ofs *///r2=__rel_dyn_start - _start=0x6b568
	add	r2, r2, r0		/* r2 <- rel dyn start in FLASH */   //所以r2=相对动态信息的起始偏移值=0x6b568
	ldr	r3, _rel_dyn_end_ofs	/* r3 <- rel dyn end ofs */ // _rel_dyn_end_ofs=__rel_dyn_end - _start=0x73608
	add	r3, r3, r0		/* r3 <- rel dyn end in FLASH */    //所以r3=相对动态信息的结束偏移值=0x00073608
fixloop:
	ldr	r0, [r2]		/* r0 <- location to fix up, IN FLASH! */ //从反汇编可知,r2=0x6b568地址处的内容= 0x20,所以r0 = 0x20
	add	r0, r0, r9		/* r0 <- location to fix up in RAM */  //r0=0x33F41000+0x20=0x33F41020
	ldr	r1, [r2, #4]    // r2 + 4 =0x6b56C ,地址0x6b56C的内容是0x17,所以r1 = 0x17
	and	r7, r1, #0xff   // r7 = 0x17& 0xff = 0x17
	cmp	r7, #23			/* relative fixup? */  // 0x 17的十进制就是 23 ,因此r7与23 相等,下一条指令将跳转到fixabs
	beq	fixrel
	cmp	r7, #2			/* absolute fixup? */
	beq	fixabs
	/* ignore unknown type of fixup */
	b	fixnext
fixabs:
	/* absolute fix: set location to (offset) symbol value */
	mov	r1, r1, LSR #4		/* r1 <- symbol index in .dynsym */
	add	r1, r10, r1		/* r1 <- address of symbol in table */
	ldr	r1, [r1, #4]		/* r1 <- symbol value */
	add	r1, r1, r9		/* r1 <- relocated sym addr */
	b	fixnext
fixrel:
	/* relative fix: increase location by offset */
	ldr	r1, [r0]     // r0 = 0x33F41020, 可知知道这个地址的内容即是反汇编0x20地址的内容0x000001e0
	add	r1, r1, r9   // r1 = r1 + r9 = 0x33F41000 + 0x000001e0 = 0x33F411e0 
fixnext:
	str	r1, [r0]    //改变链接地址里的内容, 0x33F41020=0x33F411e0  (之前为0x1e0)
	add	r2, r2, #8		/* each rel.dyn entry is 8 bytes */  //r2等于下一个相对动态信息(0x24)的地址
	cmp	r2, r3     //若没到尾部__rel_dyn_end,便继续执行: fixloop
	blo	fixloop
#endif

clear_bss:
#ifndef CONFIG_SPL_BUILD
	ldr	r0, _bss_start_ofs
	ldr	r1, _bss_end_ofs
	mov	r4, r6			/* reloc addr */
	add	r0, r0, r4
	add	r1, r1, r4
	mov	r2, #0x00000000		/* clear			    */

clbss_l:str	r2, [r0]		/* clear loop...		    */
	add	r0, r0, #4
	cmp	r0, r1
	bne	clbss_l

	bl coloured_LED_init
	bl red_led_on
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

5. 修改代码重定位地址CONFIG_SYS_TEXT_BASE为0x33f00000
    在前面的u-boot重定位函数copy_code_to_sdram中,使用到_TEXT_BASE作为copy_code_to_sdram函数的目的地址,那么它在哪里被定义呢?
在代码中跳转到 :_TEXT_BASE

 bl nand_init_ll
 mov r0,#0
 ldr r1, _TEXT_BASE
  • 1
  • 2
  • 3

跳转到lowlevel_init.S中的这里:

_TEXT_BASE:
	.word	CONFIG_SYS_TEXT_BASE
  • 1
  • 2

然后点击:CONFIG_SYS_TEXT_BASE,跳转到smdk2440.h中的这里:

#define CONFIG_SYS_TEXT_BASE    0x0
  • 1

然后把CONFIG_SYS_TEXT_BASE的值改为0x33f00000

#define CONFIG_SYS_TEXT_BASE    0x33f00000
  • 1

6. 修改board_init_f(arch/arm/lib/board.c中), 把relocate_code去掉:

//relocate_code(addr_sp, id, addr);
  • 1

同时去掉:

//	addr -= gd->mon_len;  // addr = 0x33ff0000 - 0x000ae4e0 = 0x33F41B20
//	addr &= ~(4096 - 1);  // 4k对齐 addr = 0x33F41B20 & ~0xfff = 0x33F41000
  • 1
  • 2

改为:

addr = CONFIG_SYS_TEXT_BASE;
  • 1

因为在调用uboot第二阶段的函数void board_init_r(gd_t *id, ulong dest_addr)之前需要重新设置栈,栈空间等内存分布在board_init_f中设置,在start.S中把值赋给sp;第二阶段函数所用得到的参数id也在board_init_f设置,而参数dest_addr就是uboot代码重定位的目的地址等于_TEXT_BASE。所以,需要在start.S 定义存储sp的全局变量base_sp和存储id的全局变量gd_addr

.globl base_sp
base_sp:
	.long 0
.globl gd_addr
gd_addr:
	.long 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

同时,在start.S 中进入

同时在board_init_f函数的开头,引入这两个变量,

extern unsigned long base_sp;
extern unsigned long gd_addr;
  • 1
  • 2

最后,board_init_f函数修改如下:

void board_init_f(ulong bootflag)
{
	bd_t *bd;
	init_fnc_t **init_fnc_ptr;
	gd_t *id;
	ulong addr, addr_sp;
	extern unsigned long base_sp;
	extern unsigned long gd_addr;
#ifdef CONFIG_PRAM
	ulong reg;
#endif

	bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");

	/* Pointer is writable since we allocated a register for it */
	gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
	/* compiler optimization barrier needed for GCC >= 3.4 */
	__asm__ __volatile__("": : :"memory");

	memset((void *)gd, 0, sizeof(gd_t));

	gd->mon_len = _bss_end_ofs;
#ifdef CONFIG_OF_EMBED
	/* Get a pointer to the FDT */
	gd->fdt_blob = _binary_dt_dtb_start;
#elif defined CONFIG_OF_SEPARATE
	/* FDT is at end of image */
	gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE);
#endif
	/* Allow the early environment to override the fdt address */
	gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
						(uintptr_t)gd->fdt_blob);

	for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
		if ((*init_fnc_ptr)() != 0) {
			hang ();
		}
	}

#ifdef CONFIG_OF_CONTROL
	/* For now, put this check after the console is ready */
	if (fdtdec_prepare_fdt()) {
		panic("** CONFIG_OF_CONTROL defined but no FDT - please see "
			"doc/README.fdt-control");
	}
#endif

	debug("monitor len: %08lX\n", gd->mon_len);
	/*
	 * Ram is setup, size stored in gd !!
	 */
	debug("ramsize: %08lX\n", gd->ram_size);
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
	/*
	 * Subtract specified amount of memory to hide so that it won't
	 * get "touched" at all by U-Boot. By fixing up gd->ram_size
	 * the Linux kernel should now get passed the now "corrected"
	 * memory size and won't touch it either. This should work
	 * for arch/ppc and arch/powerpc. Only Linux board ports in
	 * arch/powerpc with bootwrapper support, that recalculate the
	 * memory size from the SDRAM controller setup will have to
	 * get fixed.
	 */
	gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif

	addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;

#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
	/* reserve kernel log buffer */
	addr -= (LOGBUFF_RESERVE);
	debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN,
		addr);
#endif
#endif

#ifdef CONFIG_PRAM
	/*
	 * reserve protected RAM
	 */
	reg = getenv_ulong("pram", 10, CONFIG_PRAM);
	addr -= (reg << 10);		/* size is in kB */
	debug("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
#endif /* CONFIG_PRAM */

#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
	/* reserve TLB table */
	addr -= (4096 * 4);

	/* round down to next 64 kB limit */
	addr &= ~(0x10000 - 1);

	gd->tlb_addr = addr;
	debug("TLB table at: %08lx\n", addr);
#endif

	/* round down to next 4 kB limit */
	addr &= ~(4096 - 1);
	debug("Top of RAM usable for U-Boot at: %08lx\n", addr);

#ifdef CONFIG_LCD
#ifdef CONFIG_FB_ADDR
	gd->fb_base = CONFIG_FB_ADDR;
#else
	/* reserve memory for LCD display (always full pages) */
	addr = lcd_setmem(addr);
	gd->fb_base = addr;
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */

	/*
	 * reserve memory for U-Boot code, data & bss
	 * round down to next 4 kB limit
	 */
	//addr -= gd->mon_len;
	//addr &= ~(4096 - 1);
    addr = CONFIG_SYS_TEXT_BASE;

	debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);

#ifndef CONFIG_SPL_BUILD
	/*
	 * reserve memory for malloc() arena
	 */
	addr_sp = addr - TOTAL_MALLOC_LEN;
	debug("Reserving %dk for malloc() at: %08lx\n",
			TOTAL_MALLOC_LEN >> 10, addr_sp);
	/*
	 * (permanently) allocate a Board Info struct
	 * and a permanent copy of the "global" data
	 */
	addr_sp -= sizeof (bd_t);
	bd = (bd_t *) addr_sp;
	gd->bd = bd;
	debug("Reserving %zu Bytes for Board Info at: %08lx\n",
			sizeof (bd_t), addr_sp);

#ifdef CONFIG_MACH_TYPE
	gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif

	addr_sp -= sizeof (gd_t);
	id = (gd_t *) addr_sp;
	debug("Reserving %zu Bytes for Global Data at: %08lx\n",
			sizeof (gd_t), addr_sp);

	/* setup stackpointer for exeptions */
	gd->irq_sp = addr_sp;
#ifdef CONFIG_USE_IRQ
	addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
	debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
		CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif
	/* leave 3 words for abort-stack    */
	addr_sp -= 12;

	/* 8-byte alignment for ABI compliance */
	addr_sp &= ~0x07;
#else
	addr_sp += 128;	/* leave 32 words for abort-stack   */
	gd->irq_sp = addr_sp;
#endif

	debug("New Stack Pointer is: %08lx\n", addr_sp);

#ifdef CONFIG_POST
	post_bootmode_init();
	post_run(NULL, POST_ROM | post_bootmode_get(0));
#endif

	gd->bd->bi_baudrate = gd->baudrate;
	/* Ram ist board specific, so move it to board code ... */
	dram_init_banksize();
	display_dram_config();	/* and display it */

	gd->relocaddr = addr;
	gd->start_addr_sp = addr_sp;
	gd->reloc_off = addr - _TEXT_BASE;
	debug("relocation Offset is: %08lx\n", gd->reloc_off);
	memcpy(id, (void *)gd, sizeof(gd_t));

	gd_addr = (unsigned long)id;
	base_sp = addr_sp;
//	relocate_code(addr_sp, id, addr);

	/* NOTREACHED - relocate_code() does not return */
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188

7. 修改链接脚本: 把start.S, init.c, lowlevel.S等文件放在最前面:
链接脚本为:arch/arm/cpu/u-boot.lds 最前面有下面一段代码:

      .text :
      {
          __image_copy_start = .;
          CPUDIR/start.o (.text)
          *(.text)
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

将其改为:

      .text :
      {
          __image_copy_start = .;
          CPUDIR/start.o (.text)
          board/samsung/smdk2440/libsmdk2440.o (.text)
          *(.text)
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

libsmdk2440.o包含了start.S, init.c, lowlevel.S,所以只需要把它放前。
8. 编译,烧写到NAND Flash,设置为NAND 启动观察能否正常启动:
在这里插入图片描述
从上图可知,u-boot已经支持从NAND Flash 启动了,但是在打印DRAM后就卡死了,经检查发现,卡死在board_init_f函数里无法继续执行。在board_init_f函数最后添加puts("test1\r\n"),编译烧写后打印如下:在这里插入图片描述
把u-boot烧写到开发板后,会一直重复打印上图的内容。找了很久也找不出原因,最终从admithhq 的博客找到了解决办法:在include/common.h中的276行,unsigned int board_init_f (ulong) __attribute__ ((noreturn));__attribute__ ((noreturn))去掉,否则board_init_r无法取得需要的参数,导致程序不能正常运行。(同时也把· void board_init_r (gd_t *, ulong) __attribute__ ((noreturn));__attribute__ ((noreturn))去掉)
去掉后,重新编译uboot并烧写到开发板,可以正常的往下执行,如下图所示:
在这里插入图片描述

八、修改代码支持NOR Flash

1.从上一节把uboot烧写到NAND启动后,最后打印出Flash: *** failed ***,然后提示### ERROR ### Please RESET the board ###,让我们重启开发板。
(1)我们在source insight中搜索“Flash:”这个字符串出现在哪里,在board.c中的board_init_r函数中,有以下代码:


#if !defined(CONFIG_SYS_NO_FLASH)
	puts("Flash: ");

	flash_size = flash_init();
	if (flash_size > 0) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
		char *s = getenv("flashchecksum");

		print_size(flash_size, "");
		/*
		 * Compute and print flash CRC if flashchecksum is set to 'y'
		 *
		 * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
		 */
		if (s && (*s == 'y')) {
			printf("  CRC: %08X", crc32(0,
				(const unsigned char *) CONFIG_SYS_FLASH_BASE,
				flash_size));
		}
		putc('\n');
# else	/* !CONFIG_SYS_FLASH_CHECKSUM */
		print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
	} else {
		puts(failed);
		hang();
	}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

上面代码有以下两行:

		puts(failed);
		hang();
  • 1
  • 2

其中:failed是一个指针,它的定义是:static char *failed = "*** failed ***\n";
hang()函数的定义为:

void hang(void)
{
	puts("### ERROR ### Please RESET the board ###\n");
	for (;;);
}
  • 1
  • 2
  • 3
  • 4
  • 5

结合前面NAND启动的串口输出信息可知,Flash初始化失败,程序执行puts(failed)后进入hang()死循环。
Flash初始化失败,回看Flash初始化函数:flash_size = flash_init();, flash_init()的代码如下(在drivers/mtd/cfi_flash.c中):

unsigned long flash_init (void)
{
	unsigned long size = 0;
	int i;

#ifdef CONFIG_SYS_FLASH_PROTECTION
	/* read environment from EEPROM */
	char s[64];
	getenv_f("unlock", s, sizeof(s));
#endif

	/* Init: no FLASHes known */
	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) {
		flash_info[i].flash_id = FLASH_UNKNOWN;

		/* Optionally write flash configuration register */
		cfi_flash_set_config_reg(cfi_flash_bank_addr(i),
					 cfi_flash_config_reg(i));

		if (!flash_detect_legacy(cfi_flash_bank_addr(i), i))
			flash_get_size(cfi_flash_bank_addr(i), i);
		size += flash_info[i].size;
		if (flash_info[i].flash_id == FLASH_UNKNOWN) {
#ifndef CONFIG_SYS_FLASH_QUIET_TEST
			printf ("## Unknown flash on Bank %d "
				"- Size = 0x%08lx = %ld MB\n",
				i+1, flash_info[i].size,
				flash_info[i].size >> 20);
#endif /* CONFIG_SYS_FLASH_QUIET_TEST */
		}
#ifdef CONFIG_SYS_FLASH_PROTECTION
		else if ((s != NULL) && (strcmp(s, "yes") == 0)) {
			/*
			 * Only the U-Boot image and it's environment
			 * is protected, all other sectors are
			 * unprotected (unlocked) if flash hardware
			 * protection is used (CONFIG_SYS_FLASH_PROTECTION)
			 * and the environment variable "unlock" is
			 * set to "yes".
			 */
			if (flash_info[i].legacy_unlock) {
				int k;

				/*
				 * Disable legacy_unlock temporarily,
				 * since flash_real_protect would
				 * relock all other sectors again
				 * otherwise.
				 */
				flash_info[i].legacy_unlock = 0;

				/*
				 * Legacy unlocking (e.g. Intel J3) ->
				 * unlock only one sector. This will
				 * unlock all sectors.
				 */
				flash_real_protect (&flash_info[i], 0, 0);

				flash_info[i].legacy_unlock = 1;

				/*
				 * Manually mark other sectors as
				 * unlocked (unprotected)
				 */
				for (k = 1; k < flash_info[i].sector_count; k++)
					flash_info[i].protect[k] = 0;
			} else {
				/*
				 * No legancy unlocking -> unlock all sectors
				 */
				flash_protect (FLAG_PROTECT_CLEAR,
					       flash_info[i].start[0],
					       flash_info[i].start[0]
					       + flash_info[i].size - 1,
					       &flash_info[i]);
			}
		}
#endif /* CONFIG_SYS_FLASH_PROTECTION */
	}

	flash_protect_default();
#ifdef CONFIG_FLASH_CFI_MTD
	cfi_mtd_init();
#endif

	return (size);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

上面的程序有一个if判断语句:

if (!flash_detect_legacy(cfi_flash_bank_addr(i), i))
			flash_get_size(cfi_flash_bank_addr(i), i);
  • 1
  • 2

从字面意思看出flash_detect_legacy为旧的检测flash,flash_get_size就应该为新的检测flash机制,先看一下旧的,没看出什么,再看flash_get_size,发现有很多debug调试信息,有这么多调试信息,那就应该用起来:

debug ("manufacturer is %d\n", info->vendor);
debug ("manufacturer id is 0x%x\n", info->manufacturer_id);
debug ("device id is 0x%x\n", info->device_id);
debug ("device id2 is 0x%x\n", info->device_id2);
debug ("cfi version is 0x%04x\n", info->cfi_version);
  • 1
  • 2
  • 3
  • 4
  • 5

搜索debug 查到:
在include/common.h中有如下代码:

#define debug(fmt, args...)			\
	debug_cond(_DEBUG, fmt, ##args)
  • 1
  • 2

很明显应该是用的_DEBUG,搜索_DEBUG,在include/common.h中有:

#ifdef DEBUG
#define _DEBUG	1
#else
#define _DEBUG	0
#endif
  • 1
  • 2
  • 3
  • 4
  • 5

那么我们就把#define _DEBUG 1给加上,在cfi_flash.c中定义如下两行:

#define DEBUG	1   //不确定是哪个就都定义,反正也不会出错
#define _DEBUG	1
  • 1
  • 2

重新编译u-boot烧写到NOR Flash,并设置为NOR Flash启动,启动结果如下图:
在这里插入图片描述
从打印的这句话:JEDEC PROBE: ID c2 2249 0
可知厂家ID是c2,设备ID是2249,查看NOR Flash(MX29LV160DBT)的数据手册有:
在这里插入图片描述
从数据手册可知,MX29LV160DBT的设备厂家ID是C2,设备ID是2249,与uboot读出的厂家ID、设备ID相符。
注:当把上面编译好的uboot烧写到NAND Flash,并设置为NAND启动时,uboot打印的信息如下图:
在这里插入图片描述
从上图可知,从NAND Flash启动读出的厂家ID是f0,设备ID是ea00,这明显与从NOR Flash启动读厂家ID、设备ID不一样。为什么同一个uboot在NOR Flash启动与NAND 启动读出的NOR flash厂家ID、设备ID不一样呢?(当时被这个问题卡了好久,最终在视频听到韦老师的一句话:“如果从NAND启动,跟本就没有NOR flash …”,恍然大悟。)
原因:当Jz2440开发板从NAND启动时,CPU根本看不到NOR Flash,此时的NOR Flash是不存在的。从s3c2440 的数据手册可找到它的地址空间分布如下图:

从上面的两张图可知,从NOR Flash启动时,NOR Flash 的地址范围是0 ~ 0x00200000( NOR Flahs 2MB,并且接到nGCS0),从NAND Flash启动时片内的4kB RAM 的地址范围是:0 ~ 0x00001000,当从NAND启动时,CPU是看不到NOR Flash的。

那么当从NAND 启动时读出的NOR flash厂家ID、设备ID为什么是f0、ea00呢?
原因:从上面的NOR Flash数据手册的读ID指令表可知,复位时会向0x00地址写0xF0,当读ID时往0x5550x2AA等地址写数据后,最后从
     0x00地址读出厂家ID、从0x01地址读设备ID(注:Jz2440开发板的NOR Flash的位宽是16bit,所以读取NOR Flash某个地址上的数据
     时,需要把NOR Flash对应的地址左移一位(地址乘以2),因此读设备ID时读的是内存0x02(0x01<<1)地址的内容
)。NAND Flash启动时,
     s3c2440会自动不u-boot 的前4kB内容复制到片内RAM,当读厂家ID、设备ID时,读到的内容就是片内RAM的0x00、0x02地址里的数据。
     用UltraEdit软件打开u-boot.bin文件里的数据如下图,0x00地址的内容复位NOR Flash是被改为0xF0,从0x02地址读出的16位数据是
     0xEA00 (s3c2440的存储方式是小端模式,高字节存储在高地址、低字节存储在低地址)

     

    言归正传,继续记录uboot移植,前面说uboot从NOR Flash启动正确读出了NOR Flash的厂家ID、设备ID。根据打印信息,在源码中搜索字符串“JEDEC PROBE:”在drivers/mtd/cfi_flash.c中的flash_detect_legacy函数中有如下代码片段:

				debug("JEDEC PROBE: ID %x %x %x\n",
						info->manufacturer_id,
						info->device_id,
						info->device_id2);
				if (jedec_flash_match(info, info->start[0]))
					break;
				else
					unmap_physmem((void *)info->start[0],
						      MAP_NOCACHE);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

从上面的代码可知厂家ID、设备ID是如何打印的;同时还需要通过jedec_flash_match函数进行匹配,jedec_flash_match函数代码如下:(在drivers/mtd/jedec_flash.c)

int jedec_flash_match(flash_info_t *info, ulong base)
{
	int ret = 0;
	int i;
	ulong mask = 0xFFFF;
	if (info->chipwidth == 1)
		mask = 0xFF;

	for (i = 0; i < ARRAY_SIZE(jedec_table); i++) {
		if ((jedec_table[i].mfr_id & mask) == (info->manufacturer_id & mask) &&
		    (jedec_table[i].dev_id & mask) == (info->device_id & mask)) {
			fill_info(info, &jedec_table[i], base);
			ret = 1;
			break;
		}
	}
	return ret;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

jedec_flash_match发现一个数组jedec_table,匹配设备的ID用的应该就是这个数组里的内容了,查看数组如下:


static const struct amd_flash_info jedec_table[] = {
#ifdef CONFIG_SYS_FLASH_LEGACY_256Kx8
	{
		.mfr_id		= (u16)SST_MANUFACT,
		.dev_id		= SST39LF020,
		.name		= "SST 39LF020",
		.uaddr		= {
			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
		},
		.DevSize	= SIZE_256KiB,
		.CmdSet		= P_ID_AMD_STD,
		.NumEraseRegions= 1,
		.regions	= {
			ERASEINFO(0x01000,64),
		}
	},
#endif
......
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这个结构体里的内容,定义了许多类型的flash,每一个定义就是一个flash芯片。我们在里面自己定义我们的芯片结构项:

{
         .mfr_id     =  (u16)0x00C2,/*厂家ID*/
         .dev_id     = 0x2249,           /*设备ID*/
         .name       = "MXIC MX29LV160DB",
         .uaddr      = {
             [1] = MTD_UADDR_0x0555_0x02AA /* x16 *//*NOR FLASH看到的解锁地址*/
         },
         .DevSize        = SIZE_2MiB,
         .CmdSet         = P_ID_AMD_STD,
         .NumEraseRegions    = 4, /* 擦除区域的数目 */
         .regions        = {
             ERASEINFO(16*1024, 1),
             ERASEINFO(8*1024, 2),
             ERASEINFO(32*1024, 1),
             ERASEINFO(64*1024, 31),

         }
     },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

同时把board_init_r函数中的以下两行代码注释掉,让u-boot继续运行,避免u-boot卡死在死循环里。

		//puts(failed);
		//hang();
  • 1
  • 2

然后,重新编译u-boot,烧写到NOR Flash并启动运行,打印信息如下:
在这里插入图片描述
从上图可知,已经成功打印出Flash的容量:2MiB;但是还显示错误ERROR: too many flash sectors

(2) 从源码中搜索错误ERROR: too many flash sectors,发现在drivers/mtd/cfi_flash.c有如下代码:

if (sect_cnt >= CONFIG_SYS_MAX_FLASH_SECT) {
					printf("ERROR: too many flash sectors\n");
					break;
				}
  • 1
  • 2
  • 3
  • 4

跳转到CONFIG_SYS_MAX_FLASH_SECT这个定义(在smdk2440.h中),有:

#define CONFIG_SYS_MAX_FLASH_SECT	(19)
  • 1

从下图的NOR Flash的数据手册可知,Secter总共有35个;因此,把CONFIG_SYS_MAX_FLASH_SECT宏定义改为::

#define CONFIG_SYS_MAX_FLASH_SECT	(35)
  • 1

在这里插入图片描述
此外,在cfi_flash.c中把Debug调试信息去掉:

//#define DEBUG	1   //不确定是哪个就都定义,反正也不会出错
//#define _DEBUG	1
  • 1
  • 2

重新编译u-boot,烧写到NOR Flash并启动运行,打印信息如下:
在这里插入图片描述
从上图可知,已经可以正常启动uboot了,那么现在测试一下NOR FLASH能否擦除与读写。
① 先解除写保护,在串口中输入:protect off all
② 输入:flinfo;打印正常:
在这里插入图片描述
③ 输入:erase 80000 8ffff
在这里插入图片描述
④ 输入:cp.b 30000000 80000 10000
在这里插入图片描述
从上图可知,NOR Flash 写成功。

(2) 测试norflash写功能的完整性:
在串口输入以下命令:

protect off all   //关闭写保护
erase 80000 8ffff
cp.b 30000000 80000 10000
md.b 80000      //显示0x80000地址的内容
md.b 30000000
cmp.b 30000000 80000 10000  //比较0x30000000地址的内容与0x80000地址的内容,比较数据长度0x10000字节
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

结果如下图:
在这里插入图片描述
由上图可知,NOR Flash读写测试成功。

九、修改代码支持NAND Flash

1.在之前为让uboot编译通过,我们把NAND FLASH 给屏蔽掉了,现在把它添加回来,把include/configs/smdk2440.h里的#define CONFIG_CMD_NAND取消注释,重新编译,错误如下:
在这里插入图片描述
遇到错误要按照从第一个错误开始解决的办法,因为有时候第一个错误解决了,后面的错误可能就没有了。我们这里第一个错误是:

s3c2410_nand.c:72: error: dereferencing pointer to incomplete type
  • 1

显示大概的意思是不完整的类型,我们去s3c2410_nand.c中72行去寻找,是如下代码:

if (ctrl & NAND_NCE)
72行:            writel(readl(&nand->nfconf) & ~S3C2410_NFCONF_nFCE,
                   &nand->nfconf);
  • 1
  • 2
  • 3

调用了一个nand结构体:

struct s3c2410_nand *nand = s3c2410_get_base_nand();
  • 1

结构体如下:

#ifdef CONFIG_S3C2410
/* NAND FLASH (see S3C2410 manual chapter 6) */
struct s3c2410_nand {
	u32	nfconf;
	u32	nfcmd;
	u32	nfaddr;
	u32	nfdata;
	u32	nfstat;
	u32	nfecc;
};
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

从上面的#ifdef CONFIG_S3C2410可知,我们应该是没有定义CONFIG_S3C2410,因为之前是我们在smdk2440.h中注释掉了2410的定义,换成了2440:

//#define CONFIG_S3C2410        /* specifically a SAMSUNG S3C2410 SoC */
#define CONFIG_S3C2440      /* specifically a SAMSUNG S3C2440 SoC */
  • 1
  • 2

所以,现在我们只能修改代码支持2440的部分了,操作步骤:
① 把drivers/mtd/nand/s3c2410_nand.c复制为s3c2440_nand.c
② 修改drivers/mtd/nand/目录下的Makefile,在Makefile里搜索CONFIG_NAND_S3C2410,有如下代码:

 COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
  • 1

在这行代码后面添加下面的代码,把s3c2440_nand.c编译进uboot

COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
  • 1

③ 在include/configs/smdk2440.h中添加支持s3c2440_nand,在smdk2440.h中搜索CONFIG_NAND_S3C2410,有如下代码:

#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
  • 1
  • 2

改为:

#ifdef CONFIG_S3C2410
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#else
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_S3C2440_NAND_HWECC
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.首先去看一下NAND Flash 初始化的相关函数,在board.cboard_init_r函数中有:

void board_init_r(gd_t *id, ulong dest_addr)
{
	......
	......
	nand_init();        /* go init the NAND */
	......
	......
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

跳转到nand.c文件中,有如下代码:

void nand_init(void)
{
#ifdef CONFIG_SYS_NAND_SELF_INIT
	board_nand_init();
#else
	int i;

	for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
		nand_init_chip(i);
#endif

	printf("%lu MiB\n", total_nand_size / 1024);

#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
	/*
	 * Select the chip in the board/cpu specific driver
	 */
	board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
#endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

关于上面代码#ifdef CONFIG_SYS_NAND_SELF_INIT中的CONFIG_SYS_NAND_SELF_INIT,在nand.h中的代码如下图所示:(source insight截图),从图可知CONFIG_NAND_FSL_ELBC是黑色的,说明没有定义;因此,上面的代码不会执行board_nand_init()函数。
在这里插入图片描述
跳过board_nand_init()函数,执行nand_init_chip()函数:

static void nand_init_chip(int i)
{
	struct mtd_info *mtd = &nand_info[i];
	struct nand_chip *nand = &nand_chip[i];
	ulong base_addr = base_address[i];
	int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;

	if (maxchips < 1)
		maxchips = 1;

	mtd->priv = nand;
	nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;

	if (board_nand_init(nand))
		return;

	if (nand_scan(mtd, maxchips))
		return;

	nand_register(i);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

接下来看board_nand_init函数:(在drivers/mtd/nand/s3c2440_nand.c)

int board_nand_init(struct nand_chip *nand)
{
	u_int32_t cfg;
	u_int8_t tacls, twrph0, twrph1;
	struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
	struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();

	debug("board_nand_init()\n");

	writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);

	/* initialize hardware */
#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
	tacls  = CONFIG_S3C24XX_TACLS;
	twrph0 = CONFIG_S3C24XX_TWRPH0;
	twrph1 =  CONFIG_S3C24XX_TWRPH1;
#else
	tacls = 4;
	twrph0 = 8;
	twrph1 = 8;
#endif

	cfg = S3C2410_NFCONF_EN;
	cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
	cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
	cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
	writel(cfg, &nand_reg->nfconf);

	/* initialize nand_chip data structure */
	nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
	nand->IO_ADDR_W = (void *)&nand_reg->nfdata;

	nand->select_chip = NULL;

	/* read_buf and write_buf are default */
	/* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
	nand->read_buf = nand_read_buf;
#endif

	/* hwcontrol always must be implemented */
	nand->cmd_ctrl = s3c2410_hwcontrol;

	nand->dev_ready = s3c2410_dev_ready;

#ifdef CONFIG_S3C2410_NAND_HWECC
	nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
	nand->ecc.calculate = s3c2410_nand_calculate_ecc;
	nand->ecc.correct = s3c2410_nand_correct_data;
	nand->ecc.mode = NAND_ECC_HW;
	nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
	nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else
	nand->ecc.mode = NAND_ECC_SOFT;
#endif

#ifdef CONFIG_S3C2410_NAND_BBT
	nand->options = NAND_USE_FLASH_BBT;
#else
	nand->options = 0;
#endif

	debug("end of nand_init\n");

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

将上面代码的第6行:

struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();
  • 1

改为:

struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();
  • 1

然后往下看代码,看到设置时序的时候,与2440手册对比发现,发现时序设置不对,它是适用于2410的,并不适用于2440,将以下代码:

	cfg = S3C2410_NFCONF_EN;
	cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
	cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
	cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
	writel(cfg, &nand_reg->nfconf);
  • 1
  • 2
  • 3
  • 4
  • 5

改为:

    /* 初始化时序 */
    cfg = ((tacls-1)<<12)|((twrph0-1)<<8)|((twrph1-1)<<4);
    writel(cfg, &nand_reg->nfconf);
  • 1
  • 2
  • 3

去掉ECC的代码,因为这一部分太复杂,但是对我们的程序又没有影响太大,所以删掉下面的代码:

#ifdef CONFIG_S3C2410_NAND_HWECC
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
	struct s3c2410_nand *nand = s3c2410_get_base_nand();
	debug("s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
	writel(readl(&nand->nfconf) | S3C2410_NFCONF_INITECC, &nand->nfconf);
}

static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
				      u_char *ecc_code)
{
	struct s3c2410_nand *nand = s3c2410_get_base_nand();
	ecc_code[0] = readb(&nand->nfecc);
	ecc_code[1] = readb(&nand->nfecc + 1);
	ecc_code[2] = readb(&nand->nfecc + 2);
	debug("s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
	       mtd , ecc_code[0], ecc_code[1], ecc_code[2]);

	return 0;
}

static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
				     u_char *read_ecc, u_char *calc_ecc)
{
	if (read_ecc[0] == calc_ecc[0] &&
	    read_ecc[1] == calc_ecc[1] &&
	    read_ecc[2] == calc_ecc[2])
		return 0;

	printf("s3c2410_nand_correct_data: not implemented\n");
	return -1;
}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

然后把s3c2410_hwcontrol, s3c2410_dev_ready, s3c2410_nand, s3c2410_get_base_nand,替换成s3c2440_hwcontrol, s3c2440_dev_ready, s3c2440_nand, s3c2440_get_base_nand;

接下来分析s3c2440_hwcontrol函数,它最后面有这样几行代码:

		if (ctrl & NAND_NCE) /*使能片选*/
			writel(readl(&nand->nfconf) & ~S3C2410_NFCONF_nFCE,
			       &nand->nfconf);
		else  /*取消片选*/
			writel(readl(&nand->nfconf) | S3C2410_NFCONF_nFCE,
			       &nand->nfconf);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

上的代码是2410的配置,我们的Jz2440开发板是2440的,需要查看2440手册查看相关寄存器,我们的2440需要配置的是NFCONT寄存器,查看寄存器配置后,将上面的代码修改为:

		if (ctrl & NAND_NCE) /*使能片选*/
			writel(readl(&nand->nfcont) & ~(1<<1),
			       &nand->nfcont);
		else  /*取消片选*/
			writel(readl(&nand->nfcont) | (1<<1),
			       &nand->nfcont);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

此外,还需要使能NAND Flash控制器, 初始化ECC, 禁止片选 ,因此在s3c2440_nand.c中的board_nand_init函数中加入下代码:

    /* 初始化时序 */
    cfg = ((tacls-1)<<12)|((twrph0-1)<<8)|((twrph1-1)<<4);
    writel(cfg, &nand_reg->nfconf);
    /*使能NAND Flash控制器, 初始化ECC, 禁止片选*/ /*在初始化时序后面添加*/
    writel((1<<4)|(1<<1)|(1<<0),&nand_reg->nfcont);
  • 1
  • 2
  • 3
  • 4
  • 5

同时还需要在board_nand_init函数里加上nand->select_chip = s3c2440_nand_select;所以,将

nand->select_chip = NULL;
  • 1

修改为:

nand->select_chip = s3c2440_nand_select;
  • 1

然后编写s3c2440_nand_select函数:

static void s3c2440_nand_select(struct mtd_info *mtd, int chipnr)
{
    struct s3c2440_nand *nand = s3c2440_get_base_nand();

    switch (chipnr) {
    case -1:    /* 取消选中 */
        nand->nfcont |= (1<<1);
        break;

    case 0:     /* 选中 */
        nand->nfcont &= ~(1<<1);
        break;

    default:
        BUG();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

通过分析知道,需要修改s3c2440_hwcontrol函数重新修改为如下函数:

static void s3c2440_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
    struct nand_chip *chip = mtd->priv;
    struct s3c2440_nand *nand = s3c2440_get_base_nand();

    if (ctrl & NAND_CLE)
    {
        /* 发命令 */   
        writeb(dat, &nand->nfcmd);
    }

    if (ctrl & NAND_ALE)
    {
        /* 发地址 */
        writeb(dat, &nand->nfaddr);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

重新编译,把uboot烧写到NOR Flash并启动,串口打印信息如下:
在这里插入图片描述
从上图打印信息可知,已经识别出NAND: 256 MiB,说明我们的uboot已经支持NAND Flash。
下面从NAND Flash启动看看能否识别,首先我们从NOR启动的uboot直接把uboot拷贝到NAND,输入以下命令:

nand erase 0 80000
nand write 0 0 80000
  • 1
  • 2

然后测试烧写到NAND Flash中的uboot是否正常,输入以下命令:

nand read 30000000 0 80000
cmp.b 0 30000000 80000     //比较拷贝的代码是否全部一样
  • 1
  • 2

比较代码的结果如下,从下图可知,代码拷贝完全正确;
在这里插入图片描述
然后,设置为NAND Flash启动,并重启开发板,打印结果如下图,从图中可知,成功识别出NAND: 256 MiB
在这里插入图片描述
3.总结分析过程(涉及修改的三个文件:s3c2440_nand.c,smdk2440.h,Makefile)

nand_init
	nand_init_chip
		board_nand_init
			设置nand_chip结构体, 提供底层的操作函数
		nand_scan
			nand_scan_ident
				nand_set_defaults
					chip->select_chip = nand_select_chip;
					chip->cmdfunc = nand_command;
					chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
					
				nand_get_flash_type
					chip->select_chip
					chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
							nand_command()  // 即可以用来发命令,也可以用来发列地址(页内地址)、行地址(哪一页)
								chip->cmd_ctrl
										s3c2440_hwcontrol
								
					chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
					*maf_id = chip->read_byte(mtd);
					*dev_id = chip->read_byte(mtd);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
十、修改代码支持DM9000网卡

现在的开发板大多使用DM9000网卡了,我们的uboot源码里面也是有DM9000网卡的驱动程序,在drivers/net/目录下,文件名是dm9000x.c
1.去drivers/net/目录下的Makefile搜索dm9000字符串,搜索结果如下:
在这里插入图片描述
从图中可知,定义了CONFIG_DRIVER_DM9000,dm9000.c才会被编译进uboot里(同时去掉CONFIG_CS8900)
2.去到uboot的配置文件smdk2440.h,找到下面的宏定义:

 /*
  * Hardware drivers
  */
 #define CONFIG_CS8900       /* we have a CS8900 on-board */
 #define CONFIG_CS8900_BASE  0x19000300
 #define CONFIG_CS8900_BUS16 /* the Linux driver does accesses as shorts */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这是cs8900网卡的定义,把它去掉,加上dm9000所需要的宏:

#ifdef 0
#define CONFIG_CS8900       /* we have a CS8900 on-board */
#define CONFIG_CS8900_BASE  0x19000300
#define CONFIG_CS8900_BUS16 /* the Linux driver does accesses as shorts */
#else
#define CONFIG_DRIVER_DM9000
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

重新编译uboot,发现如下错误:
在这里插入图片描述
上图的第二行dm9000x.c:156: error: 'DM9000_DATA' undeclared (first use in this function)显示dm9000x.c没有定义DM9000_DATA这个宏参数。在经验不足,对驱动程序了解不多的情况下,我们没有办法知道这个宏参数是干什么的,但是我们可以仿照uboot中现有的程序中相同的地方模仿。我们在uboot源码中搜索DM9000_DATA这个字符串:

grep "DM9000_DATA" * -nR
  • 1

搜索结果如下:
在这里插入图片描述
我们随便找一个配置文件进去看看别人是怎么配置的,假如找的是这个吧:

include/configs/at91sam9261ek.h:159:#define DM9000_DATA                 (CONFIG_DM9000_BASE + 4)
  • 1

进入该文件,DM9000网卡的配置是这样的:

 #define CONFIG_DRIVER_DM9000
 #define CONFIG_DM9000_BASE      0x30000000
 #define DM9000_IO           CONFIG_DM9000_BASE
 #define DM9000_DATA         (CONFIG_DM9000_BASE + 4)
  • 1
  • 2
  • 3
  • 4

原来是我们少定义了一些参数。那么我们来看看如何取值CONFIG_DM9000_BASE 与DM9000_DATA。
3.DM9000属于内存类接口,操作内存类接口需要知道它的访问地址,同时还需要根据DM9000网卡的实际情况设置内存控制器的时序和位宽
(1) 设置访问地址:
首先是 CONFIG_DM9000_BASE ,这个是DM9000的基地址,是CPU用来识别网卡的一个基地址。 查看DM9000网卡原理图:它的片选引脚是:nGCS4,那么我们查看2440手册,因为网卡是内存类的接口,所以我们看2440手册内存控制器那一章,找到片选信号的地址分配表:
在这里插入图片描述
从上图可以看出:nGCS4片选管脚对应的基地址是0x20000000,所以CONFIG_DM9000_BASE的值是0x20000000,内存控制器把nGCS4管脚设为低电平,从而选中DM9000网卡。然后是 DM9000_DATA的地址取值。查看原理图知,cpu只发出了LADDR2给DM9000网卡内的CMD引脚, 说明CPU只关心访问地址的第2位,所以我们把基地址加4,因此#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
关于DM9000网卡总体的配置如下:

#define CONFIG_DRIVER_DM9000
#define CONFIG_DM9000_BASE              0x20000000
#define DM9000_IO                       CONFIG_DM9000_BASE
#define DM9000_DATA                     (CONFIG_DM9000_BASE + 4)
  • 1
  • 2
  • 3
  • 4

(2) 设置内存控制器的时序以及位宽:
   uboot中lowlevel_init.S中有设置内存控制器寄存器的相关的代码:

SMRDATA:
    .long 0x22011110     //BWSCON
	.long 0x00000700     //BANKCON0
	.long 0x00000700     //BANKCON1
	.long 0x00000700     //BANKCON2
	.long 0x00000700     //BANKCON3  
	.long 0x00000700     //BANKCON4
	.long 0x00000700     //BANKCON5
	.long 0x00018005     //BANKCON6
	.long 0x00018005     //BANKCON7
	.long 0x008C04F4     // REFRESH
	.long 0x000000B1     //BANKSIZE
	.long 0x00000030     //MRSRB6
	.long 0x00000030     //MRSRB7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

.long 0x22011110 //BWSCON这个寄存器应该是设置的位宽,.long 0x00000700 //BANKCON4(nGCS4)这个应该是设置时序的寄存器。
① 在s3c2440手册中找到BWSCON这个寄存器,关于nGCS4对应的BANK4的设置如下图的bit[19:16]
在这里插入图片描述
从上图可知,设置位宽的相关位是DW4[17:16]
在这里插入图片描述
bit[17:16]位对应的是01,BANK4的位宽是16bit,查看原理图得知,DM9000网卡的位宽刚好是16bit,所以,不需要修改了。bit[18]的等待信号DM9000上没有,不需要设置。bit[19]也没有用到,不需要设置。
② 设置时序(设置BANKCON4寄存器):
对比s3c2440与DM9000的时序图可知,
a. 地址和片选信号可以同时发出:Tacs = 0
b. 发出片选信号之后多长时间发读写信号:写:Tcos = T1 = 0ns
c. 读写信号需要维持的时间(读写脉冲):Tacc = T2 = 10ns(Tacp DM9000没有,不需要设置);
d. 读写信号取消之后,片选信号还需要维持多长时间:写:Tcoh = T4 = 3ns,读:Tcoh = T4 = 6ns
e. 片选信号取消之后,地址信号还需要维持多长时间:Tcah = T5 =0ns
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以,BANKCON4的设置如下图,BANKCON4 = 0x00000040
在这里插入图片描述
将BANKCON4 修改为0x00000040,然后从新编译uboot,编译顺利通过,把uboot烧写到NOR Flash并重新启动,打印信息如下:
在这里插入图片描述
上图显示Net: No ethernet found,那么我们去源码中搜索它在哪个地方,在源码中搜索Net:,在board.c中有:

#if defined(CONFIG_CMD_NET)
	puts("Net:   ");
	eth_initialize(gd->bd);
  • 1
  • 2
  • 3

跳转到eth_initialize,里面有一个函数board_eth_init

int board_eth_init(bd_t *bis)
{
	int rc = 0;
#ifdef CONFIG_CS8900
	rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
#endif
	return rc;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

发现没有初始化DM9000网卡。我们在DM9000网卡驱动程序中找到:dm9000_initialize,应该是DM9000的初始化函数,在uboot的顶层目录搜索:grep "dm9000_initialize" * -nR,看看有没有类似的初始化,发现多是这样:

return dm9000_initialize(bis);
  • 1

所以我们可以把board_eth_init函数改成:(在board/samsung/smdk2440/smdk2410.c的136行)

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

重新编译,启动开发板:
在这里插入图片描述
上图显示:Net: dm9000,说明已经成功识别DM9000网卡。
(3) ping 电脑测试:
① 设置开发板的IP地址:

set ipaddr 192.168.0.200
  • 1

② ping测试(ubuntu虚拟机IP是192.168.0.103)

ping 192.168.0.103
  • 1

结果如下:
在这里插入图片描述
重复ping了很多次,仍然显示:ping failed,查找原因突然想起BANKCON4的bit[10:8]=000,这个时间设置的太临界了(Tacc = T2 = 10ns),那么干脆把它设置为最大bit[10:8]=111;(注:如果bit[10:8]=001,虽然能解决ping failed的问题,但在后面使用tftp 30000000 uImage_4.3命令下载的内核,bootm 30000000启动内核时有CRC校验错误,导致内核启动失败,原因应该是下载内核时数据传输出了错误,后面把bit[10:8]改为111,完美解决内核启动出现CRC检验错误的问题。)
所以,将BANKCON4 修改为0x00000740
重新编译uboot,烧写到NOR Flash,启动开发板,重复①②步骤,打印结果如下:
在这里插入图片描述
上图显示:

*** ERROR: `ethaddr' not set
  • 1

说明没有设置MAC地址。
③ 设置MAC 地址

set ethaddr 00:0c:29:4d:e4:f4  //MAC可以随便设置
  • 1

然后重新ping,打印结果如下:(注:ping一次可能不成功,需要多ping几次)
在这里插入图片描述
上图显示:host 192.168.0.103 is alive,说明已经ping成功。
④ 下面我们试一下看看能不能用tftp下载。因为uboot没有集成TFTP工具,那么我们就用windows上的tftp工具进行下载内核:
在这里插入图片描述
由上图可知服务器的IP地址是:192.168.0.106
所以需要在开发板设置服务器IP地址:

set serverip 192.168.0.106
  • 1

然后使用fttp命令从window下载内核:

tftp 30000000 uImage_4.3
  • 1

在这里插入图片描述
⑤ 启动内核:

bootm 30000000
  • 1

打印信息如下,从图中可知,内核启动成功,同时也说明uboot支持了DM9000网卡。
在这里插入图片描述

十一、移植U-BOOT之裁剪和修改默认参数(易用性)启动内核,以及对uboot进行分区

这一节的目标:裁剪U-BOOT,使其更加易用,修改默认参数,以及制作最终修改好得补丁文件方便以后的快速移植。

    在裁剪修改之前呢,我们先来了解一下uboot的环境参数(环境变量):uboot在启动的时候首先会读取环境参数,然后判断环境参数是否有效,如果设置的环境参数是无效的,那么就使用默认的参数。

Linux系统在硬盘中(FLASH)的分区如下图所示:
在这里插入图片描述
上图显示:uboot放到第一个分区,环境参数放到第二个分区,内核放到第三个分区,文件系统放到第四个分区。
之前我们修改的U-BOOT启动后,一直有一个警告,如下图所示:
在这里插入图片描述
上图显示:CRC的参数错误,使用默认的环境变量。那么我们就从这个问题引入,在uboot中搜索:using default environment,在common/env_common.c中可找到:

const uchar default_environment[] = {
#ifdef	CONFIG_BOOTARGS
	"bootargs="	CONFIG_BOOTARGS			"\0"
#endif
#ifdef	CONFIG_BOOTCOMMAND
	"bootcmd="	CONFIG_BOOTCOMMAND		"\0"
#endif
#ifdef	CONFIG_RAMBOOTCOMMAND
	"ramboot="	CONFIG_RAMBOOTCOMMAND		"\0"
#endif
#ifdef	CONFIG_NFSBOOTCOMMAND
	"nfsboot="	CONFIG_NFSBOOTCOMMAND		"\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
	"bootdelay="	MK_STR(CONFIG_BOOTDELAY)	"\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
	"baudrate="	MK_STR(CONFIG_BAUDRATE)		"\0"
#endif
#ifdef	CONFIG_LOADS_ECHO
	"loads_echo="	MK_STR(CONFIG_LOADS_ECHO)	"\0"
#endif
#ifdef	CONFIG_ETHADDR
	"ethaddr="	MK_STR(CONFIG_ETHADDR)		"\0"
#endif
#ifdef	CONFIG_ETH1ADDR
	"eth1addr="	MK_STR(CONFIG_ETH1ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH2ADDR
	"eth2addr="	MK_STR(CONFIG_ETH2ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH3ADDR
	"eth3addr="	MK_STR(CONFIG_ETH3ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH4ADDR
	"eth4addr="	MK_STR(CONFIG_ETH4ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH5ADDR
	"eth5addr="	MK_STR(CONFIG_ETH5ADDR)		"\0"
#endif
#ifdef	CONFIG_IPADDR
	"ipaddr="	MK_STR(CONFIG_IPADDR)		"\0"
#endif
#ifdef	CONFIG_SERVERIP
	"serverip="	MK_STR(CONFIG_SERVERIP)		"\0"
#endif
#ifdef	CONFIG_SYS_AUTOLOAD
	"autoload="	CONFIG_SYS_AUTOLOAD		"\0"
#endif
#ifdef	CONFIG_PREBOOT
	"preboot="	CONFIG_PREBOOT			"\0"
#endif
#ifdef	CONFIG_ROOTPATH
	"rootpath="	CONFIG_ROOTPATH			"\0"
#endif
#ifdef	CONFIG_GATEWAYIP
	"gatewayip="	MK_STR(CONFIG_GATEWAYIP)	"\0"
#endif
#ifdef	CONFIG_NETMASK
	"netmask="	MK_STR(CONFIG_NETMASK)		"\0"
#endif
#ifdef	CONFIG_HOSTNAME
	"hostname="	MK_STR(CONFIG_HOSTNAME)		"\0"
#endif
#ifdef	CONFIG_BOOTFILE
	"bootfile="	CONFIG_BOOTFILE			"\0"
#endif
#ifdef	CONFIG_LOADADDR
	"loadaddr="	MK_STR(CONFIG_LOADADDR)		"\0"
#endif
#ifdef	CONFIG_CLOCKS_IN_MHZ
	"clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
	"pcidelay="	MK_STR(CONFIG_PCI_BOOTDELAY)	"\0"
#endif
#ifdef	CONFIG_EXTRA_ENV_SETTINGS
	CONFIG_EXTRA_ENV_SETTINGS
#endif
	"\0"
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

bootargs是传给内核的启动参数,可以设置文件系统的相关分区等。我们在配置文件smdk2440.h中定义一下:

#define CONFIG_BOOTARGS "console=ttySAC0 root=dev/mtdblock3" //代表内核从串口0启动,文件系统放到第3个分区
  • 1

bootcmd是uboot用来启动内核的参数,我们也在配置文件中定义(bootargs定义的下面)定义一下:

#define CONFIG_BOOTCOMMAND "nand read 30000000 0xbac 0x200000;bootm 30000000"
//因为现在还无得知内核的分区的具体地址,先随便设置一个值,用于演示,等我们把整个FLASH分区规划好了之后,再来设置
  • 1
  • 2

bootdelay是uboot启动后的那个倒数计时的参数,当uboot启动后,进入倒计时启动,我们按下任意键,进入uboot交互界面开始进行一些设置等操作。这里我们就去默认值。
ipaddr是uboot单板的ip地址,可以定义:

#define CONFIG_IPADDR       192.168.0.200
  • 1

ethaddr表示网卡的MAC地址,可以定义:

#define CONFIG_ETHADDR      00:0c:29:4d:e4:f4  
  • 1

设置好我们常用的参数后,我们先来裁剪一下uboot。我们在uboot命令行输入help,发现有各种命令,有些命令根本不需要,那么我们就需要把相关的宏定义给去掉。这里我们可以把去掉uboot对usb、文件系统的支持。(因为太分散了,就不做记录了)
配置好后,如果直接make编译,可能会出现错误,解决办法:

make distclean
make smdk2440_config
make
  • 1
  • 2
  • 3

然后,我们对uboot进行分区,在分区之前我们来看看之前是怎分区的:

0x00000000-0x00040000 : “bootloader” (0~256k)
0x00040000-0x00060000 : “params”
0x00060000-0x00260000 : “kernel”
0x00260000-0x10000000 : “root”
  • 1
  • 2
  • 3
  • 4

我们先来设置一下我们的参数的存放地址吧,因为之前我们设置好参数之后,每次都不敢save,就是怕破坏FLASH;
在smdk2440.h配置文件中,找到如下环境变量的相关定义:

#define CONFIG_ENV_ADDR         (CONFIG_SYS_FLASH_BASE + 0x070000)
#define CONFIG_ENV_IS_IN_FLASH
#define CONFIG_ENV_SIZE         0x10000
/* allow to overwrite serial and ethaddr */
#define CONFIG_ENV_OVERWRITE
  • 1
  • 2
  • 3
  • 4
  • 5

那么我们重新定义这些宏,该怎么定义呢?
在uboot命令行使用help命令查看save相关的信息:
在这里插入图片描述
然后我们在uboot源码中搜索字符串:saveenv
找到了类似这样的语句:common/env_nand.c:174:int saveenv(void)
我们去common目录下看看Makefile,看看env_nand.c的编译依赖哪个宏?找到了下面这句:COBJS-$(CONFIG_ENV_IS_IN_NAND) += env_nand.o,说明编译env_nand.c依赖的是CONFIG_ENV_IS_IN_NAND这个宏的定义。那么我们就定义这个宏CONFIG_ENV_IS_IN_NAND
所以我们将上面环境变量的宏重新定义如下:

#define CONFIG_ENV_IS_IN_NAND
#define CONFIG_ENV_OFFSET       0x00040000
#define CONFIG_ENV_SIZE         0x20000
#define CONFIG_ENV_RANGE        CONFIG_ENV_SIZE
  • 1
  • 2
  • 3
  • 4

然后重新编译,没有错误。
先设置好ip等,使用tftp命令下载uboot:

tftp 30000000 u-boot.bin
protect off all
erase 0 3ffff 
cp.b 30000000 0 40000
  • 1
  • 2
  • 3
  • 4

重启开发板:
在这里插入图片描述
由启动知,已经有了倒数的命令,以及我们设置的ip等,都直接就存在了。
但是还是有呢个关于CRC的警告信息,那是因为,那个CRC的变量没有写到FLSAH中,我们执行save命令,再重启:
在这里插入图片描述
此时,就没有那个警告信息了,说明参数已经成功被写进flash,说明上面修改的的参数保存的地址的区域,也设置成功了。
烧写内核:

tftp 30000000 uImage_4.3
nand erase 60000 200000
nand write 30000000 60000 200000
  • 1
  • 2
  • 3

从这里,就可以感觉到,烧写个程序要这么麻烦。下面我们就来修改代码,让烧写程序变得简单:
在配置文件中定义宏:CONFIG_CMD_MTDPARTS

#define CONFIG_CMD_MTDPARTS
#define MTDIDS_DEFAULT      "nand0=jz2440-0"    /* 表示哪一个设备 */
#define MTDPARTS_DEFAULT    "mtdparts=jz2440-0:256k(u-boot),"   \
                        "128k(params),"     \
                        "2m(kernel),"   \
                        "-(rootfs)"     \

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在board.c中654行添加:

run_command("mtdparts default",0);
  • 1

重新编译,出现错误:
在这里插入图片描述
通过查找,发现mtdcore.c中(在drivers/mtd目录下)定义了get_mtd_device_nm这个函数,应该是没有被编译进内核,所以才显示错误,去Makefile中查看,发现,还需要定义这个宏:CONFIG_MTD_DEVICE,那么我们在配置文件中再定义这个宏:

#define CONFIG_CMD_MTDPARTS
#define CONFIG_MTD_DEVICE
#define MTDIDS_DEFAULT      "nand0=jz2440-0"    /* 表示哪一个设备 */
#define MTDPARTS_DEFAULT    "mtdparts=jz2440-0:256k(u-boot),"   \
                        "128k(params),"     \
                        "2m(kernel),"   \
                        "-(rootfs)"     \
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

再重新编译,没有错误。烧写uboot,重启,在uboot命令行输入:mtdparts,显示如下:
在这里插入图片描述
上图可知,uboot支持了mtdparts命令,之前启动时出现的:Unknown command 'mtdparts' - try 'help'也消失了。(之前一直出现这提示,是因为在board.c中添加了run_command("mtdparts default",0);;为什么会添加了呢?因为board.c是直接从之前移植好的uboot直接复制过来的)
再来烧写内核时,就可以这样烧写内核了:

tftp 30000000 uImage_4.3
nand erase.part kernel
nand write 30000000 kernel
  • 1
  • 2
  • 3

然后我们再回过头把配置文件中的这句话:

#define CONFIG_BOOTCOMMAND "nand read 30000000 0xbac 0x200000;bootm 30000000"
  • 1

修改为:

#define CONFIG_BOOTCOMMAND "nand read 30000000 kernel 0x200000;bootm 30000000"
  • 1

重新编译,烧写uboot,烧写内核。重启,发现可以直接启动内核了,如下图所示:
在这里插入图片描述

十二、移植U-BOOT之支持烧写YAFFS文件系统以及制作U-BOOT补丁

    前面我们通过修改uboot的代码让它支持串口、nand flash,nor flash,网络下载文件等功能,现在我们来实现最后一个功能,实现Uboot烧写YAFFS文件系统,同时制作uboot补丁方便以后的移植,避免重复造轮子。
关于JFFS和YAFFS文件系统的区别可以参考这篇文章:JFFS和YAFFS文件系统
该uboot已经支持JFFS2文件系统的烧写了,我们先来烧写JFFS2文件系统:

tftp 30000000 fs_mini_mdev.jffs2
nand erase.part rootfs
nand write.jffs2 30000000 0x00260000 5b89a8
  • 1
  • 2
  • 3

烧写完后,需要先设置文件系统的类型,在uboot中输入命令:

set bootargs console=ttySAC0 root=/dev/mtdblock3 rootfstype=jffs2
  • 1

然后输入命令启动linux:

boot
  • 1

启动后,打印的信息如下:
在这里插入图片描述
从上图串口的输出信息可知,linux成功挂载了文件系统。
那么我们的uboot支持烧写YAFFS文件系统吗?
可以验证一下:

reboot
tftp 30000000 fs_mini_mdev.yaffs2
nand erase.part rootfs
nand write.yaffs 30000000 260000 889bc0
  • 1
  • 2
  • 3
  • 4

打印信息如下:
在这里插入图片描述
上图显示:Unknown nand command suffix '.yaffs'.,说明此时的uboot还没有支持yaffs文件系统是烧写。
在uboot源码目录中搜索:Unknown nand command suffix,在common/cmd_nand.c的608行有:

#ifdef CONFIG_CMD_NAND_YAFFS
         } else if (!strcmp(s, ".yaffs")) {
             if (read) {
                 printf("Unknown nand command suffix '%s'.\n", s);
                 return 1;
             }
             ret = nand_write_skip_bad(nand, off, &rwsize,
                         (u_char *)addr, WITH_YAFFS_OOB);
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

从上面的代码可知,如果需要支持YAFFS文件系统,必须定义宏:CONFIG_CMD_NAND_YAFFS,所以需要在配置文件smdk2440.h添加宏定义:

#define CONFIG_CMD_NAND_YAFFS
  • 1

重新编译uboot,输入以下命令烧写uboot:

tftp 30000000 u-boot.bin; protect off all; erase 0 3ffff; cp.b 30000000 0 40000
  • 1

(注:上面每条指令用英文的分号隔开)
然后输入命令重启开发板:reset
然后重新烧写YAFFS文件系统:

tftp 30000000 fs_mini_mdev.yaffs2
nand erase.part rootfs
nand write.yaffs 30000000 260000 889bc0
  • 1
  • 2
  • 3

从下图的打印信息可知,现在的uboot已经支持了yaffs文件系统的烧写。
在这里插入图片描述
启动之前把之前的设置:

set bootargs console=ttySAC0 root=/dev/mtdblock3 rootfstype=jffs2
  • 1

改为:

set bootargs console=ttySAC0 root=/dev/mtdblock3 rootfstype=yaffs2
  • 1

然后输入命令:boot,启动linux,最后的打印信息如下:
在这里插入图片描述
很显然,没有完全挂载上。
yaffs文件系统包含两部分数据,页数据,和OOB数据。我们现在来对比一下我们少烧写的yaffs文件系统的数据与原有的数据的差别:

nand dump 260000
  • 1

显示的就是yaffs文件系统的的16进制数据。通过对比,发现OOB数据区的数据不一样。
drivers/mtd/nand/nand_util.c中的nand_write_skip_bad函数中有:(在drivers/mtd/nand/nand_util.c的567行)

	if (!rval)  
		break;
  • 1
  • 2

这个地方写错了,导致只写了一页的OOB数据,应该是:

	if (rval)  
		break;
  • 1
  • 2

还有一个地方需要修改,在drivers/mtd/nand/nand_util.c的518行有:

if (!need_skip && !(flags & WITH_DROP_FFS)) {
  • 1

改为:

if (!need_skip && !(flags & WITH_DROP_FFS) && !(flags & WITH_YAFFS_OOB)) {
  • 1

否则:如果分区中没有坏块(need_skip=0)时, 将执行这个分支, 导致oob区烧写不正确。
同时,还需要把drivers/mtd/nand/nand_util.c的556行的:

ops.mode = MTD_OOB_AUTO;
  • 1

改为:

ops.mode = MTD_OOB_RAW;  //烧写时使用原本的OOB
  • 1

重新编译uboot,更新uboot,重新烧写文件系统:
① 更新uboot:

tftp 30000000 u-boot.bin; protect off all; erase 0 3ffff; cp.b 30000000 0 40000
  • 1

② 重启开发板,重新烧写文件系统:

tftp 30000000 fs_mini_mdev.yaffs2;nand erase.part rootfs;nand write.yaffs 30000000 260000 889bc0
  • 1

在uboot中输出命令:boot,启动linux,从下图的打印信息可知,linux启动成功,uboot支持了yaffs文件系统的烧写。
在这里插入图片描述
最后,制作uboot补丁:
在uboot目录下,分别输入以下命令:

make distclean
rm u-boot.dis
cd ..
mv u-boot-2012.04.01 u-boot-2012.04.01_100ask
tar jxf u-boot-2012.04.01.tar.bz2 (解压未经修改的uboot源文件)
diff -urN u-boot-2012.04.01 u-boot-2012.04.01_100ask > u-boot-2012.04.01_100ask.patch (生成补丁)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

补丁文件生成后,以后换电脑或者换系统之类的,就可以直接给uboot打补丁了:

cd u-boot-2012.04.01
patch -p1 </u-boot-2012.04.01_100ask.patch
  • 1
  • 2

至此,uboot移植已完成。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/762361
推荐阅读
相关标签
  

闽ICP备14008679号