Uboot移植之<二>------支持DM900、zImage内核启动
2014-01-17 17:43
489 查看
接着前一节uboot移植,继续做移植工作。
第一步:查找uboot源码中drivers/net/目录是否支持DM9000芯片。经查:U-BOOT-2010.06版本已经对CS8900和DM9000X网卡有比较完善的代码支持。
第二步:修改配置文件inlude/configs/tx2440.h。 why?因为s3c24xx默认支持cs8900,所以需要将代码修改成支持DM9000。需要修改哪些地方?通过查看drivers/net中dm9000x.c和dm9000x.h可知:
1、
dm9000x.h需要CONFIG_DRIVER_DM9000宏定义依赖
2、
dm9000x.c需要CONFIG_DM9000_BASE、DM9000_DATA、DM9000_IO、CONFIG_DM9000_NO_SROM等
修改后代码:
#if 0 注释到cs8900
#define CONFIG_NET_MULTI
#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 */
#endif
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_NET_MULTI 1
#define CONFIG_DM9000_NO_SROM 1
#define CONFIG_DM9000_BASE 0x20000300
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
3、给u-boot加上ping命令,用来测试网络通不通
#define CONFIG_CMD_PING
4、恢复被注释掉的网卡MAC地址和修改你合适的开发板IP地址
#define CONFIG_ETHADDR 12:34:56:78:90:ab
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR 192.168.1.200 开发板的IP地址
#define CONFIG_SERVERIP 192.168.1.100 windows XP服务器的IP地址
说明:CONFIG_IPADDR指的是开发板的IP地址,CONFIG_SERVERIP指的是windows XP服务器的IP地址,这两个IP地址必须在同一网段上,在使用网络时,必须保证电脑服务器端的IP地址和CONFIG_SERVERIP的值保持一致。
第三步:增加板级网络设置。修改board/samsung/tx2440/tx2440.c
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;
}
第四步:编译uboot.bin make –j4
编译运行时,ping 网关即 ping192.168.1.1
tx2440 # ping 192.168.1.106
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
could not establish link
Using dm9000 device
ping failed; host 192.168.1.106 isnot alive 上述现象是无法ping通。
第五步:根据第四步提示无法ping通信息可知:无法建立link连接。解决办法:修改dm9000x.c相关代码。
1、找到“could not establish link”相关代码并屏蔽
#if 0
i = 0;
while (!(phy_read(1) & 0x20)) { /* autonegation complete bit */
udelay(1000);
i++;
if (i == 10000) {
printf("could not establish link\n");
return 0;
}
}
#endif
屏蔽掉dm9000_halt函数中的内容:
static void dm9000_halt(struct eth_device *netdev)
{
#if 0
DM9000_DBG("%s\n", __func__);
/* RESET devie */
phy_write(0, 0x8000); /* PHY RESET */
DM9000_iow(DM9000_GPR, 0x01); /* Power-Down PHY */
DM9000_iow(DM9000_IMR, 0x80); /* Disable all interrupt */
DM9000_iow(DM9000_RCR, 0x00); /* Disable RX */
#endif
再次编译后运行。
tx2440 # ping 192.168.1.106
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
operating at unknown: 0 mode
Using dm9000 device
host 192.168.1.106 is
alive 表示可以ping通
下面截图是:使用TFTP工具网络下载程序
我们开发板上使用的MTD设备就是nand flash,我们在uboot中需要对 nand flash进行分区,这和我们电脑上硬盘的分区很相似。其实就是把nand flash划分几个地址区域,每个分区都有 分区名、分区的size、偏移地址。
有了分区,我们就可以规定:哪些分区存放什么程序,烧写nand flash的时候,就可以直接指定分区名,而不用在计算烧写的地址了。
在uboot中已经集成MTD分区的相关操作,我们只要加入MTD分区的配置和分区表的定义。
1、在tx2440.h配置文件中加入 MTDPARTS命令
#define CONFIG_CMD_MTDPARTS
#define CONFIG_MTD_DEVICE /*使用MTD设备*/
#define CONFIG_MTD_PARTITIONS
2、加入分区信息
#define MTDIDS_DEFAULT "nand0=nandflash0" /*设置mtdids变量*/
#define MTDPARTS_DEFAULT /*默认的MTD分区表*/
"mtdparts=nandflash0:1m@0(bios)," \
"128k(params)," \
"4m(kernel)," \
"-(root)"
分区说明:
如果定义了CONFIG_CMD_MTDPARTS,就会编译common目录下的cmd_mtdparts.c文件,这需要用到drivers/mtd/mtdcore.c中函数。就要定义CONFIG_MTD_DEVICE和CONFIG_MTD_PARTITIONS这两项,才能编译mtdcore.c和mtdpart.c。
编译uboot,烧录nand flash中。uboot运行后,输入命令:help,即可看到mtdparts命令。
现象:使用命令mtdparts试了,未打印出我们设置分区信息。而使用mtdparts default,然后再用
mtdparts,便可打印出来分区信息。如下图:
解决方法:在程序中执行一次mtdparts default命令,才能使分区生效。修改 common/mian文件,加入mtdparts_init函数调用:
#ifdef CONFIG_AUTO_COMPLETE /*自动补齐命令*/
install_auto_complete();
#endif
#ifdef CONFIG_CMD_MTDPARTS /*nand flash分区初始化*/
extern int mtdparts_init(void); //该函数在common/cmd_mtdparts.c中定义
if (!(s=getenv("mtdparts")))//获取mtdparts环境变量值
{
run_command("mtdparts default", 0); //运行命令
}
else
{
mtdparts_init();
}
#endif
具体移植步骤如下:
1、在arch/arm/cpu/lib目录下创建 boot_zImage.c文件,同时在同目录下Makefile加入编译选项:
COBJS-y += boot_zImage.o
给文件主要实现boot_zImage命令
boot_zImage函数启动内核步骤:
1、调用copy_kernel_img函数,再调用nand_copy函数,读取nand flash中0x12 0000地址处开始的内核镜像(大小为0x400000即4Mbyte)拷贝到内存中0x3000
8000地址处。
2、设置启动参数setup_linux_param( )、获取match_type
3、调用call_linux函数,启动内核
注:用nand_copy函数位于board/samsung/tx2440/nand_read.c中。
1、在include/configs/tx2440.h中加入支持yafss的配置:
#define ENABLE_CMD_NAND_YAFFS 1 /*支持Yaffs文件系统*/
#define ENABLE_CMD_NAND_YAFFS_SKIPFB 1 /*支持烧写Yaffs时,跳过第一个好bolck*/
2、加入烧写yaffs的命令支持,修改common/cmd_nand.c文件:
在U_BOOT_CMD中加入yaffs的选项
U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand, 。。。。。。。。。。。。。
3、修改 include/linux/mtd/mtd.h文件:
在mtd_info结构体中加入nand驱动中用到的两个变量:
4、在drivers/mtd/nand/nand_utilc文件中修改 nand_write_skip_bad( )函数,添加对OOB区操作支持
5、修改drivers/mtd/nand/nand_base.c文件:
在nand_do_write_ops函数加入
在nand_write函数中加入
将存放yaffs文件的buf中 data和OOB数据的分离出来,方便向nand flash写入 。(该代码参考天祥电子)
for(i=0;i<(datapages);i++)
{
memcpy( (void *)(data_buffer+i*datasize), (void *)(buf+datasize*i+i*oobsize), datasize);
memcpy( (void *)(oob_buffer+i*oobsize), (void *)(buf+datasize*(i+1)+i*oobsize),oobsize);
}
执行过程如下:
疑问:1、nand falsh中何时设置nand->writesize,nand->oobsize,nand->erasesize??
(1) 建立common/cmd_menu.c
习惯上通用命令源代码放在common目录下,与开发板专有命令源代码则放在board/<board_dir>目录下,并且习惯以“cmd_<命令名>.c”为文件名。
源码:
(2) 定义“menu”命令
在cmd_menu.c中使用如下的代码定义“menu”命令:
_BOOT_CMD(
menu, 3, 0, do_menu,
"menu - display a menu, to select the items to do something\n",
" - display a menu, to select the items to do something"
);
其中U_BOOT_CMD命令格式如下:
U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
各个参数的意义如下:
name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
maxargs:命令的最大参数个数
rep:是否自动重复(按Enter键是否会重复执行)
cmd:该命令对应的响应函数
usage:简短的使用说明(字符串)
help:较详细的使用说明(字符串)
在内存中保存命令的help字段会占用一定的内存,通过配置U-Boot可以选择是否保存help字段。若在include/configs/tx2440.h中定义了CONFIG_SYS_LONGHELP宏,则在U-Boot中使用help命令查看某个命令的帮助信息时将显示usage和help字段的内容,否则就只显示usage字段的内容。
U_BOOT_CMD宏在include/command.h中定义:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
“##”与“#”都是预编译操作符,“##”有字符串连接的功能,“#”表示后面紧接着的是一个字符串。
其中的cmd_tbl_t在include/command.h中定义如下:
struct cmd_tbl_s {
char *name; /* 命令名 */
int maxargs; /* 最大参数个数 */
int repeatable; /* 是否自动重复 */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 响应函数 */
char *usage; /* 简短的帮助信息 */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* 较详细的帮助信息 */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* 自动补全参数 */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s cmd_tbl_t;
一个cmd_tbl_t结构体变量包含了调用一条命令的所需要的信息。
其中Struct_Section在include/command.h中定义如下:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
凡是带有__attribute__ ((unused,section (".u_boot_cmd"))属性声明的变量都将被存放在".u_boot_cmd"段中,并且即使该变量没有在代码中显式的使用编译器也不产生警告信息。
在U-Boot连接脚本u-boot.lds中定义了".u_boot_cmd"段:
. = .;
__u_boot_cmd_start = .; /*将 __u_boot_cmd_start指定为当前地址 */
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .; /* 将__u_boot_cmd_end指定为当前地址 */
这表明带有“.u_boot_cmd”声明的函数或变量将存储在“u_boot_cmd”段。这样只要将U-Boot所有命令对应的cmd_tbl_t变量加上“.u_boot_cmd”声明,编译器就会自动将其放在“u_boot_cmd”段,查找cmd_tbl_t变量时只要在__u_boot_cmd_start与__u_boot_cmd_end之间查找就可以了。
因此“menu”命令的定义经过宏展开后如下:
cmd_tbl_t __u_boot_cmd_menu __attribute__ ((unused,section (".u_boot_cmd"))) = {menu, 3, 0, do_menu, "menu - display a menu, to select the items to do something\n", " - display a menu, to
select the items to do something"}
实质上就是用U_BOOT_CMD宏定义的信息构造了一个cmd_tbl_t类型的结构体。编译器将该结构体放在“u_boot_cmd”段,执行命令时就可以在“u_boot_cmd”段查找到对应的cmd_tbl_t类型结构体。
(3) 实现命令的函数
在cmd_menu.c中添加“menu”命令的响应函数的实现。具体的实现代码略:
int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
/* 实现代码略 */
}
(4) 将common/cmd_menu.c编译进u-boot.bin
在common/Makefile中加入如下代码:
COBJS-$( CONFIG_CMD_MENU) += cmd_menu.o
在include/configs/tx440.h加入如代码:
#define CONFIG_CMD_MENU
重新编译下载U-Boot就可以使用menu命令了
(5)menu命令执行的过程
在U-Boot中输入“menu”命令执行时,U-Boot接收输入的字符串“menu”,传递给run_command函数。run_command函数调用common/command.c中实现的find_cmd函数在__u_boot_cmd_start与__u_boot_cmd_end间查找命令,并返回menu命令的cmd_tbl_t结构。然后run_command函数使用返回的cmd_tbl_t结构中的函数指针调用menu命令的响应函数do_menu,从而完成了命令的执行。
#define CONFIG_CMDLINE_EDITING 支持上下键调用历史命令
#define CONFIG_AUTO_COMPLETE 支持Tab补全命令
2、再次编译,建议每次编译前都清除一下上次编译留下的中间代码:
#make distclean
#make tx2440_config
#make
3.5 支持网络DM9000
uboot中已集成了网络驱动,支持了多种网卡设备的驱动。具体移植步骤如下:第一步:查找uboot源码中drivers/net/目录是否支持DM9000芯片。经查:U-BOOT-2010.06版本已经对CS8900和DM9000X网卡有比较完善的代码支持。
第二步:修改配置文件inlude/configs/tx2440.h。 why?因为s3c24xx默认支持cs8900,所以需要将代码修改成支持DM9000。需要修改哪些地方?通过查看drivers/net中dm9000x.c和dm9000x.h可知:
1、
dm9000x.h需要CONFIG_DRIVER_DM9000宏定义依赖
2、
dm9000x.c需要CONFIG_DM9000_BASE、DM9000_DATA、DM9000_IO、CONFIG_DM9000_NO_SROM等
修改后代码:
#if 0 注释到cs8900
#define CONFIG_NET_MULTI
#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 */
#endif
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_NET_MULTI 1
#define CONFIG_DM9000_NO_SROM 1
#define CONFIG_DM9000_BASE 0x20000300
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
3、给u-boot加上ping命令,用来测试网络通不通
#define CONFIG_CMD_PING
4、恢复被注释掉的网卡MAC地址和修改你合适的开发板IP地址
#define CONFIG_ETHADDR 12:34:56:78:90:ab
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR 192.168.1.200 开发板的IP地址
#define CONFIG_SERVERIP 192.168.1.100 windows XP服务器的IP地址
说明:CONFIG_IPADDR指的是开发板的IP地址,CONFIG_SERVERIP指的是windows XP服务器的IP地址,这两个IP地址必须在同一网段上,在使用网络时,必须保证电脑服务器端的IP地址和CONFIG_SERVERIP的值保持一致。
第三步:增加板级网络设置。修改board/samsung/tx2440/tx2440.c
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;
}
第四步:编译uboot.bin make –j4
编译运行时,ping 网关即 ping192.168.1.1
tx2440 # ping 192.168.1.106
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
could not establish link
Using dm9000 device
ping failed; host 192.168.1.106 isnot alive 上述现象是无法ping通。
第五步:根据第四步提示无法ping通信息可知:无法建立link连接。解决办法:修改dm9000x.c相关代码。
1、找到“could not establish link”相关代码并屏蔽
#if 0
i = 0;
while (!(phy_read(1) & 0x20)) { /* autonegation complete bit */
udelay(1000);
i++;
if (i == 10000) {
printf("could not establish link\n");
return 0;
}
}
#endif
屏蔽掉dm9000_halt函数中的内容:
static void dm9000_halt(struct eth_device *netdev)
{
#if 0
DM9000_DBG("%s\n", __func__);
/* RESET devie */
phy_write(0, 0x8000); /* PHY RESET */
DM9000_iow(DM9000_GPR, 0x01); /* Power-Down PHY */
DM9000_iow(DM9000_IMR, 0x80); /* Disable all interrupt */
DM9000_iow(DM9000_RCR, 0x00); /* Disable RX */
#endif
再次编译后运行。
tx2440 # ping 192.168.1.106
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 08:00:3e:26:0a:5b
operating at unknown: 0 mode
Using dm9000 device
host 192.168.1.106 is
alive 表示可以ping通
下面截图是:使用TFTP工具网络下载程序
3.6加入MTD(NAND)分区
MTD分区是uboot中一个非常重要的概念,因为uboot的主要作用就是引导操作系统和烧写程序,那么我们的程序(指bootloader、内核、文件系统等)是如何存储在MTD设备中。我们开发板上使用的MTD设备就是nand flash,我们在uboot中需要对 nand flash进行分区,这和我们电脑上硬盘的分区很相似。其实就是把nand flash划分几个地址区域,每个分区都有 分区名、分区的size、偏移地址。
有了分区,我们就可以规定:哪些分区存放什么程序,烧写nand flash的时候,就可以直接指定分区名,而不用在计算烧写的地址了。
在uboot中已经集成MTD分区的相关操作,我们只要加入MTD分区的配置和分区表的定义。
1、在tx2440.h配置文件中加入 MTDPARTS命令
#define CONFIG_CMD_MTDPARTS
#define CONFIG_MTD_DEVICE /*使用MTD设备*/
#define CONFIG_MTD_PARTITIONS
2、加入分区信息
#define MTDIDS_DEFAULT "nand0=nandflash0" /*设置mtdids变量*/
#define MTDPARTS_DEFAULT /*默认的MTD分区表*/
"mtdparts=nandflash0:1m@0(bios)," \
"128k(params)," \
"4m(kernel)," \
"-(root)"
分区说明:
序号 | 分区名 | 分区大小 | 偏移地址 | 作用 |
0 | bios | 0x0010 0000(1M) | 0x0000 0000 | 存放uboot |
1 | params | 0x0002 0000(128K) | 0x0010 0000 | 存储环境变量 |
2 | kernel | 0x0040 0000(4M) | 0x0012 0000 | 存放内核 |
3 | root | 0xfae0 0000(其他) | 0x0052 0000 | 存放文件系统 |
编译uboot,烧录nand flash中。uboot运行后,输入命令:help,即可看到mtdparts命令。
现象:使用命令mtdparts试了,未打印出我们设置分区信息。而使用mtdparts default,然后再用
mtdparts,便可打印出来分区信息。如下图:
解决方法:在程序中执行一次mtdparts default命令,才能使分区生效。修改 common/mian文件,加入mtdparts_init函数调用:
#ifdef CONFIG_AUTO_COMPLETE /*自动补齐命令*/
install_auto_complete();
#endif
#ifdef CONFIG_CMD_MTDPARTS /*nand flash分区初始化*/
extern int mtdparts_init(void); //该函数在common/cmd_mtdparts.c中定义
if (!(s=getenv("mtdparts")))//获取mtdparts环境变量值
{
run_command("mtdparts default", 0); //运行命令
}
else
{
mtdparts_init();
}
#endif
3.7支持内核启动
uboot默认只支持启动uImage,还不支持启动zImage。具体移植步骤如下:
1、在arch/arm/cpu/lib目录下创建 boot_zImage.c文件,同时在同目录下Makefile加入编译选项:
COBJS-y += boot_zImage.o
给文件主要实现boot_zImage命令
#include <common.h> #include <command.h> #include <image.h> #include <u-boot/zlib.h> #include <asm/byteorder.h> #include <asm/arch-s3c24x0/s3c2410.h> /*modify by yedapeng */ #include "asm/mach-types.h"/*modify by yedapeng */ #define LINUX_KERNEL_OFFSET 0x8000 #define LINUX_PARAM_OFFSET 0x100 #define LINUX_PAGE_SIZE 0x00001000 #define LINUX_PAGE_SHIFT 12 #define LINUX_ZIMAGE_MAGIC 0x016f2818 #define DRAM_SIZE 0x04000000 extern int nand_copy(unsigned char*, unsigned long, int); /* * Disable IRQs */ #define local_irq_disable() \ ({ \ unsigned long temp; \ __asm__ __volatile__( \ "mrs %0, cpsr @ local_irq_disable\n" \ " orr %0, %0, #128\n" \ " msr cpsr_c, %0" \ : "=r" (temp) \ : \ : "memory", "cc"); \ }) static inline void cpu_arm920_cache_clean_invalidate_all(void) { __asm__( " mov r1, #0\n" " mov r1, #7 << 5\n" /* 8 segments */ "1: orr r3, r1, #63 << 26\n" /* 64 entries */ "2: mcr p15, 0, r3, c7, c14, 2\n" /* clean & invalidate D index */ " subs r3, r3, #1 << 26\n" " bcs 2b\n" /* entries 64 to 0 */ " subs r1, r1, #1 << 5\n" " bcs 1b\n" /* segments 7 to 0 */ " mcr p15, 0, r1, c7, c5, 0\n" /* invalidate I cache */ " mcr p15, 0, r1, c7, c10, 4\n" /* drain WB */ ); } void cache_clean_invalidate(void) { cpu_arm920_cache_clean_invalidate_all(); } static inline void cpu_arm920_tlb_invalidate_all(void) { __asm__( "mov r0, #0\n" "mcr p15, 0, r0, c7, c10, 4\n" /* drain WB */ "mcr p15, 0, r0, c8, c7, 0\n" /* invalidate I & D TLBs */ ); } void tlb_invalidate(void) { cpu_arm920_tlb_invalidate_all(); } void call_linux(long a0, long a1, long a2) { local_irq_disable(); /*禁止终端*/ cache_clean_invalidate(); /*使cache失效*/ tlb_invalidate(); /*刷新cache*/ __asm__( "mov r0, %0\n" "mov r1, %1\n" "mov r2, %2\n" "mov ip, #0\n" "mcr p15, 0, ip, c13, c0, 0\n" /* zero PID */ "mcr p15, 0, ip, c7, c7, 0\n" /* invalidate I,D caches */ "mcr p15, 0, ip, c7, c10, 4\n" /* drain write buffer */ "mcr p15, 0, ip, c8, c7, 0\n" /* invalidate I,D TLBs */ "mrc p15, 0, ip, c1, c0, 0\n" /* get control register */ "bic ip, ip, #0x0001\n" /* disable MMU */ "mcr p15, 0, ip, c1, c0, 0\n" /* write control register */ "mov pc, r2\n" "nop\n" "nop\n" : /* no outpus */ : "r" (a0), "r" (a1), "r" (a2) /*参数传递*/ : "r0","r1","r2","ip" ); } /* * pram_base: base address of linux paramter 存放linux启动参数的地址即0x3000 0100 */ static void setup_linux_param(ulong param_base) { struct param_struct *params = (struct param_struct *)param_base; char *linux_cmd; // printf("Setup linux parameters at 0x%08lx\n", param_base); memset(params, 0, sizeof(struct param_struct)); params->u1.s.page_size = LINUX_PAGE_SIZE; /* LINUX_PAGE_SIZE=0x00001000=4Kbyte 为什么是该值? */ params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT); /* why? */ /* set linux command line */ /*bootargs--uboot传递给内核启动参数,具体定义在tx2440文件下: CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 mem=64M" */ linux_cmd = getenv ("bootargs"); if (linux_cmd == NULL) { printf("Wrong magic: could not found linux command line\n"); } else { memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1); // printf("linux command line is: \"%s\"\n", linux_cmd); } } /* * dst: destination address * src: source * size: size to copy * mt: type of storage device */ static inline int copy_kernel_img(ulong dst, const char *src, size_t size) { int ret = 0; ret = nand_copy((unsigned char *)dst, (unsigned long)src, (int)size); return ret; } int boot_zImage(ulong from, size_t size) { int ret; ulong boot_mem_base; /* base address of bootable memory */ ulong to; ulong mach_type; boot_mem_base = 0x30000000; /* copy kerne image */ to = boot_mem_base + LINUX_KERNEL_OFFSET; /*=0x3000 0000 + 0x8000*/ printf("Copy linux kernel from 0x%08lx to 0x%08lx, size = 0x%08lx ... ", from, to, size); /*函数原型:copy_kernel_img() 功能:将nand flash地址0x120000,长度为4Mbytekernel源码,复制到内存SDRAM地址0x3000 8000开始处 参数: 1、to---目的地址(即内存地址) 2、form-源地址(即nand flash地址) 3、size-复制的长度 返回值:0--表示成功复制,-1表示失败 */ ret = copy_kernel_img(to, (char *)from, size); if (ret) { printf("failed\n"); return -1; } else { printf("Copy Kernel to SDRAM done,"); } if (*(ulong *)(to + 9*4) != LINUX_ZIMAGE_MAGIC) { printf("Warning: this binary is not compressed linux kernel image\n"); printf("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4)); } else { // printf("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4)); ; } /* Setup linux parameters and linux command line */ /*1、设置启动参数地址=0x3000 0000 + 0x100=0x3000 0100*/ setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET); /* 2、设置CPU机器型号 Get machine type */ mach_type = MACH_TYPE_S3C2440; // printf("MACH_TYPE = %d\n", mach_type); /* Go Go Go */ printf("NOW, Booting Linux......\n"); call_linux(0, mach_type, to); return 0; } int do_boot_zImage (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { boot_zImage(0x120000,0x400000); return 0; } U_BOOT_CMD( boot_zImage, 3, 0, do_boot_zImage, "boot_zImage - boot Linux 's zImage\n", " - boot Linux 's zImage" );
boot_zImage函数启动内核步骤:
1、调用copy_kernel_img函数,再调用nand_copy函数,读取nand flash中0x12 0000地址处开始的内核镜像(大小为0x400000即4Mbyte)拷贝到内存中0x3000
8000地址处。
2、设置启动参数setup_linux_param( )、获取match_type
3、调用call_linux函数,启动内核
注:用nand_copy函数位于board/samsung/tx2440/nand_read.c中。
3.8 支持yaffs2启动
uboot默认不支持烧写yaff2文件系统镜像,我们只需要加入烧写yaffs文件的命令,并修改nand的驱动中写操作。1、在include/configs/tx2440.h中加入支持yafss的配置:
#define ENABLE_CMD_NAND_YAFFS 1 /*支持Yaffs文件系统*/
#define ENABLE_CMD_NAND_YAFFS_SKIPFB 1 /*支持烧写Yaffs时,跳过第一个好bolck*/
2、加入烧写yaffs的命令支持,修改common/cmd_nand.c文件:
else ret = nand_write_skip_bad(nand, off, &size, (u_char *)addr); /* hailin add start*/ #if defined(ENABLE_CMD_NAND_YAFFS) } else if ( s != NULL && !strcmp(s, ".yaffs") || !strcmp(s, ".yaffs2")) { if(read) { /*不支持 nand read.yaffs2 命令*/ printf("nand read.yaffs[2] is not provide temporarily!"); } else { nand->rw_oob = 1;//支持yaff文件的OBB区的读写 #if defined(ENABLE_CMD_NAND_YAFFS_SKIPFB) nand->skipfirstblk = 1; //跳过第一个好block #else nand->skipfirstblk = 0; #endif /********************************************** **函数原型:int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, u_char *buffer) **功能:向nand flash中写入镜像文件。写入过程中,遇到bad块跳过,写入下一个新的块。 **参数: 1、nand表示 nand_info_t型结构体 2、offset表示目的地址 即写入nand flash起始地址 3、*length表示写入镜像文件长度 4、*buffer表示源地址 即需要写入镜像文件在内存存储地址 **返回值: 0表示成功 ***********************************************/ ret = nand_write_skip_bad(nand,off,&size,(u_char *)addr); #if defined(ENABLE_CMD_NAND_YAFFS_SKIPFB) nand->skipfirstblk = 0; #endif nand->rw_oob = 0; } #endif /*hailin add end*/
在U_BOOT_CMD中加入yaffs的选项
U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand, 。。。。。。。。。。。。。
#if defined(ENABLE_CMD_NAND_YAFFS) /*hailin add start */ "nand read[.yaffs[2]] is not provide temporarily!\n" "nand write[.yaffs[2]] addr off size - write the `size' byte yaffs image starting\n" " at offset `off' from memory address `addr' (.yaffs2 for 2048+64 NAND)\n" #endif /*hailin add end */加入了烧写yaffs文件系统命令后,我们即可执行nand write.yaffs命令来烧写 yaffs文件系统镜像。
3、修改 include/linux/mtd/mtd.h文件:
在mtd_info结构体中加入nand驱动中用到的两个变量:
struct mtd_info { /* hailin add start*/ #if defined(ENABLE_CMD_NAND_YAFFS) u_char rw_oob;//支持OOB区的读取 u_char skipfirstblk;//跳过第一个好block #endif /*hailin add end*/
4、在drivers/mtd/nand/nand_utilc文件中修改 nand_write_skip_bad( )函数,添加对OOB区操作支持
size_t len_incl_bad; u_char *p_buffer = buffer; /* hailin add start 添加OOB操作的支持*/ #if defined(ENABLE_CMD_NAND_YAFFS) if(nand->rw_oob==1) { size_t oobsize = nand->oobsize; size_t datasize = nand->writesize; int datapages = 0; /*测试烧写yaffs文件印象是不是(64+2048byte)整数倍,如果是表示烧写数据正确*/ if (((*length)%(nand->oobsize+nand->writesize)) != 0) { printf ("Attempt to write error length data!\n"); return -EINVAL; } datapages = *length/(datasize+oobsize); //烧写所需页数 *length = datapages*datasize; //计算烧写到nand flash数据区中长度 left_to_write = *length; } #endif /*hailin add end*/
/*检测烧写文件在nand占用长度(包括坏块长度)*/ len_incl_bad = get_len_incl_bad (nand, offset, *length); if ((offset + len_incl_bad) > nand->size) {//烧写长度+烧写地址 大于需要烧写印象文件,表示无可用空间 printf ("Attempt to write outside the flash area\n"); return -EINVAL; } #if !defined(ENABLE_CMD_NAND_YAFFS) //add by hailin /*烧写非yaffs的镜像文件,例如uboot、kernel文件*/ if (len_incl_bad == *length) {//当在烧写文件在nand占用长度 == 烧写文件长度,表示在nand这段储存空间无bad block rval = nand_write (nand, offset, length, buffer); if (rval != 0) printf ("NAND write to offset %llx failed %d\n", offset, rval); return rval; } #endif //add by hailin while (left_to_write > 0) { size_t block_offset = offset & (nand->erasesize - 1); size_t write_size; WATCHDOG_RESET (); if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { printf ("Skip bad block 0x%08llx\n", offset & ~(nand->erasesize - 1)); offset += nand->erasesize - block_offset; continue; } /*hailin add start*/ #if defined(ENABLE_CMD_NAND_YAFFS) if(nand->skipfirstblk==1) {//跳过第一个好block nand->skipfirstblk=0; printf ("Skip the first good block %08lx\n", offset & ~(nand->erasesize - 1)); offset += nand->erasesize - block_offset; continue; } #endif /*hailin add end*/ if (left_to_write < (nand->erasesize - block_offset)) write_size = left_to_write; else write_size = nand->erasesize - block_offset; printf("\rWriting at 0x%08lx --",offset); //hailin add rval = nand_write (nand, offset, &write_size, p_buffer); if (rval != 0) { printf ("NAND write to offset %llx failed %d\n", offset, rval); *length -= left_to_write; return rval; } left_to_write -= write_size; printf("%d%% is complete.",100-(left_to_write/(*length/100))); //add by yedapeng offset += write_size; //p_buffer += write_size; /* add for hailin start*/ #if defined(ENABLE_CMD_NAND_YAFFS) if(nand->rw_oob==1) { p_buffer += write_size+(write_size/nand->writesize*nand->oobsize); } else { p_buffer += write_size; } #else p_buffer += write_size; #endif } /* add for hailin end*/ return 0; }
5、修改drivers/mtd/nand/nand_base.c文件:
在nand_do_write_ops函数加入
在nand_write函数中加入
static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { struct nand_chip *chip = mtd->priv; int ret; /*add by hailin begin*/ /* 以K9F2G08U0B为例,该nand flash一页为(2K+64)字节(2K 表示的是 main 区容量, 64 表示的是 spare 区容量),它的一块为 64 页,而整个设备包括了2048个块。 则mtd->oobsize=64,mtd->writesize=2048 */ #if defined(ENABLE_CMD_NAND_YAFFS) int oldopsmode = 0; u_char data_buffer[len] ,oob_buffer[len/(mtd->writesize)*mtd->oobsize]; if(mtd->rw_oob==1) { size_t oobsize = mtd->oobsize; size_t datasize = mtd->writesize; int i = 0; uint8_t oobtemp[oobsize]; int datapages = 0; datapages = len/(datasize); /* 下面for循环函数作用:将buf内存中yaffs文件中data区和oob区, 分别存储在data_buffer和obb_buffer两个内存块。 具体步骤: 1、将buf内存地址中data数据复制到data_buffer中 2、将buf内存地址中oob数据复制到oob_buffer中 3、复制次数=页数datapage */ for(i=0;i<(datapages);i++) { memcpy((void *)(data_buffer+i*datasize), (void *)(buf+datasize*i+i*oobsize), datasize); memcpy((void *)(oob_buffer+i*oobsize), (void *)(buf+datasize*(i+1)+i*oobsize), oobsize); } } #endif /* Do not allow reads past end of device */ if ((to + len) > mtd->size) return -EINVAL; if (!len) return 0; nand_get_device(chip, mtd, FL_WRITING); chip->ops.len = len; /* hailin add start*/ if(mtd->rw_oob!=1) {//是否支持OOB区读写? chip->ops.datbuf = (uint8_t *)buf; // } else{ chip->ops.datbuf = (uint8_t *)data_buffer;//支持OOB区读写,设置data区 } #if defined(ENABLE_CMD_NAND_YAFFS) if(mtd->rw_oob!=1) {//是否支持OOB区读写? chip->ops.oobbuf = NULL; } else { //chip->ops.oobbuf = (uint8_t *)(buf+len); chip->ops.oobbuf = (uint8_t *)oob_buffer;//设置OOB区 chip->ops.ooblen = mtd->oobsize;//设置OOB区长度 oldopsmode = chip->ops.mode; chip->ops.mode = MTD_OOB_RAW; } #else chip->ops.oobbuf = NULL; //未定义NAND_YAFFS命令,则不使用OOB区 #endif /*hailin add end*/ //chip->ops.datbuf = (uint8_t *)buf; /*此处是否需要屏蔽,需要屏蔽*/ //chip->ops.oobbuf = NULL;/*此处是否需要屏蔽,需要屏蔽*/ ret = nand_do_write_ops(mtd, to, &chip->ops);//执行写操作 *retlen = chip->ops.retlen; nand_release_device(mtd);//释放nand flash /*hailin add start*/ #if defined(ENABLE_CMD_NAND_YAFFS) chip->ops.mode = oldopsmode; #endif /*hailin add end*/ return ret; }
将存放yaffs文件的buf中 data和OOB数据的分离出来,方便向nand flash写入 。(该代码参考天祥电子)
for(i=0;i<(datapages);i++)
{
memcpy( (void *)(data_buffer+i*datasize), (void *)(buf+datasize*i+i*oobsize), datasize);
memcpy( (void *)(oob_buffer+i*oobsize), (void *)(buf+datasize*(i+1)+i*oobsize),oobsize);
}
执行过程如下:
疑问:1、nand falsh中何时设置nand->writesize,nand->oobsize,nand->erasesize??
3.9 支持menu菜单选项
下面以添加menu命令(启动菜单)为例讲解U-Boot添加命令的方法。(1) 建立common/cmd_menu.c
习惯上通用命令源代码放在common目录下,与开发板专有命令源代码则放在board/<board_dir>目录下,并且习惯以“cmd_<命令名>.c”为文件名。
源码:
#include <common.h> #include <command.h> static char awaitkey(unsigned long delay, int* error_p) { int i; char c; if (delay == -1) { while (1) { if (tstc()) /* we got a key press */ return getc(); } } else { for (i = 0; i < delay; i++) { if (tstc()) /* we got a key press */ return getc(); udelay (10*1000); } } if (error_p) *error_p = -1; return 0; } void main_menu_usage(void) { printf("\r\n######## Hotips TFTP DownLoad for TX2440A ########\r\n"); printf("\r\n"); printf("[1] 下载 u-boot.bin 写入 Nand Flash\r\n"); printf("[2] 下载 Linux(zImage) 内核镜像写入 Nand Flash\r\n"); printf("[3] 下载 yaffs2(fs.yaffs2) 文件系统镜像写入 Nand Flash\r\n"); printf("[4] 下载 Linux(uImage) 内核镜像到内存并运行\r\n"); printf("[5] 重启设备\r\n"); printf("[6] 擦除nand flash\r\n"); printf("[q] 退出菜单\r\n"); printf("\r\n"); printf("输入选择: "); } void menu_shell(void) { char cmd_buf[200]; char c; //unsigned char j=1; while (1) { main_menu_usage(); //打印菜单 c = awaitkey(-1, NULL); printf("%c\n", c); switch (c) { case '1': //输入‘1’ 下载 u-boot.bin { /* 1、将镜像通过tftp下载到开始地址0x3000 0000的内存中 2、擦除需要写入空间 3、将存储在内存中镜像 烧写到起始地址为0x0的nand flash中 */ strcpy(cmd_buf,"tftp 0x30000000 u-boot.bin;nand erase 0 0x100000;nand write 0x30000000 0x0 0x100000"); run_command(cmd_buf,0); break; } case '2': //输入‘2’ 下载 Linux(zImage_2.6.31.bin) { strcpy(cmd_buf,"tftp 0x30000000 zImage_2.6.31.bin;nand erase 0x120000 0x400000;nand write 0x30000000 0x120000 0x400000"); run_command(cmd_buf,0); break; } case '3': //输入‘3’ 下载 yaffs2(root_qtfs.bin) { strcpy(cmd_buf,"tftp 0x30000000 root_qtfs.bin;nand erase 0x520000 0x3ffffc0;nand write.yaffs 0x30000000 0x520000 0x3ffffc0"); run_command(cmd_buf,0); break; } case '4': //输入‘4’ 下载 Linux(uImage) 内核镜像到内存并运行 { strcpy(cmd_buf,"tftp 0x30000000 zImage_2.6.31.bin;bootm 0x30000000"); run_command(cmd_buf,0); break; } case '5': //输入‘5’ 重启系统 { strcpy(cmd_buf,"boot_zImage"); run_command(cmd_buf,0); break; } case '6': //输入‘6’ 擦除nand flash { strcpy(cmd_buf,"nand erase 0x120000 0xc800000"); run_command(cmd_buf,0); break; } default:break; } if(c=='q') { break;} //退出菜单 } } int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { menu_shell(); return 0; } U_BOOT_CMD( menu, 3, 0, do_menu, "menu - display a menu, to select the items to do something\n", " - display a menu, to select the items to do something" );
(2) 定义“menu”命令
在cmd_menu.c中使用如下的代码定义“menu”命令:
_BOOT_CMD(
menu, 3, 0, do_menu,
"menu - display a menu, to select the items to do something\n",
" - display a menu, to select the items to do something"
);
其中U_BOOT_CMD命令格式如下:
U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
各个参数的意义如下:
name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
maxargs:命令的最大参数个数
rep:是否自动重复(按Enter键是否会重复执行)
cmd:该命令对应的响应函数
usage:简短的使用说明(字符串)
help:较详细的使用说明(字符串)
在内存中保存命令的help字段会占用一定的内存,通过配置U-Boot可以选择是否保存help字段。若在include/configs/tx2440.h中定义了CONFIG_SYS_LONGHELP宏,则在U-Boot中使用help命令查看某个命令的帮助信息时将显示usage和help字段的内容,否则就只显示usage字段的内容。
U_BOOT_CMD宏在include/command.h中定义:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
“##”与“#”都是预编译操作符,“##”有字符串连接的功能,“#”表示后面紧接着的是一个字符串。
其中的cmd_tbl_t在include/command.h中定义如下:
struct cmd_tbl_s {
char *name; /* 命令名 */
int maxargs; /* 最大参数个数 */
int repeatable; /* 是否自动重复 */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 响应函数 */
char *usage; /* 简短的帮助信息 */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* 较详细的帮助信息 */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* 自动补全参数 */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s cmd_tbl_t;
一个cmd_tbl_t结构体变量包含了调用一条命令的所需要的信息。
其中Struct_Section在include/command.h中定义如下:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
凡是带有__attribute__ ((unused,section (".u_boot_cmd"))属性声明的变量都将被存放在".u_boot_cmd"段中,并且即使该变量没有在代码中显式的使用编译器也不产生警告信息。
在U-Boot连接脚本u-boot.lds中定义了".u_boot_cmd"段:
. = .;
__u_boot_cmd_start = .; /*将 __u_boot_cmd_start指定为当前地址 */
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .; /* 将__u_boot_cmd_end指定为当前地址 */
这表明带有“.u_boot_cmd”声明的函数或变量将存储在“u_boot_cmd”段。这样只要将U-Boot所有命令对应的cmd_tbl_t变量加上“.u_boot_cmd”声明,编译器就会自动将其放在“u_boot_cmd”段,查找cmd_tbl_t变量时只要在__u_boot_cmd_start与__u_boot_cmd_end之间查找就可以了。
因此“menu”命令的定义经过宏展开后如下:
cmd_tbl_t __u_boot_cmd_menu __attribute__ ((unused,section (".u_boot_cmd"))) = {menu, 3, 0, do_menu, "menu - display a menu, to select the items to do something\n", " - display a menu, to
select the items to do something"}
实质上就是用U_BOOT_CMD宏定义的信息构造了一个cmd_tbl_t类型的结构体。编译器将该结构体放在“u_boot_cmd”段,执行命令时就可以在“u_boot_cmd”段查找到对应的cmd_tbl_t类型结构体。
(3) 实现命令的函数
在cmd_menu.c中添加“menu”命令的响应函数的实现。具体的实现代码略:
int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
/* 实现代码略 */
}
(4) 将common/cmd_menu.c编译进u-boot.bin
在common/Makefile中加入如下代码:
COBJS-$( CONFIG_CMD_MENU) += cmd_menu.o
在include/configs/tx440.h加入如代码:
#define CONFIG_CMD_MENU
重新编译下载U-Boot就可以使用menu命令了
(5)menu命令执行的过程
在U-Boot中输入“menu”命令执行时,U-Boot接收输入的字符串“menu”,传递给run_command函数。run_command函数调用common/command.c中实现的find_cmd函数在__u_boot_cmd_start与__u_boot_cmd_end间查找命令,并返回menu命令的cmd_tbl_t结构。然后run_command函数使用返回的cmd_tbl_t结构中的函数指针调用menu命令的响应函数do_menu,从而完成了命令的执行。
3.10 其他
3.10.1支持Tab补全命令和上下键调用历史命令
1、include/configs/TX2440.h中添加代码#define CONFIG_CMDLINE_EDITING 支持上下键调用历史命令
#define CONFIG_AUTO_COMPLETE 支持Tab补全命令
2、再次编译,建议每次编译前都清除一下上次编译留下的中间代码:
#make distclean
#make tx2440_config
#make
3.10.2输出FLCK HCLK PCLK频率
在\arch\arm\lib\board.c中添加void print_cpuinfo(void) { unsigned long fclk,hclk,pclk,uclk; fclk=get_FCLK(); hclk=get_HCLK(); pclk=get_PCLK(); uclk=get_UCLK(); printf("FCLK=%ldMHz,HCLK=%ldMHz,PCLK=%ldMHz,UCLK=%ldMHz\n",fclk/1000000,hclk/1000000,pclk/1000000,uclk/1000000); }
相关文章推荐
- UBOOT-2010-03在S3C2440上的移植<三>------------自动识别启动模式Nand Or Nor
- UBOOT-2010-03在S3C2440上的移植<四>------------支持NANDFLASH
- u-boot-2010.06在TQ2440上的移植<4>--支持nandflash启动
- UBOOT-2010-03在S3C2440上的移植<二>------------硬件初始化
- Uboot移植之<一>------S3C2440平台搭建(支持Norflash和nand flash)
- UBOOT-2010-03在S3C2440上的移植<四>------------支持NANDFLASH<续>
- WiFi 移植记录及心得 <二>
- Linux虚拟文件系统(内核初始化<二>)
- Spring启动<二>——XmlWebApplicationContext
- UBOOT-2010-03在S3C2440上的移植<一>------------项目搭建
- <2012 12 05> FL2440开发板的U-boot-2010.09版本移植(四)Nor Flash启动支持
- linux启动流程导读(arm为例)<二>
- EBS Form开发 弹性域定义中的参数<二>
- 大数据处理之道 (htmlparser 过滤器<二>)
- 百度地图开发之实现运动轨迹 <二>
- Android学习笔记:<二>01.Activity初步
- 面试项目<二>_银行业务调度系统
- Ralink雷凌rt3070驱动的ARM平台移植问题<error: unknown field ‘private’ specified in initializer>
- 认识标签<二>
- JNI方法调用C++类库 <二>