我们知道,通过NOOBS系统,Raspberry Pi 2B(树莓派)可以在一张TF卡上安装多个操作系统,你可以在启运的过程中选择进入哪一个操作系统,这个功能是如何实现的呢,我们需要对Raspberry Pi 2B(树莓派)的开机过程进行深入了解,以便能将定制的系统安装到Raspberry Pi 2B(树莓派)上。
- 从相关的文档上看,系统开机的过程是这样的:(Standalone partitioning explained · raspberrypi_noobs Wiki · GitHub和NOOBS partitioning explained · raspberrypi_noobs Wiki · GitHub)
- 加载并运行bootcode.bin
- bootcode.bin运行时,加载并运行start.elf和fixup.dat
- start.elf读取config.txt, 对GPU进行配置
- start.elf读取cmdline.txt, 加载并运行kernel.img或者是kernel7.img(rpi 2b) 将cmdline.txt中的参数传递给kernel
- kernel运行并解析command line, 去挂载根文件系统
- kernel挂载根文件系统后,执行init进程,作linux系统的初始化
- NOOBS启动过程:
- 加载并运行bootcode.bin
- bootcode.bin发现start.elf文件不存在, 加载并执行recovery.elf
- recovery.elf读取recovery.cmdline, 加载recovery.img(kernel)或者是recovery7.img(rpi 2b)和recovery.rfs(initrd)
- recovery.img运行并解析recovery.elf传递过来的command line
- 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
- 其他启动规则:
- 如何启动分区中有recovery.elf, 会优先启动recovery系统(待验证)->[2016-01-02]已更新
- 如果recovery系统已经运行,那么要进入正常系统之前,recovery系统会往/sys/module/bcm2709/parameters/reboot_part中写值,以防止系统重启后进入的还是recovery的系统(待验证)->[2016-01-02]已更新
- 从上面的代码可以看出,分区63用于关机,不能作为启动分区
- 再看看开机log(串口log), 找一根串口线(TTL电平), raspberry pi 的GPIO口: 6 – GND, 8 – TX, 10 – RX。log如下:
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分区所在的代码。
相关的参考文档:
- https://www.raspberrypi.org/documentation/installation/noobs.md
- https://github.com/raspberrypi/noobs
- https://github.com/raspberrypi/noobs/issues/71
- https://github.com/raspberrypi/noobs/commit/acc5ec4d79de20930d1a93eda9954b108abe08f3
- https://github.com/raspberrypi/noobs/wiki
- https://github.com/raspberrypi/noobs/wiki/Standalone-partitioning-explained