前言
环境介绍:
1.编译环境
Ubuntu 18.04.5 LTS
2.SDK
orangepi Linux 5.4 SDK
3.uboot
v2020.04
一、现象
根据《OrangePi_PCPlus_H3_用户手册_v3.1.pdf》5. Linux 5.4 SDK使用说明编译好镜像文件,将Linux 镜像烧写到 tf卡,启动板卡 ,uboot按空格键停留再uboot,保存环境变量,打印如下:
U-Boot 2020.04-orangepi (Jan 02 2021 - 19:58:40 +0800) Allwinner Technology
CPU: Allwinner H3 (SUN8I 1680)
Model: Xunlong Orange Pi PC Plus
DRAM: 1 GiB
MMC: Device 'mmc@1c11000': seq 1 is in use by 'mmc@1c10000'
mmc@1c0f000: 0, mmc@1c10000: 2, mmc@1c11000: 1
Loading Environment from FAT... Unable to use mmc 1:1... In: serial
Out: serial
Err: serial
Net: phy interface0
eth0: ethernet@1c30000
230456 bytes read in 14 ms (15.7 MiB/s)
starting USB...
Bus usb@1c1a000: USB EHCI 1.00
Bus usb@1c1a400: USB OHCI 1.0
Bus usb@1c1b000: USB EHCI 1.00
Bus usb@1c1b400: USB OHCI 1.0
Bus usb@1c1c000: USB EHCI 1.00
Bus usb@1c1c400: USB OHCI 1.0
Bus usb@1c1d000: USB EHCI 1.00
Bus usb@1c1d400: USB OHCI 1.0
scanning bus usb@1c1a000 for devices... 1 USB Device(s) found
scanning bus usb@1c1a400 for devices... 1 USB Device(s) found
scanning bus usb@1c1b000 for devices... 1 USB Device(s) found
scanning bus usb@1c1b400 for devices... 1 USB Device(s) found
scanning bus usb@1c1c000 for devices... 1 USB Device(s) found
scanning bus usb@1c1c400 for devices... 1 USB Device(s) found
scanning bus usb@1c1d000 for devices... 1 USB Device(s) found
scanning bus usb@1c1d400 for devices... 1 USB Device(s) found
scanning usb for storage devices... 0 Storage Device(s) found
Autoboot in 1 seconds, press <Space> to stop
=>
=> saveenv
Saving Environment to FAT... Unable to use mmc 1:1... Failed (1)
无法保存环境变量,并且在前面加载环境变量的时候也出现了同样的打印信息,无法使用mmc 1:1
Unable to use mmc 1:1... Failed (1)
二、问题定位
直接在uboot搜索“Unable to use”
检索发现打印信息出自
env/fat.c
总共有两个函数有该打印信息
2.1 env_fat_load(void)
static int env_fat_load(void)
{
ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
struct blk_desc *dev_desc = NULL;
disk_partition_t info;
int dev, part;
int err;
#ifdef CONFIG_MMC
if (!strcmp(CONFIG_ENV_FAT_INTERFACE, "mmc"))
mmc_initialize(NULL);
#endif
part = blk_get_device_part_str(CONFIG_ENV_FAT_INTERFACE,
CONFIG_ENV_FAT_DEVICE_AND_PART,
&dev_desc, &info, 1);
if (part < 0)
goto err_env_relocate;
dev = dev_desc->devnum;
if (fat_set_blk_dev(dev_desc, &info) != 0) {
/*
* This printf is embedded in the messages from env_save that
* will calling it. The missing \n is intentional.
*/
printf("Unable to use %s %d:%d... ",
CONFIG_ENV_FAT_INTERFACE, dev, part);
goto err_env_relocate;
}
err = file_fat_read(CONFIG_ENV_FAT_FILE, buf, CONFIG_ENV_SIZE);
if (err == -1) {
/*
* This printf is embedded in the messages from env_save that
* will calling it. The missing \n is intentional.
*/
printf("Unable to read \"%s\" from %s%d:%d... ",
CONFIG_ENV_FAT_FILE, CONFIG_ENV_FAT_INTERFACE, dev, part);
goto err_env_relocate;
}
return env_import(buf, 1);
err_env_relocate:
env_set_default(NULL, 0);
return -EIO;
}
2.2 env_fat_save(void)
static int env_fat_save(void)
{
env_t __aligned(ARCH_DMA_MINALIGN) env_new;
struct blk_desc *dev_desc = NULL;
disk_partition_t info;
int dev, part;
int err;
loff_t size;
err = env_export(&env_new);
if (err)
return err;
part = blk_get_device_part_str(CONFIG_ENV_FAT_INTERFACE,
CONFIG_ENV_FAT_DEVICE_AND_PART,
&dev_desc, &info, 1);
if (part < 0)
return 1;
dev = dev_desc->devnum;
if (fat_set_blk_dev(dev_desc, &info) != 0) {
/*
* This printf is embedded in the messages from env_save that
* will calling it. The missing \n is intentional.
*/
printf("Unable to use %s %d:%d... ",
CONFIG_ENV_FAT_INTERFACE, dev, part);
return 1;
}
err = file_fat_write(CONFIG_ENV_FAT_FILE, (void *)&env_new, 0, sizeof(env_t),
&size);
if (err == -1) {
/*
* This printf is embedded in the messages from env_save that
* will calling it. The missing \n is intentional.
*/
printf("Unable to write \"%s\" from %s%d:%d... ",
CONFIG_ENV_FAT_FILE, CONFIG_ENV_FAT_INTERFACE, dev, part);
return 1;
}
return 0;
}
从函数名判断
env_fat_save是保存环境变量时调用
env_fat_load是uboot一开始加载环境变量时调用
但都集中与下面这函数
if (fat_set_blk_dev(dev_desc, &info) != 0) {
/*
* This printf is embedded in the messages from env_save that
* will calling it. The missing \n is intentional.
*/
printf("Unable to use %s %d:%d... ",
CONFIG_ENV_FAT_INTERFACE, dev, part);
return 1;
}
其中fat_set_blk_dev定义在
fs/fat/fat.c
int fat_set_blk_dev(struct blk_desc *dev_desc, disk_partition_t *info)
{
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
cur_dev = dev_desc;
cur_part_info = *info;
/* Make sure it has a valid FAT header */
if (disk_read(0, 1, buffer) != 1) {
cur_dev = NULL;
return -1;
}
/* Check if it's actually a DOS volume */
if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
cur_dev = NULL;
return -1;
}
/* Check for FAT12/FAT16/FAT32 filesystem */
if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
return 0;
if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
return 0;
cur_dev = NULL;
return -1;
}
2.3 疑点1
该函数作用是检查tf卡分区以及文件类型的,ps具体不是很懂哈~
从打印情况来看,估计采用推荐的格式化工具格式化tf卡出了问题。
2.4 疑点2
uboot打印的"mmc 1:1…"到底啥意思?
打印的源码如下:
printf("Unable to use %s %d:%d... ",
CONFIG_ENV_FAT_INTERFACE, dev, part);
CONFIG_ENV_FAT_INTERFACE 为mmc 定义如下,可以理解为环境变量的接口,也就是环境变量的存储介质。
include/generated/autoconf.h
#define CONFIG_ENV_FAT_INTERFACE "mmc"
dev则为h3 mmc的设备号。
h3总共有三个mmc接口,如下是《Allwinner_H3_Datasheet_v1.2.pdf》找到的。
与uboot一开始打印的信息可以对应上
mmc@1c0f000: 0, mmc@1c10000: 2, mmc@1c11000: 1
其实uboot打印的序号、地址与规格书是有出入的,这里不做深入研究,与我们现在的问题解决关系不大。ps之前就是以为有关系,花了好大力气研究这里,白白浪费时间 -_-!
orangepi pc plus mmc接口硬件设计如下
sd/mmc0→tf卡
sd/mmc1→wifi
sd/mmc2→emmc
part是对应的存储介质内部的分区。
由上可知前面的打印意思是mmc 1设备的分区1无法使用。
三、问题解决
我们先从上面的疑点1入手~
3.1 解疑点1
如下是采用可视化分区工具gparted查看装有系统tf卡的分区情况
这工具挺好用的,适合小白可通过sudo apt install gparted安装。
tf卡只做了一个分区sdb1,并且格式化为ext4,分区1里面存放的就是启动系统。
其中boot里面存放的就是linux kernel编译好的镜像,还有启动参数boot.scr。
那么有人会问uboot存放在哪呢?
其实是是放在tf卡前面未分配的4MB里面,这里不细说。
额。。。以上有点扯远了。
其实问题就出在分区1格式化为ext4!
因为前面uboot的fat_set_blk_dev函数会检查分区格式是否为FAT/FAT32。虽然h3启动对分区格式无要求,能正常加载到想要的数据就行,但不正确的格式影响uboot环境变量加载、保存。
解决方法查看我另外一篇文章。
3.2 解疑点2
以上就算格式化正确了,还是无法解决环境变量问题。
因这个版本uboot存在问题,默认是从mmc 1分区1加载、保存环境变量。
根据前面的推断mmc 1并非tf卡,在加上打印信息与Datasheet有出入,做如下推断:
打印的mmc 1 对应地址是mmc@1c11000,而该地址在Datasheet对应的是mmc2,实际硬件接的emmc。也就是不管插不插卡,默认都是从emmc加载、保存环境变量。
要做的就是修改从tf卡加载、保存环境变量。
下面这个函数的CONFIG_ENV_FAT_DEVICE_AND_PART,就是导致默认从mmc1加载环境变量的。
part = blk_get_device_part_str(CONFIG_ENV_FAT_INTERFACE,
CONFIG_ENV_FAT_DEVICE_AND_PART,
&dev_desc, &info, 1);
该宏定义在:include/generated/autoconf.h
#define CONFIG_ENV_FAT_DEVICE_AND_PART "1:auto"
是env/Kconfig通过MMC_SUNXI_SLOT_EXTRA的不同值自动生成的。
config ENV_FAT_DEVICE_AND_PART
string "Device and partition for where to store the environemt in FAT"
depends on ENV_IS_IN_FAT
default "0:1" if TI_COMMON_CMD_OPTIONS
default "0:auto" if ARCH_ZYNQMP
default "0:auto" if ARCH_SUNXI && MMC_SUNXI_SLOT_EXTRA = -1
default "1:auto" if ARCH_SUNXI && MMC_SUNXI_SLOT_EXTRA != -1
default "0" if ARCH_AT91
而MMC_SUNXI_SLOT_EXTRA则定义在configs/orangepi_pc_plus_defconfig
CONFIG_MMC_SUNXI_SLOT_EXTRA=2
这定义估计是单板支持2个启动mmc设备。
查看orangepi pc(无emmc)的配置文件orangepi_pc_defconfig,则没有该参数。采用该配置文件编译之后CONFIG_ENV_FAT_DEVICE_AND_PART如下:
#define CONFIG_ENV_FAT_DEVICE_AND_PART "0:auto"
试着把orangepi_pc_plus_defconfig的CONFIG_MMC_SUNXI_SLOT_EXTRA屏蔽掉,
编译之后值同上。
最终效果见这两篇博文
香橙派orangepi pc plus h3 启动tf卡制作