修改uboot以支持4G以上的分区
接到任务需要:需要对itop4412的开发板给根目录分配4G以上的空间。因为uboot默认使不能分配4G以上分区的,因此需要修改源码。
祭出“百度大法”,找到了下面的博客:
博客地址:https://www.cnblogs.com/stay-foolish1984/p/10489625.html。
关键内容:
/common/Cmd_mmc_fdisk.c里面的 unsigned int calc_unit(unsigned int length,
SDInfo sdInfo),length定义的是unsigned int,是32bit的。但是调用的时候输入是unsigned long
long,是64bit的。block_offset = calc_unit((unsigned long
long)simple_strtoul(argv[3], NULL, 0)10241024,
sdInfo);所以这个过程中,丢了高位置信息,改掉这个bug,重新编译uboot。
总结一句话就是,数据的大小超出了数据的可允许范围,导致数据被截断。例如一个char类型的数据是8位 ,可表示255也就是十六进制的0xff。当表示257的时候,那么则为十六进制的0x101。但是char最多存储8位,因此高位的0x101中的1被丢弃(二进制的8位就是十六进制的2位)则只剩下了0x01 。也就是十进制的1。
如何快速计算一个数被截断后剩余多少呢?将这个数与它的数据类型所表示的最大的数+1取余即可。例如:char最大表示255,+1 是256。257%256取余的结果为1。
同理,32位的int类型的数X则是X %(2^32 + 1)
。
按照博客中的提示,将
unsigned int calc_unit(unsigned int length, SDInfo sdInfo),
改为
unsigned int calc_unit(unsigned long long length, SDInfo sdInfo),
重新编译uboot,烧录后重新进行分区。5G分区成功,事情竟然如此顺利?然后修改system.img镜像的分区大小也为5G。开始烧录。
然而事情并不简单
虽然分区成功了,但是烧录的时候依然报错:
Invalid Volume Size! Image is bigger than partition size
不应该啊,分区已经成功修改了,莫非是镜像的分区设置的太大了?于是修改镜像大小为4.9G重新烧录,依然如此。此时懒得再去百度了,自己解决吧!
首先利用shell命令
find . -type f |xargs grep "Invalid Volume Size! Image is bigger than partition size"
查找这个提示出现在哪个文件中,我们去看他具体的代码内容。据此,我们找到了common/decompress_ext4.c
这个文件,打开文件中再搜索报错的字符串:
根据代码判断这里应该是用 分区的大小 / 文件的块大小 得到分区的block数, 再去对比 镜像的block总块数,如果分区的block总数小于镜像的block总数,说明分区不合法。Parti_size表示的分区的大小,我们看下这句代码所在的函数
parti_size
的类型也是unsigned int
,那传入的尺寸如果大于unsigned int
的数据范围一样会被截断,导致后面的判断结果错误。于是我们改为如下这样:
同时别忘了修改include/decompress_ext4.h
头文件中相应的函数声明。
再次编译,然后重新烧录。问题依旧!!!
莫非调用check_compress_ext
方法的时候传入的parti_size
已经是被截断的unsigned int
的类型了?我们再用上面查找错误提示的的方法去查找哪个文件调用了check_compress_ext
打开此文件并查找check_compress
传入的参数为ptn->length;那我们再看看ptn这个结构体是如何定义的。往上找,看看ptn是如何赋值的。
ptn是直接通过函数传递进来的,而且pth的结构体定义是struct fastboot_ptentry
我们再使用find配合grep的方法找到这个结构体是在include/fastboot.h
中定义的:
成员length的类型同样是unsigned int
。修改如下:
先编译uboot测试。问题依旧,果然不是那么简单就可以解决的。根据我的推测,uboot应该是先读取分区尺寸大小的信息,然后保存在fastboot_ptentry
结构体中,那么是不是有可能在把分区大小信息赋值到结构体中的length变量的时候,就已经被截断了呢?
看来有点棘手,夜已深,但是问题不解决哪能安心睡觉呢?简单洗漱一下裹上我的小被几继续。
接下来看看都有哪些文件调用了fastboot_ptentry
结构体,并给他赋值了吧。
还好,全部都在cmd_fastboot.c
中,而不是分散在多个文件中,这就方便很多了。
然后我挨个查看了使用了pth结构体的函数,然后发现了设置分区参数的函数
set_partition_table
但是观察代码发现,这个是根据环境变量来赋值分区信息的,而我们的分区信息是保存在了mmc里面。所以基本判断不是这个函数。
柳暗花明
线索中断了,莫非我的判断不对?
这个函数从环境变量读取分区信息的,那么调用这个函数的地方会不会也有从mmc读取的步骤呢?
果然,在调用这个函数的附近,发现了set_partition_table_sdmmc()
我们进入这个函数,原来也在cmd_fastboot.c
里面(而且就在set_partition_table
这个函数下面…)
一行一行的看代码,找到了希望:
count和后面的常量都是unsigned int
类型,这里就是一个比较容易出错的地方,虽然我们之前已经把length成员的变量修改为unsigned long long
,但是在c语言中,两个int类型的数据相乘,结果也会按照int类型保存。这样如果结果大于了int的范围,那么依然会被截断!!(起码我所使用的交叉编译器如此)。
如何避免呢?把其中一个变量类型强转为unsigned long long
即可,这样编译器会自动转换两个操作数都为unsigned long long
类型,结果自然也就是unsigned long long
喽。
编译,烧录。成功!!!
关机睡觉!!!
后记:
虽然在文中写的解决过程是一步步的进行推导,然而在实际的解决过程中并没有这么的逻辑清晰,也远比文中写的复杂很多。因为我不熟悉uboot(第一次编译),也不常烧录嵌入式系统,所以对此流程不甚了解。尝试不少错误的线索,或者同时在多个线索上推导,因此过程也是十分无聊和枯燥的。前后总共耗费了两个晚上才解决。
现在的过程仅仅是解决后的复盘,省去了很多的弯路。其实或许也有更简单的解决办法,例如单纯的注释掉判断部分的代码,这样是不是也能正常烧录呢?我并没有这么做,因为这样出现问题就忽略问题的办法个人不推荐。
那么如果一开始就从uboot的执行流程顺序判断,是不是能更快找到问题所在呢?或许吧,但是因为对uboot不熟悉,因此可能熟悉整个流程也需要消耗更多的时间呢?