前言

  本文所用的uboot代码为迅为官方提供,开发板是迅为iTOP-4412开发板。本文如有错误,欢迎指正。

  首先,我们确定一下系统启动的流程:首先启动uboot,uboot启动内核并挂载rootfs(根文件系统),内核启动完成且rootfs工作完成后,挂载emmc上的文件系统,操作系统正式开始工作。(读者要弄懂根文件系统和普通文件系统的区别与联系,网上资料很多,本文不作赘述。)

  本文实现的双系统引导,都是基于Linux的,即两个系统使用同一个内核、同一个根文件系统,只是emmc上的文件系统有所不同。第一个系统是一个最小Linux系统,第二个系统是一个带Qt/E的Linux系统。uboot启动后会从mmcblk0p4分区中读取一定长度的字符串,若字符串是“qte”,则启动带Qt/E的Linux系统;若字符串是“lin”,则启动最小Linux系统。

1.分区信息

  

  上图是emmc的分区信息。可以看到,分成了两个部分“Raw区域”和“主要分区”。Raw区域中我们主要关注bootloader、kernel、ramdisk这三个分区。ramdisk中存放根文件系统(rootfs),kernel中存放内核,bootloader中存放uboot。在主要分区中,mmcblk0p1指的是emmc的第一个分区,mmcblk0p2指的是emmc的第二个分区,mmcblk0p3指的是emmc的第三个分区,mmcblk0p4指的是emmc的第四个分区。mmcblk0指emmc,mmcblk1指SD卡。

  (1) 打开开发板和串口终端,摁回车进入uboot模式,在串口终端输入命令“fdisk -p mmc”,可以看到开发板emmc的“主要分区”信息,如下图所示。

  

  mmcblk0p1占12536MB,mmcblk0p2占1024MB,mmcblk0p3占1024MB,mmcblk0p4占300MB。

  (2) 在串口终端输入命令“fastboot”,可以看到“Raw区域”和“主要分区”的详细信息,如下图所示。

  

  其中,“bootloader”、“kernel”、“ramdisk”、“Recovery”分别对应“Raw区域”中的四个分区,“system”、“userdata”、“cache”、“fat”分别对应“主要分区”中的mmcblk0p2、mmcblk0p3、mmcblk0p4、mmcblk0p1四个分区。“system”中存放的就是操作系统的文件系统,在默认情况下,uboot引导起内核,内核启动完成后,会挂载位于“system”中的文件系统。要实现双系统引导,我们可以把第一个系统存放在“system”分区中,把第二个系统存放在“userdata”分区中,在启动时,让内核在两个分区的文件系统中进行选择。

  (3) 在串口终端输入命令“printenv”,可以看到环境变量信息,如下图所示。

  

  这里我们主要分析“bootcmd=movi read kernel 40008000;movi read rootfs 40df0000 100000;bootm 40008000 40df0000”这个语句,该语句的作用是把“movi read kernel 40008000;movi read rootfs 40df0000 100000;bootm 40008000 40df0000”赋给bootcmd变量。

  而bootcmd变量的值就是uboot启动后要执行的命令:先把内核从kernel分区中读取到内存的0x40008000处;再从ramdisk分区中(此处的rootfs分区实际就是指ramdisk分区,因为uboot判断分区时只判断首字母,所以即使写rootfs,仍然会导向ramdisk分区,这里写成rootfs是为了方便用户理解)读取0x100000个字节到内存的0x40df0000处;最后使用bootm命令,启动(挂载)已经读取到内存中的内核和根文件系统。

  在正常情况下,环境变量中还应有一个bootargs变量,bootargs的值就是uboot要传递给内核的参数,但是在上图的环境变量信息中并没有发现它,所以我们猜测迅为给的uboot源码中并没有给bootargs变量赋值。

2.uboot源码分析

  (1) 在uboot源码的“iTop4412_uboot/include/movi.h”文件中可以看到Raw区域的信息,如下。  

#define MAGIC_NUMBER_MOVI    (0x24564236)

#define SS_SIZE            (16 * 1024)

#define eFUSE_SIZE        (1 * 512)    // 512 Byte eFuse, 512 Byte reserved

#define MOVI_BLKSIZE        (1<<9)
//mj defined
#define FWBL1_SIZE (8* 1024) //IROM BL1 SIZE 8KB
#define BL2_SIZE (16 * 1024)//uboot BL2 16KB /* partition information */
#define PART_SIZE_UBOOT (495 * 1024)
#define PART_SIZE_KERNEL (6 * 1024 * 1024) #define PART_SIZE_ROOTFS (2 * 1024 * 1024)// 2M
#define RAW_AREA_SIZE (16 * 1024 * 1024)// 16MB #define MOVI_RAW_BLKCNT (RAW_AREA_SIZE / MOVI_BLKSIZE)
#define MOVI_FWBL1_BLKCNT (FWBL1_SIZE / MOVI_BLKSIZE)
#define MOVI_BL2_BLKCNT (BL2_SIZE / MOVI_BLKSIZE)
#define MOVI_ENV_BLKCNT (CONFIG_ENV_SIZE / MOVI_BLKSIZE)
#define MOVI_UBOOT_BLKCNT (PART_SIZE_UBOOT / MOVI_BLKSIZE)
#define MOVI_ZIMAGE_BLKCNT (PART_SIZE_KERNEL / MOVI_BLKSIZE)
#define ENV_START_BLOCK (544*1024)/MOVI_BLKSIZE #define MOVI_UBOOT_POS ((eFUSE_SIZE / MOVI_BLKSIZE) + MOVI_FWBL1_BLKCNT + MOVI_BL2_BLKCNT) #define MOVI_ROOTFS_BLKCNT (PART_SIZE_ROOTFS / MOVI_BLKSIZE)

  Raw区域总大小为16MB,包含了BL1、BL2、环境变量、内核、rootfs、uboot等信息。我们主要关注kernel、rootfs、uboot这三部分。内核存放在kernel分区中,根文件系统(rootfs)存放在ramdisk分区中、uboot存放在bootloader分区中。

  (2) 而对“Raw区域”和“主要分区”的操作,则在“iTop4412_uboot/common/cmd_fastboot.c”文件的“set_partition_table_sdmmc”函数中,如下(请看注释)。   

static int set_partition_table_sdmmc()
{
int start, count;
unsigned char pid; pcount = ; #if defined(CONFIG_FUSED)
/* FW BL1 for fused chip */
strcpy(ptable[pcount].name, "fwbl1");
ptable[pcount].start = ;
ptable[pcount].length = ;
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MOVI_CMD;
pcount++;
#endif /* Bootloader */
strcpy(ptable[pcount].name, "bootloader"); //Raw区域中的bootloader分区,存放uboot
ptable[pcount].start = ;
ptable[pcount].length = ;
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MOVI_CMD;
pcount++; /* Kernel */
strcpy(ptable[pcount].name, "kernel"); //Raw区域中的kernel分区,存放内核
ptable[pcount].start = ;
ptable[pcount].length = ;
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MOVI_CMD;
pcount++; /* Ramdisk */
strcpy(ptable[pcount].name, "ramdisk"); //Raw区域中的ramdisk分区,存放rootfs(根文件系统)
ptable[pcount].start = ;
ptable[pcount].length = 0x300000;
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MOVI_CMD;
pcount++; /* Recovery*/
#ifdef CONFIG_RECOVERY //mj
strcpy(ptable[pcount].name, "Recovery"); //Raw区域中的Recovery分区
ptable[pcount].start = ;
ptable[pcount].length = 0x600000; //6MB
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MOVI_CMD;
pcount++; /* System */
get_mmc_part_info((dev_number_write == )?"":"", , &start, &count, &pid); //主要分区中的mmcblk0p2分区,存放操作系统的文件系统
if (pid != 0x83)
goto part_type_error;
strcpy(ptable[pcount].name, "system");
ptable[pcount].start = start * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].length = count * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MMC_CMD;
pcount++; /* User Data */
get_mmc_part_info((dev_number_write == )?"":"", , &start, &count, &pid); //主要分区中的mmcblk0p3分区
if (pid != 0x83)
goto part_type_error;
strcpy(ptable[pcount].name, "userdata");
ptable[pcount].start = start * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].length = count * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MMC_CMD;
pcount++; /* Cache */
get_mmc_part_info((dev_number_write == )?"":"", , &start, &count, &pid); //主要分区中的mmcblk0p4分区
if (pid != 0x83)
goto part_type_error;
strcpy(ptable[pcount].name, "cache");
ptable[pcount].start = start * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].length = count * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MMC_CMD;
pcount++; /* Fat */
get_mmc_part_info((dev_number_write == )?"":"", , &start, &count, &pid); //主要分区中的mmcblk0p1分区
if (pid != 0xc)
goto part_type_error;
strcpy(ptable[pcount].name, "fat");
ptable[pcount].start = start * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].length = count * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MMC_CMD;
pcount++; #if 1 // Debug
fastboot_flash_dump_ptn();
#endif LCD_setleftcolor(0x8a2be2); return ; part_type_error:
printf("Error: No MBR is found at SD/MMC.\n");
printf("Hint: use fdisk command to make partitions.\n"); return -;
}

  (3) 在“iTop4412_uboot/board/samsung/smdkc210/smdkc210.c”文件的“board_late_init”函数中,可以看到关于bootcmd变量的信息,如下。    

int board_late_init (void)
{
int ret = check_bootmode();
if ((ret == BOOT_MMCSD || ret == BOOT_EMMC441 || ret == BOOT_EMMC43 )
&& boot_mode == ) {
//printf("board_late_init\n");
char boot_cmd[]; #if 0
sprintf(boot_cmd, "movi read kernel 40008000;movi read rootfs 40d00000 100000;bootm 40008000 40d00000"); //这条语句不会编译
#else
#ifdef SMDK4412_SUPPORT_UBUNTU
sprintf(boot_cmd, "movi read kernel 40008000;bootm 40008000 40d00000"); //这条语句不会编译
#else
sprintf(boot_cmd, "movi read kernel 40008000;movi read rootfs 40df0000 100000;bootm 40008000 40df0000"); //只有这条关于bootcmd变量的语句会编译
#endif
#endif
/* end modify */
setenv("bootcmd", boot_cmd);
//setenv("bootargs", "root=/dev/mmcblk0p2");
} return ;
}

3.修改uboot源码

  大概思路已经在前面讲过,为了方便读者理解,再详述一下双系统引导的思路。两个系统共用同一套uboot、内核、rootfs(根文件系统),在mmcblk0p2分区中存放一个系统,在mmcblk0p3中存放另一个系统,uboot每次启动时,会从mmcblk0p4分区中读取定长的字符串,根据字符串的内容来决定该引导哪一个系统。

  (1) 打开“iTop4412_uboot/common/cmd_fastboot.c”文件,修改“set_partition_table_sdmmc”函数中有关mmcblk0p2分区和mmcblk0p3分区的命名部分(只修改分区名称,其他的不做修改),将原先的“system”和“userdata”两个分区名称改为“system_linux”和“system_qte”,方便记忆。    

  /* Linux System */
get_mmc_part_info((dev_number_write == )?"":"", , &start, &count, &pid); //主要分区中的mmcblk0p2分区,用于存放最小Linux系统
if (pid != 0x83)
goto part_type_error;
strcpy(ptable[pcount].name, "system_qte");  //分区名改为system_qte
ptable[pcount].start = start * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].length = count * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MMC_CMD;
pcount++; /* Qt/E System */
get_mmc_part_info((dev_number_write == )?"":"", , &start, &count, &pid); //主要分区中的mmcblk0p3分区,用于存放Qt/E系统
if (pid != 0x83)
goto part_type_error;
//strcpy(ptable[pcount].name, "userdata");
strcpy(ptable[pcount].name, "system_linux"); //分区名改为system_linux
ptable[pcount].start = start * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].length = count * CFG_FASTBOOT_SDMMC_BLOCKSIZE;
ptable[pcount].flags = FASTBOOT_PTENTRY_FLAGS_USE_MMC_CMD;
pcount++;

  (2) 打开“iTop4412_uboot/common/main.c”文件,在“main_loop”函数中添加如下代码  

    char bootargstr[];
run_command("mmc read 0 40008000 408000 10", ); //从emmc的0x408000块处读取0x10个块的数据到内存的0x40008000处,至于为什么从emmc的0x408000处读,请看后文的解析
memcpy(bootargstr, (char*)0x40008000, ); //把读取到的数据赋给bootargstr
if(!strncmp(bootargstr, "qte", )) //判断bootargstr的内容,如果是字符串“qte”
sprintf(bootargstr, "root=/dev/mmcblk0p2"); //把mmcblk0p2设置为系统的根,即启动带Qt/E的Linux系统
else
sprintf(bootargstr, "root=/dev/mmcblk0p3"); //把mmcblk0p3设置为根,即启动最小Linux系统
setenv("bootargs", bootargstr); //设置环境变量bootargs,把bootargstr字符串的内容赋给环境变量bootargs

  添加位置如下图所示。

  

  为什么要从emmc的0x408000块处读字符串呢?首先我们明确一下“mmc read”命令的用法:mmc read <device num> addr blk# cnt [partition],即从某设备的第blk#个块(一个块为512B)开始,读取cnt个块的数据,将数据存放到内存的addr位置。其次,在前文中我们曾经获取过emmc的“主要分区”信息,如下图

  

  可以看到mmcblk0p4分区的起始块(block start #)是4227072,这是个十进制数,我们把它转换为十六进制,便得到了0x408000。

4.编译和烧写

  (1) 编译修改后的uboot,用fastboot工具将其烧写到开发板中。

  

  烧写完成后,重启开发板,并进入uboot模式。在串口终端输入命令“printenv”,可以看到环境变量中又添加了一个bootargs变量,它是用来告诉内核要挂载哪个分区中的文件系统的,挂载不同的文件系统,就实现了不同系统的引导。根据bootargs变量的值可知,目前要引导的系统是位于mmcblk0p3分区中的系统,即最小Linux系统。

  

  在串口终端输入命令“fastboot”,可以看到原先的“system”和“userdata”分区名称已经被修改为“system_qte”和“system_linux”,如下图所示。

  

  (2) 用fastboot工具把两个系统分别烧写进各自的分区中,如下图所示。

  

  (3) 重启开发板(不要进入uboot模式),可以看到开发板进入到了最小Linux系统。

  在串口终端输入命令“echo "qte" > /dev/mmcblk0p4”,该命令表示向mmcblk0p4分区的起始位置写入字符串“qte”

  

  然后重启开发板,可以看到开发板进入到了Qt/E系统。

  再在串口终端输入命令““echo "lin" > /dev/mmcblk0p4””,该命令表示向mmcblk0p4分区的起始位置写入字符串“lin”

  

  重启开发板,可以看到开发板又回到了最小Linux系统。

  双系统引导成功!

ARM开发板实现双系统引导的一种方法——基于迅为iTOP-4412开发板的更多相关文章

  1. Win7&Ubuntu12.04 双系统引导问题

    周末的时候手贱,重装系统,导致原来的ubuntu12.04和win7双系统的引导不见了,所以在此进行一下说明,如何修复. 1. win7和ubuntu12.04双系统引导修复 问题描述:    在重装 ...

  2. win10 + Ubuntu 20.04 LTS 双系统 引导界面美化

    版权声明:本文为CSDN博主「ZChen1996」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明. 原文链接:https://blog.csdn.net/ZChen1 ...

  3. Win7与Ubuntu双系统时卸载Ubuntu的方法

    Win7与Ubuntu双系统时卸载Ubuntu的方法 [日期:2010-03-26] 来源:Ubuntu社区  作者:Ubuntu编辑 [字体:大 中 小]       1. 下载MBRFix工具,放 ...

  4. 双系统中ubuntu的安装方法

    双系统中ubuntu的安装方法 注意:给电脑安装双系统时,一定要先装Windows系统,再安装Linux系统! 原因是电脑开机后,要先执行一段bootloader引导程序:再由引导程序启动操作系统.W ...

  5. C# Windows Phone 8 WP8 开发,取得手机萤幕大小两种方法。

    原文:C# Windows Phone 8 WP8 开发,取得手机萤幕大小两种方法. 一般我们在开发Windows Phone App时,需要取得萤幕的大小来自定义最佳化控制项的大小,但是开如何取得萤 ...

  6. centos7 ,windows7 grub2 双系统引导

    因为原先的windows7 和 centos6.3 安装在一台笔记本上.因为centos6.3不能识别无线网卡,在网上找了找,要升级内核到3.2以上. 因为本人初级水平,不敢擅自行动,怕把window ...

  7. Linux/Windows双系统引导修复

    安装双系统建议先安装windows,然后在安装Linux,使用Linux(grub2)引导双系统 如果重新安装了windows,则无法引导进入linux,需要修复引导 在windows下安装easyB ...

  8. CentOS7与Win7双系统引导问题

    先安装的Win7,后安装的CentOS7,结果系统引导就只有CentOS7了.记得以前CentOS6.x系列没这个问题,主要是由于CentOS7.x使用grub2的原因吧. 方案一:使用Win PE. ...

  9. 使用 boot-repair 对 Windows + Ubuntu 双系统引导修复

    问题描述:     由于在windows上进行更新/重装/修改了引导设置以后,windows会“自私”地重写引导,导致Ubuntu系统引导消失而无法选择Ubuntu启动.

随机推荐

  1. "点"醒自己

    回顾: 过去的经历 从18年开始在某机构进行了测试培训,9月正式加入测试小白大军,中间也经历了半年的空窗期,入职2个月应能力不够被公司辞退,后通过培训班的朋友内推到一家软件外包公司,工作到现在. 思考 ...

  2. GitHub 热点速览 Vol.31:在?跑个 GitHub 评分如何?

    摘要:个性化的 GitHub README 自从 7 月上线之后一直风靡在各大技术平台,当中最有意思的莫过于代表你技术的 GitHub Readme Stats 了,除了能显示你提交的 pr.comm ...

  3. spring-Sessions are not supported by the MongoDB cluster to which this client is connected

    原因: 1.mongodb版本过低4.0以下不支持事务的情况下会报这个 2.安全认证问题参考如下连接 https://blog.csdn.net/ssehs/article/details/10530 ...

  4. CSS内容布局

    网页是由不同内容块构成的:标题.段落.链接.列表.图片.视频,等等. 1.定位 定位并不适合总体布局,因为它会把元素拉出页面的正常流. 元素的初始定位方式为静态定位(static),即块级元素垂直堆叠 ...

  5. 【Python笔记】2020年7月30日练习【汉诺塔游戏】

    学习教程:廖雪峰-Python教程-函数-递归函数 学习笔记: 实例代码如下: def move(n, a, b, c): if n == 1: print(a,'--->', c) else: ...

  6. JavaScript 模块封装

    JavaScript 模块封装 前言介绍 在最早的时候JavaScript这门语言其实是并没有模块这一概念,但是随着时间的推移与技术的发展将一些复用性较强的代码封装成模块变成了必要的趋势. 在这篇文章 ...

  7. 93复原IP地址。

    from typing import List# 这道题不是很难,但是限制条件有很多.# 用递归的方法可以很容易的想到.只需要四层递归就好了.# 每次进行加上限制条件.过滤每一层就好了..class ...

  8. C#LeetCode刷题之#58-最后一个单词的长度(Length of Last Word)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3927 访问. 给定一个仅包含大小写字母和空格 ' ' 的字符串, ...

  9. 从一次外卖到对oauth2.0的思考

    别问oauth1.0哪去了,问就是不好讲. 1. 外卖并不好吃   今天下班得早,想吃顿好的,于是就点了一份外卖,过了一会儿,外卖到了,但是在小区大门被堵住了,我亲自远程开门后才能进来,又过了一会,被 ...

  10. LeetCode 309 Best Time to Buy and Sell Stock with Cooldown 解决方案

    题目描述 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 .​ 设计一个算法计算出最大利润.在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 你不能同时参与多笔 ...