小伙伴们的智能之旅

Raspberry Pi 2B开机过程分析

我们知道,通过NOOBS系统,Raspberry Pi 2B(树莓派)可以在一张TF卡上安装多个操作系统,你可以在启运的过程中选择进入哪一个操作系统,这个功能是如何实现的呢,我们需要对Raspberry Pi 2B(树莓派)的开机过程进行深入了解,以便能将定制的系统安装到Raspberry Pi 2B(树莓派)上。

  1. 加载并运行bootcode.bin
  2. bootcode.bin运行时,加载并运行start.elf和fixup.dat
  3. start.elf读取config.txt, 对GPU进行配置
  4. start.elf读取cmdline.txt, 加载并运行kernel.img或者是kernel7.img(rpi 2b) 将cmdline.txt中的参数传递给kernel
  5. kernel运行并解析command line, 去挂载根文件系统
  6. kernel挂载根文件系统后,执行init进程,作linux系统的初始化
  1. 加载并运行bootcode.bin
  2. bootcode.bin发现start.elf文件不存在, 加载并执行recovery.elf
  3. recovery.elf读取recovery.cmdline, 加载recovery.img(kernel)或者是recovery7.img(rpi 2b)和recovery.rfs(initrd)
  4. recovery.img运行并解析recovery.elf传递过来的command line
  5. recovery.img将recovery.rfs文件作为根文件系统挂载,执行init进程初始化整个linux系统

NOTE: bootcode.bin, start.elf, kernel.img, kernel7.img, cmdline.txt, config.txt, recovery.elf, recovery.img, recovery.rfs, recovery.cmdline都是在SD卡的第一个FAT分区。start_cd.elf, fixup_cd.dat是cut-down版本, start_x.elf, fixup_x.elf是testing版本,具体请看这里: http://elinux.org/RPi_Software

在recovery系统中,将要启动的系统的boot分区值写入/sys/module/bcm2708/parameters/reboot_part, 重启系统。

具体实现请看代码:https://github.com/raspberrypi/linux/blob/rpi-4.1.y/arch/arm/mach-bcm2709/bcm2709.c

reboot_part记录的的user space传过来的值,在系统重启时PM_RSTS记录重启的分区:

int calc_rsts(int partition)
{
	return PM_PASSWORD |
		((partition & (1 << 0))  << 0) |
		((partition & (1 << 1))  << 1) |
		((partition & (1 << 2))  << 2) |
		((partition & (1 << 3))  << 3) |
		((partition & (1 << 4))  << 4) |
		((partition & (1 << 5))  << 5);
}

static void bcm2709_restart(enum reboot_mode mode, const char *cmd)
{
	extern char bcm2708_reboot_mode;
	uint32_t pm_rstc, pm_wdog;
	uint32_t timeout = 10;
	uint32_t pm_rsts = 0;

	if(bcm2708_reboot_mode == 'q')
	{
		// NOOBS < 1.3 booting with reboot=q
		pm_rsts = readl(__io_address(PM_RSTS));
		pm_rsts = PM_PASSWORD | pm_rsts | PM_RSTS_HADWRQ_SET;
	}
	else if(bcm2708_reboot_mode == 'p')
	{
		// NOOBS < 1.3 halting
		pm_rsts = readl(__io_address(PM_RSTS));
		pm_rsts = PM_PASSWORD | pm_rsts | PM_RSTS_HADWRH_SET;
	}
	else
	{
		pm_rsts = calc_rsts(reboot_part);
	}

	writel(pm_rsts, __io_address(PM_RSTS));

	/* Setup watchdog for reset */
	pm_rstc = readl(__io_address(PM_RSTC));

	pm_wdog = PM_PASSWORD | (timeout & PM_WDOG_TIME_SET); // watchdog timer = timer clock / 16; need password (31:16) + value (11:0)
	pm_rstc = PM_PASSWORD | (pm_rstc & PM_RSTC_WRCFG_CLR) | PM_RSTC_WRCFG_FULL_RESET;

	writel(pm_wdog, __io_address(PM_WDOG));
	writel(pm_rstc, __io_address(PM_RSTC));
}

/* We can't really power off, but if we do the normal reset scheme, and indicate to bootcode.bin not to reboot, then most of the chip will be powered off */
static void bcm2709_power_off(void)
{
	extern char bcm2708_reboot_mode;
	if(bcm2708_reboot_mode == 'q')
	{
		// NOOBS < v1.3
		bcm2709_restart('p', "");
	}
	else
	{
		/* partition 63 is special code for HALT the bootloader knows not to boot*/
		reboot_part = 63;
		/* continue with normal reset mechanism */
		bcm2709_restart(0, "");
	}
}

如将/dev/mmcblk0p2格式化成FAT分区,将bootcode.bin, config.txt, start.elf, fixup.dat, cmdline.txt kernel7.img保存在这个分区

做如下操作就可以启动这个分区的系统(rpi2b, 待验证):

$ sudo echo 2 > /sys/module/bcm2709/parameters/reboot_part
$ sudo reboot

  1. 如何启动分区中有recovery.elf, 会优先启动recovery系统(待验证)->[2016-01-02]已更新
  2. 如果recovery系统已经运行,那么要进入正常系统之前,recovery系统会往/sys/module/bcm2709/parameters/reboot_part中写值,以防止系统重启后进入的还是recovery的系统(待验证)->[2016-01-02]已更新
  3. 从上面的代码可以看出,分区63用于关机,不能作为启动分区
Uncompressing Linux... done, booting the kernel.
?
Welcome to the rescue system
recovery login: Uncompressing Linux... done, booting the kernel.
[    0.000000] Booting Linux on physical CPU 0xf00
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Initializing cgroup subsys cpuacct
[    0.000000] Linux version 3.18.7-v7+ (dc4@dc4-XPS13-9333) (gcc version 4.8.3 20140303 (prerelease) (crosstool-NG linaro-1.13.1+bzr2650 - Linaro GCC 2014.03) ) #755 SMP PREEMPT Thu Feb 12 17:20:48 GMT 2015
[    0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] Machine model: Raspberry Pi 2 Model B
[    0.000000] cma: Reserved 8 MiB at 0x3c800000
[    0.000000] Memory policy: Data cache writealloc
[    0.000000] [bcm2709_smp_init_cpus] enter (8620->f3003010)
[    0.000000] [bcm2709_smp_init_cpus] ncores=4

从”Welcome to the rescue system”和”Recovery login”, 这时进入的时recovery的系统。同时由显示屏上显示的界面也印证了第一次开机最先进入的是recovery系统。如果这时用启没有按键盘上的shift键,一段时间后会进入正常的系统。

在Windows下,按照NOOBS_lite_v1.5/INSTRUCTIONS-README.txt的方法,使用SD formatter进行format,SD最开始的4MB空间会被保留,也就是说第一个分区是从4MB开始的,在Linux下使用fdisk进行分区,最开始会保留1MB的空间。

最重要的一点:第一个分区一定要标记为FAT32分区,也是就type为0xb,并且将该分区格式化成FAT32文件系统否则无法开机。

如果不想使用NOOBS作启动系统,可以将recovery.img或者recovery7.img(RPi 2B)和recovery.rfs替换掉。

Command (m for help): p

Disk /dev/sdb: 7948 MB, 7948206080 bytes
245 heads, 62 sectors/track, 1021 cylinders, total 15523840 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x4187c4d3

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048      231423      114688    b  W95 FAT32
/dev/sdb2          231424      460799      114688   83  Linux
/dev/sdb3          460800      985087      262144   83  Linux

[2016-01-02更新] 原来并不是我想的那样,先看一下分区表:

Disk /dev/mmcblk0: 15.9 GB, 15931539456 bytes
4 heads, 16 sectors/track, 486192 cylinders, total 31116288 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00080214

        Device Boot      Start         End      Blocks   Id  System
/dev/mmcblk0p1            8192     1679687      835748    e  W95 FAT16 (LBA)
/dev/mmcblk0p2         1687552    31050751    14681600   85  Linux extended
/dev/mmcblk0p3        31050752    31116287       32768   83  Linux
/dev/mmcblk0p5         1695744     2744319      524288   83  Linux
/dev/mmcblk0p6         2752512     2875391       61440    c  W95 FAT32 (LBA)
/dev/mmcblk0p7         2883584    31049727    14083072   83  Linux

Command (m for help): 

/dev/mmcblk0p1 存放的是recovery系统启动所需要的文件:bootcode.bin, recovery.elf, recovery7.img, recovery.rfs, recovery.cmdline.

而/dev/mmcblk0p6存放的是raspbian系统启动所需的文件:bootcode.bin, config.txt, start.elf, fixup.dat, kernel7.img, cmdline.txt

所以系统第一次开机的时候进入recovery系统,recovery系统更新PM_RSTS寄存器,通知CPU重启后加载raspbian系统的boot分区。由于PM_RSTS寄存器系统重启时数据不丢失,所以在raspbian系统里面输入reboot的时候系统重启加载的是raspbian系统boot分区所在的代码。

相关的参考文档:

  1. https://www.raspberrypi.org/documentation/installation/noobs.md
  2. https://github.com/raspberrypi/noobs
  3. https://github.com/raspberrypi/noobs/issues/71
  4. https://github.com/raspberrypi/noobs/commit/acc5ec4d79de20930d1a93eda9954b108abe08f3
  5. https://github.com/raspberrypi/noobs/wiki
  6. https://github.com/raspberrypi/noobs/wiki/Standalone-partitioning-explained