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系统的初始化
  • NOOBS启动过程:
  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

  • 在SD卡上支持多个系统的实现:

在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