Mini440之uboot移植之裁剪、分区与环境变量设置(五)
本节的代码都是在代码基础上修改的。
一、uboot修改默认环境变量
不知道你有没有留意到,我们之前介绍的u-boot串口输出信息有下面一行:
U-Boot 2016.05 (Jan 14 2022 - 22:22:01 +0800) CPUID: 32440001 FCLK: 400 MHz HCLK: 100 MHz PCLK: 50 MHz DRAM: 64 MiB WARNING: Caches not enabled Flash: 2 MiB NAND: 256 MiB *** Warning - bad CRC, using default environment In: serial Out: serial Err: serial Net: dm9000 ### main_loop: bootcmd="" SMDK2440 #
可以看出,读出的是坏的CRC,使用默认的环境变量,我们分析一下输出这个的原因。
1.1 分析环境变量初始化
在我们介绍到board_init_r()函数会进行环境变量的的初始化。具体在initr_env()函数执行过程中,该函数位于common/board_r.c文件,去掉无用代码如下:
static int initr_env(void) { /* initialize environment */ if (should_load_env()) env_relocate(); else set_default_env(NULL); /* Initialize from environment */ load_addr = getenv_ulong("loadaddr", 16, load_addr); return 0; }
其中should_load_env去除无用代码,定义如下:
static int should_load_env(void) { return 1; }
然后执行env_relocate();
1.2 env_relocate(common/env_common.c)
env_relocate函数去除无用代码:
void env_relocate(void) { if (gd->env_valid == 0) { bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM); set_default_env("!bad CRC"); } else { env_relocate_spec(); } }
由于在env_init函数过程中设置了gd->env_valid=0,所以执行set_default_env("!bad CRC");
1.3 set_default_env(common/env_common.c)
set_default_env函数定义:
void set_default_env(const char *s) { int flags = 0; if (sizeof(default_environment) > ENV_SIZE) { puts("*** Error - default environment is too large\n\n"); return; } if (s) { if (*s == '!') { printf("*** Warning - %s, " "using default environment\n\n", s + 1); } else { flags = H_INTERACTIVE; puts(s); } } else { puts("Using default environment\n\n"); } if (himport_r(&env_htab, (char *)default_environment, sizeof(default_environment), '\0', flags, 0, 0, NULL) == 0) error("Environment import failed: errno = %d\n", errno); gd->flags |= GD_FLG_ENV_READY; }
因为s="!bad CRC",所以串口会输出警告信息。
我们之前介绍过默认的环境变量都是保存在default_environment这个字符数组中的,环境变量之间使用\0分隔,其内容大致如下:
const uchar default_environment[] = { "bootdelay=" __stringify(CONFIG_BOOTDELAY) "\0" "baudrate=" __stringify(CONFIG_BAUDRATE) "\0" "ipaddr=" __stringify(CONFIG_IPADDR) "\0" "serverip=" __stringify(CONFIG_SERVERIP) "\0" "netmask=" __stringify(CONFIG_NETMASK) "\0" };
这里我们以其中的baudrate为例,CONFIG_BAUDRATE在smdk2440.h(include/configs/smdk2440.h)中定义:
#define CONFIG_BAUDRATE 115200
此外还有一个比较重要的宏配置,是用来启动linux内核的,这里我们没有配置,但是有必要介绍一下,CONFIG_BOOTARGS,搜索 bootargs 字样,可以在其它单板中看到有类似这样的定义:
#define CONFIG_BOOTARGS "root=/dev/nfs console=ttySAC0,115200 init=/linuxrc"
- root 指定文件系统位置;
- init 指定内核启动后执行的第一个应用程序;
- console 指定使用哪个终端,这里的 ttySAC0 指的就是串口0;
其它宏也是这样,比如我们熟悉的有:
- CONFIG_BOOTCOMMAND,用来启动内核的命令;
- CONFIG_BOOTDELAY:uboot启动倒计时,默认值为5s,只有设置了bootcmd,它才有用;
- CONFIG_BAUDRATE:波特率,默认为115200;
- CONFIG_IPADDR:IP 地址;
- CONFIG_SERVERIP:服务器IP地址;
- CONFIG_NETMASK:子网掩码;
- CONFIG_GATEWAYIP:网关;
仿照其它单板在 include/configs/smdk2440.h中添加相应的宏来设置默认环境变量,代码如下(红色为添加代码):
/* autoboot */ #define CONFIG_BOOTDELAY 5 #define CONFIG_BOOT_RETRY_TIME -1 #define CONFIG_RESET_TO_RETRY #define CONFIG_ZERO_BOOTDELAY_CHECK #define CONFIG_NETMASK 255.255.255.0 #define CONFIG_IPADDR 192.168.0.188 #define CONFIG_SERVERIP 192.168.0.200 #define CONFIG_ETHADDR 08:00:3e:26:0a:5b
这个其实我们在u-boot支持DM9000网卡的时候已经介绍过了。 这样当uboot启动时就会默认使用上面的环境变量,不需要每次启动都去修改变量了。
1.4 设置环境变量保存位置
在之前的打印信息中虽然会出现,"bad CRC,using default environment",后面我们通过手动设置环境变量:
set ethaddr 00:0c:29:d3:fe:1d
也是可以执行我们想要的操作,但是如果使用 save 命令将其保存到 flash 中,保存地址设置有误的话,会保存失败。并且有可能会破坏flash原有的内容,导致uboot启动失败。
所以,首先我们先找到 save 在代码中的位置,在 u-boot 上,输入 save -help 命令,打印如下:
saveenv - save environment variables to persistent storage
useage:
saveenv
在u-boot代码搜索saveenv,
grep "saveenv" * -nR
发现很多文件中都有对 saveenv 函数的定义,由于我们只需要把环境变量保存到 FALSH中,所以只需要关注与 FLASH有关的文件,如上图所示:
- common/env_nand.c 文件:将环境变量保存到 NAND 中;
- common/env_flash.c 文件:将环境变量保存到 NOR中;
打开 common 目录下的 Makefile 文件,查看编译这两个文件所依赖的宏:
可以看出:
- 编译 env_flash.c 文件需要 CONFIG_ENV_IS_IN_FLASH 宏;
- 编译 env_nand.c 文件需要 CONFIG_ENV_IS_IN_NAND 宏;
在 smdk2440.h 文件中查找这两个宏有无定义:
发现只有与NOR 相关宏的定义,没有与 NAND 相关宏的定义。
1、NAND启动
需要注意的是如果是NAND启动,不支持 NOR fFLASH操作,NAND启动的话只能将将环境变量保存在NAND FLASH中。
参照其它单板对NAND 宏的定义,修改代码:
/* 保存环境变量到NOR FLASH */ #if 0 #define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x070000) #define CONFIG_ENV_IS_IN_FLASH #define CONFIG_ENV_SIZE 0x10000 #else /* 保存环境变量到NAND FLASH */ #define CONFIG_ENV_IS_IN_NAND /* U-Boot env in NAND Flash */ #define CONFIG_ENV_SIZE 0x20000 //128kb #define CONFIG_ENV_OFFSET 0x80000 //给uboot预留512kb #endif
其中还有一个 CONFIG_ENV_RANGE 宏,这个宏可以不定义,如果不定义的话,在env_nand.c 中,会把它设置为 CONFIG_ENV_SIZE。
重新编译,烧写uboot,就可以使用 save 命令保存环境变量了。
2、NOR启动
如果是NOR启动,修改CONFIG_ENV_ADDR:
/* 保存环境变量到NOR FLASH */ #if 1 #define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x080000) //给uboot预留512kb #define CONFIG_ENV_IS_IN_FLASH #define CONFIG_ENV_SIZE 0x10000 #else /* 保存环境变量到NAND FLASH */ #define CONFIG_ENV_IS_IN_NAND /* U-Boot env in NAND Flash */ #define CONFIG_ENV_SIZE 0x20000 //128kb #define CONFIG_ENV_OFFSET 0x80000 //给uboot预留512kb #endif
重新编译,烧写uboot,设置环境变量,并保存:
U-Boot 2016.05 (Jan 16 2022 - 13:53:11 +0800) CPUID: 32440001 FCLK: 400 MHz HCLK: 100 MHz PCLK: 50 MHz DRAM: 64 MiB WARNING: Caches not enabled Flash: 2 MiB NAND: 256 MiB In: serial Out: serial Err: serial Net: dm9000 SMDK2440 # set ethaddr 00:0c:29:d3:fe:1d SMDK2440 # save Saving Environment to Flash... Un-Protected 1 sectors Erasing Flash... . done Erased 1 sectors Writing to Flash... 9....8....7....6....5....4....3....2....1....done
二、设置 mtdparts 分区
如果想要查看 uboot,kernel等的分区,在uboot中输入mtdparts命令即可,该命令用来查看NAND FLASH分区情况。运行u-boot,输入该命令:
SMDK2440 # mtdparts
mtdids not defined, no default present
发现不支持该命令,修改代码使u-boot支持mtdparts命令,命令相关的都在cmd目录下定义。
打开cmd目录下的 Makefile ,发现要想编译mtdparts.c 文件,需定义CONFIG_CMD_MTDPARTS 宏,如下图所示:
然后我们do_mtdparts函数:
static int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { if (argc == 2) { if (strcmp(argv[1], "default") == 0) { setenv("mtdids", (char *)mtdids_default); setenv("mtdparts", (char *)mtdparts_default); setenv("partition", NULL); mtdparts_init(); return 0; } else if (strcmp(argv[1], "delall") == 0) { /* this may be the first run, initialize lists if needed */ mtdparts_init(); setenv("mtdparts", NULL); /* mtd_devices_init() calls current_save() */ return mtd_devices_init(); } } /* make sure we are in sync with env variables */ if (mtdparts_init() != 0) return 1; if (argc == 1) { list_partitions(); return 0; } ... }
当执行mtdparts命令时,第一个参数为命令mtdparts命令对应的结构体,第二个参数为命令标志,第三个参数为命令参数个数,这里没有传入额外参数,长度也就是1,即命令本身。
该函数会调用mtdparts_init函数。
2.1 mtdparts_init(cmd/mtdparts.c)
/** * Parse and initialize global mtdids mapping and create global * device/partition list. * * @return 0 on success, 1 otherwise */ int mtdparts_init(void) { ... /* get variables */ ids = getenv("mtdids"); parts = getenv("mtdparts"); current_partition = getenv("partition"); /* if mtdids variable is empty try to use defaults */ if (!ids) { if (mtdids_default) { debug("mtdids variable not defined, using default\n"); ids = mtdids_default; setenv("mtdids", (char *)ids); } else { printf("mtdids not defined, no default present\n"); return 1; } } ... }
在默认环境变量中没有定义mtdids环境变量,ids为0,执行if (mtdids_default)语句,在同文件有如下定义:
/* default values for mtdids and mtdparts variables */ #if defined(MTDIDS_DEFAULT) static const char *const mtdids_default = MTDIDS_DEFAULT; #else static const char *const mtdids_default = NULL; #endif
2.2 配置分区宏
我们的单板头文件(include/configs/mdk2440.h)默认没有定义MTDIDS_DEFAULT宏,所以执行mtdparts时输出错误信息,查看其它单板头文件是如何定义的,修改smdk2440.h里宏的定义:
/* * File system */ #define CONFIG_CMD_UBI #define CONFIG_CMD_UBIFS #define CONFIG_CMD_MTDPARTS #define CONFIG_MTD_DEVICE #define CONFIG_MTD_PARTITIONS #define CONFIG_YAFFS2 #define CONFIG_RBTREE /* mtdparts command line support */ #define MTDIDS_DEFAULT "nand0=Mini2440-0" /* default mtd partition table */ #define MTDPARTS_DEFAULT "mtdparts=Mini2440-0:512k(u-boot)," \ "128k(params)," \ "4m(kernel)," \ "-(rootfs);" \
这样当在u-boot命令行执行“mtdparts default”命令时,do_mtdparts会根据上面的MTDPARTS_DEFAULT宏保存的mtd分区信息,来将NAND分区。
然后再次使用mtdparts查看分区信息,如下:
SMDK2440 # mtdparts default SMDK2440 # mtdparts device nand00>, # parts = 4 #: name size offset mask_flags 0: u-boot 0x00080000 0x00000000 0 1: params 0x00020000 0x00080000 0 2: kernel 0x00400000 0x000a0000 0 3: rootfs 0x0fb60000 0x004a0000 0 active partition: nand0,0 - (u-boot) 0x00080000 @ 0x00000000 defaults: mtdids : nand0=Mini2440-0 mtdparts: mtdparts=Mini2440-0:512k(u-boot),128k(params),4m(kernel),-(rootfs);
256MB大小的NAND被分成四个分区:
- 0x00000000~0x00080000:512kb存放u-boot;
- 0x00080000~0x000A0000:128kb存放linux内核启动参数;
- 0x000A0000~0X004A0000:4MB存放linux内核;
- 0x004A0000~0x10000000:剩下空间存放根文件系统;
2.3 u-boot启动自动分区
下面修改代码,让u-boot启动时自动设置分区(执行“mtdparts default”命令),在u-boot进入main_loop()死循环之前添加执行命令代码(run_command("mtdparts default", 0);),修改run_main_loop()函数如下(common/board_r.c文件中):
static int run_main_loop(void) { #ifdef CONFIG_SANDBOX sandbox_main_loop_init(); #endif run_command("mtdparts default", 0); /* main_loop() can return to retry autoboot, if so just run it again */ for (;;) main_loop(); return 0; }
这样,u-boot每次启动时都会执行一次mtdparts default 命令,使它根据默认参数来自动分区,这样mtdparts就设置好了。
编译,烧写,在uboot中输入mtdparts,打印如下:
SMDK2440 # mtdparts device nand00>, # parts = 4 #: name size offset mask_flags 0: u-boot 0x00080000 0x00000000 0 1: params 0x00020000 0x00080000 0 2: kernel 0x00400000 0x000a0000 0 3: rootfs 0x0fb60000 0x004a0000 0 active partition: nand0,0 - (u-boot) 0x00080000 @ 0x00000000 defaults: mtdids : nand0=Mini2440-0 mtdparts: mtdparts=Mini2440-0:512k(u-boot),128k(params),4m(kernel),-(rootfs);
三、裁切u-boot
到这一步,我们编译的u-boot 大概有482kb,这其中有很多无用的代码,所以接下来我们需要对 uboot 进行裁剪。
u-boot 很多文件都是基于 Makefile 里的宏来进行编译的,而这些宏的定义基本上都在 include/configs/s3c2440.h 中,所以我们需要修改 s3c2440.h 文件,去掉不需要的宏。
3.1 裁切USB
#if 0 #define CONFIG_USB_OHCI #define CONFIG_USB_OHCI_S3C24XX #define CONFIG_USB_KEYBOARD #define CONFIG_USB_STORAGE #define CONFIG_DOS_PARTITION #endif
3.2 裁切RTC
/************************************************************ * RTC ************************************************************/ #if 0 #define CONFIG_RTC_S3C24X0 #endif
3.3 裁切BOOTP选项
/* * BOOTP options */ #if 0 #define CONFIG_BOOTP_BOOTFILESIZE #define CONFIG_BOOTP_BOOTPATH #define CONFIG_BOOTP_GATEWAY #define CONFIG_BOOTP_HOSTNAME #endif
3.4 裁切部分不需要的命令行
/* * Command line configuration. */ #if 0 #define CONFIG_CMD_BSP #define CONFIG_CMD_DATE #endif #define CONFIG_CMD_NAND #define CONFIG_CMD_REGINFO
3.5 裁切文件系统
/* * File system */ #if 0 #define CONFIG_CMD_UBI #define CONFIG_CMD_UBIFS #define CONFIG_YAFFS2 #define CONFIG_RBTREE #endif #define CONFIG_CMD_MTDPARTS #define CONFIG_MTD_DEVICE #define CONFIG_MTD_PARTITIONS
3.6 编译
编译,打印如下错误:
打开common目录下的Makefile,定位到usb关键字,如下:
可以看出编译 usb.c 需要宏 CONFIG_CMD_USB 的支持,在 configs/smdk2440_defconfig找到该宏,将其屏蔽,时屏蔽文件系统相关的宏:
CONFIG_ARM=y CONFIG_TARGET_SMDK2440=y CONFIG_HUSH_PARSER=y CONFIG_SYS_PROMPT="SMDK2440 # " # CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_DHCP=y CONFIG_CMD_PING=y CONFIG_CMD_CACHE=y #CONFIG_CMD_EXT2=y #CONFIG_CMD_FAT=y
再次编译:
make clean make distclean make smdk2440_config make ARCH=arm CROSS_COMPILE=arm-linux- V=1
此时编译完成u-boot大概为246kb。
3.7 下载运行
我们将u-boot下载到NOR FALSH中运行,以NOR方式启动,并在命令行输入print,查看所有环境变量:
SMDK2440 # print baudrate=115200 bootdelay=5 ethact=dm9000 ethaddr=00:0c:29:d3:fe:1d ipaddr=192.168.0.188 mtddevname=u-boot mtddevnum=0 mtdids=nand0=Mini2440-0 mtdparts=mtdparts=Mini2440-0:512k(u-boot),128k(params),4m(kernel),-(rootfs); netmask=255.255.255.0 partition=nand0,0 serverip=192.168.0.200 stderr=serial stdin=serial stdout=serial Environment size: 355/65532 bytes
我们再次向NOR FALSH烧入Mini2440商家原有的Superboot2440.bin到,从NOR启动,我们搭配MiniTools将u-boot.bin代码下载到NAND运行:
需要注意的是使用MiniTools下载程序,好像文件大于400kb,MiniTools就无法下载成功。但是这里我们的u-boot裁切到246kb。可以下载成功。
以NAND方式启动,并在命令行输入print,查看所有环境变量:
SMDK2440 # print baudrate=115200 bootdelay=5 ethact=dm9000 ethaddr=08:00:3e:26:0a:5b ipaddr=192.168.0.188 mtddevname=u-boot mtddevnum=0 mtdids=nand0=Mini2440-0 mtdparts=mtdparts=Mini2440-0:512k(u-boot),128k(params),4m(kernel),-(rootfs); netmask=255.255.255.0 partition=nand0,0 serverip=192.168.0.200 stderr=serial stdin=serial stdout=serial Environment size: 355/65532 bytes
四、代码下载
Young / s3c2440_project[u-boot-2016.05-crop]
参考文章
[1]移植u-boot-2012.04.01到JZ2440(六: 修改源码之裁剪uboot、设置分区与环境变量)
[2]