17.1 启动原理
引导启动时,NAND Flash 存储器的开始 4K 字节将被加载到 Steppingstone 中并且执行加载到 Steppingstone 的引导代码。
通常引导代码会复制 NAND Flash 的内容到 SDRAM 中。通过使用硬件 ECC,有效地检查 NAND Flash 数据。
在复制完成的基础上,将在 SDRAM 中执行主程序。
17.2 编写代码
编写的 s3c2440 启动代码放在 board/samsung/jz2440 下,这是开发板特有的。
17.2.1 添加 nand 启动的 API 代码
在 samsung/board/jz2440 文件夹添加代码:
boot_from_nand.h
1 #ifndef __BOOT_FROM_NAND_H 2 #define __BOOT_FROM_NAND_H 3 4 #define BOOT_TYPY_NAND 0 5 #define BOOT_TYPY_NOR 1 6 7 /* NAND FLASH控制器 */ 8 #define NFCONF (*((volatile unsigned long *)0x4E000000)) ///< nandflash 配置寄存器 9 #define NFCONT (*((volatile unsigned long *)0x4E000004)) ///< nandflash 控制寄存器 10 #define NFCMMD (*((volatile unsigned char *)0x4E000008)) ///< nandflash 命令集寄存器 11 #define NFADDR (*((volatile unsigned char *)0x4E00000C)) ///< nandflash 地址集寄存器 12 #define NFDATA (*((volatile unsigned char *)0x4E000010)) ///< nandflash 数据寄存器 13 #define NFSTAT (*((volatile unsigned char *)0x4E000020)) ///< nandflash 运行状态寄存器 14 15 #define TACLS 0 16 #define TWRPH0 1 17 #define TWRPH1 0 18 19 /** bss 段首尾地址 */ 20 extern char __bss_start[0]; 21 extern char __bss_end[0]; 22 23 /** 重定位拷贝的起始和结束地址 */ 24 extern char __image_copy_start[0]; 25 extern char __image_copy_end[0]; 26 27 #endif
boot_from_nand.c
1 #include "boot_from_nand.h" 2 3 /** 片选信号操作 */ 4 static void nand_select(void) 5 { 6 /** 7 * NFCONT 寄存器的第 1 位控制片选信号 nFCE 8 * 此位为 0 使能片选, 为 1 禁止片选 9 */ 10 NFCONT &= ~(1 << 1); 11 } 12 13 /** nand 发送命令 */ 14 static void nand_write_cmd(unsigned char cmd) 15 { 16 volatile int i; 17 NFCMMD = cmd; 18 for (i = 0; i < 10; i++); 19 } 20 21 /** nand 发送地址 */ 22 static void nand_write_addr(unsigned int addr) 23 { 24 unsigned int col = addr % 2048; 25 unsigned int page = addr / 2048; 26 volatile int i; 27 28 NFADDR = col & 0xff; 29 for (i = 0; i < 10; i++); 30 NFADDR = (col >> 8) & 0xff; 31 for (i = 0; i < 10; i++); 32 33 NFADDR = page & 0xff; 34 for (i = 0; i < 10; i++); 35 NFADDR = (page >> 8) & 0xff; 36 for (i = 0; i < 10; i++); 37 NFADDR = (page >> 16) & 0xff; 38 for (i = 0; i < 10; i++); 39 } 40 41 /** 等待 nand 处于空闲状态 */ 42 static void nand_wait_idle(void) 43 { 44 while (!(NFSTAT & 1)); 45 } 46 47 /** 读取 nand 中的数据 */ 48 static unsigned char nand_read_data(void) 49 { 50 return NFDATA; 51 } 52 53 /** 取消片选 */ 54 static void nand_deselect(void) 55 { 56 NFCONT |= (1 << 1); 57 } 58 59 void boot_nand_read(unsigned int addr, unsigned char *buf, unsigned int len) 60 { 61 int col = addr % 2048; 62 int i = 0; 63 64 /* 1. 选中 */ 65 nand_select(); 66 67 while (i < len) 68 { 69 /* 2. 发出读命令00h */ 70 nand_write_cmd(0x00); 71 72 /* 3. 发出地址(分5步发出) */ 73 nand_write_addr(addr); 74 75 /* 4. 发出读命令30h */ 76 nand_write_cmd(0x30); 77 78 /* 5. 判断状态 */ 79 nand_wait_idle(); 80 81 /* 6. 读数据 */ 82 for (; (col < 2048) && (i < len); col++) 83 { 84 buf[i] = nand_read_data(); 85 i++; 86 addr++; 87 } 88 89 col = 0; 90 } 91 92 /* 7. 取消选中 */ 93 nand_deselect(); 94 } 95 96 /* 清除 BSS */ 97 void boot_clear_bss(void) 98 { 99 int *pBssStart = __bss_start; 100 int *pBssEnd = __bss_end; 101 102 for (; pBssStart < pBssEnd; pBssStart++) 103 *pBssStart = 0; 104 } 105 106 /* 判定是否是nor启动 */ 107 static int boot_type_check(void) 108 { 109 volatile int *p = (volatile int *)0; 110 int val; 111 112 val = *p; 113 *p = 0x12345678; 114 if (*p == 0x12345678) 115 { 116 /* 写成功, 是nand启动 */ 117 *p = val; 118 return BOOT_TYPY_NAND; 119 } 120 else 121 { 122 /* NOR不能像内存一样写 */ 123 return BOOT_TYPY_NOR; 124 } 125 } 126 127 128 /* 拷贝代码到sdram */ 129 void boot_copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len) 130 { 131 char *pCodeStart = __image_copy_start; 132 char *pCodeEnd = __image_copy_end; 133 char *pSrc = (char *)0; 134 int i = 0; 135 136 /* 如果是NOR启动 */ 137 if (boot_type_check()) 138 { 139 while (pCodeStart < pCodeEnd) 140 { 141 *pCodeStart = *pSrc; 142 pCodeStart++; 143 pSrc++; 144 } 145 } 146 else 147 { 148 boot_nand_read(0, (unsigned char *)pCodeStart, (unsigned int)(pCodeEnd - pCodeStart)); 149 } 150 } 151 152 static void nand_reset(void) 153 { 154 nand_select(); // 片选 155 nand_write_cmd(0xff); // 发送复位命令 156 nand_wait_idle(); // 等待空闲 157 nand_deselect(); 158 } 159 160 /** nand flash 初始化 */ 161 void boot_nand_init(void) 162 { 163 /** 设置时序 */ 164 NFCONF = (TACLS << 12) | (TWRPH0 << 8) |(TWRPH1 << 4); 165 /** 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ 166 NFCONT = (1 << 4) | (1 << 1) |(1 << 0); 167 168 /** 复位 */ 169 nand_reset(); 170 }
修改 Makefile:
这里使用 extra-y 防止 jz2440 目录编译成一个 built-in.o,boot_from_nand.o 需要独立出来,需要添加到链接脚本中。
同样的 lowlevel_init
在重定位完成之前被调用,用于初始化SDRAM,但是该函数在 bin 文件中会被链接到了 4KB 之外,因此也需要单独编译,并在 链接脚本中添加
17.2.2 重定位
前面已经说了 nand 中的前 4K 代码必须要拷贝到 芯片内部的 DRAM 中,所以必须要保证前 4K 代码必须包含重定位的代码,那么就需要对重定位进行修改,一个是保证 4K 代码包含启动代码,另一个是保证可以从 nand 启动。
代码的重定位要放在 SDRAM 初始化之后,nandflash 中的代码必须要拷贝到 SDRAM 中才能运行。
那么代码的修改就要从 cpu_init_crit(ubootarcharmcpuarm920tstart.S) 之后开始。
修改后,跳转到 _main 中去执行,里面做一些重定位判断,可以不影响操作,但是清除 BSS 那需要屏蔽掉:
前面已经做过了 BSS 段的处理,所以这里不需要再做了。
17.2.3 修改链接脚本
/arch/arm/cpu/u-boot.lds
17.2.4 配置重定位地址
为了不像从Nor flash启动时那样,针对不同的变量需要修改其在RAM中对应的地址,在从NAND flash启动时,我们在编译时直接加上在RAM中的偏移地址,使其编译之后的地址固定为将来在RAM中运行的地址。将链接地址确定为 0x33f00000
,为u-boot镜像及其上面的内容保留1MB的空间
ubootincludeconfigsjz2440.h
反汇编 u-boot,查看起始地址是否改变:arm-linux-objdump -D u-boot > u-boot.dis
可以看到起始地址已经改变
17.2.5 去点 -pie 选项
uboot/arch/arm/config.mk
17.2.6 去掉重定位方式的检查
检查用于ARM架构的u-boot的重定位方式,重定位必须是“相对的”,指的是动态链接的重定位方式。
将操作注释掉
17.2.6 设置重定位后的起始地址
u-boot 完成重定位后,要从 SRAM 中的 0x33f00000 处启动,因此要改 ubootarcharmlib elocate.S 的 relocate_vectors 向量地址:
同时 board_init_f 中的 reserve_boot 函数的栈地址也需要改: