RPI: PieOnPi: u-boot构建及相关使用方法

当前,树莓派上所运行的Android 9系统,也是通过基于u-boot的bootloader引导开机的,这也是参考AndroidThings v1.0.4系统的。

下面这篇文档对Android官方提供的AndroidThings文件结构进行分析:

RPI3: AndroidThings Preview6 分区表分析(iot_rpi3.img)

而这篇文档提供的相关的脚本工具,可以将Android官方提供的AndroidThings文件拆分成多个分区文件:

RPI3: 在树莓派3上运行ANDROID 8.1系统

  • 使用bootloader的优点

目前看来,使用bootloader会增加开机时间,但是有了它,我们可以:

  1. 通过它来升级系统
  2. 通过它来分析由bootcode.bin, start_x.elf及fixup_x.dat所生成的flat device tree
  • 构建方法

1. 下载源代码:

$ git clone https://github.com/brobwind/pie-device-brobwind-rpi3-u-boot/ device/brobwind/rpi3/u-boot

2. 从https://github.com/raspberrypi/linux/blob/rpi-4.14.y/scripts/mkknlimg 下载mkknlimg脚本

$ curl https://raw.githubusercontent.com/raspberrypi/linux/rpi-4.14.y/scripts/mkknlimg > device/brobwind/rpi3/u-boot/mkknlimg
$ chmod +x device/brobwind/rpi3/u-boot/mkknlimg

3. 编译

$ cddevice/brobwind/rpi3/u-boot
$ ARCH=arm64 CROSS_COMPILE=../../../../prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-androidkernel- make rpi_3_defconfig
$ ARCH=arm64 CROSS_COMPILE=../../../../prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-androidkernel- make

4. 加标记

$ ./mkknlimg --dtok --ddtk --270x u-boot.bin u-boot-dtok.bin
  • u-boot环境变量

在rpiboot(device/brobwind/rpi3/boot/rpiboot)目录下,有个名为uboot.env的文件,用于存放u-boot的环境变量。可以用 https://chromium.googlesource.com/chromiumos/platform/uboot-env/+/refs/heads/master/uboot-env.py 这个工具进行编辑。

原文件dump出来的内容如下:

$ ./uboot-env.py --list -f uboot.env 
android_rootdev="/dev/mmcblk${android_root_devnum}p${android_root_partnum}"
stderr=serial,lcd
oem_bootloader_a=0x0d
stdout=serial,lcd
initrd_high=2d000000
stdin=serial
autoload=no
fastbootcmd=
  usb start && 
  dhcp && 
  fastboot udp
fdt_high=2e000000
oem_overlay_max_size=0x4000
oem_bootloader_b=0x0e
bootcmd=
  fdt addr "${fdt_addr}"; 
  fdt resize "${oem_overlay_max_size}"; 
  android_ab_select "android_slot" mmc "0;misc" || run fastbootcmd || reset; 
  test "${android_slot}" = "a" && env set oem_part "${oem_bootloader_a}"; 
  test "${android_slot}" = "b" && env set oem_part "${oem_bootloader_b}"; 
  ext2load mmc "0:${oem_part}" "${fdt_high}" /kernel.dtbo && 
      fdt apply "${fdt_high}"; 
  fdt get value bootargs /chosen bootargs; 
  setenv bootargs "${bootargs} androidboot.serialno=${serial#}"; 
  boot_android mmc "0;misc" "${android_slot}" "${kernel_addr_r}"; 
  reset
kernel_addr_r=0x01000000
board=rpi3
  • 常用的一些命令

在执行这些命令之前,你需要使用串口线, 树莓派通过这条串口线(USB转串口线)与PC机进行通信,

(图片来自: https://developer.android.com/things/hardware/raspberrypi#serial-console)

配置串口为115200,8n1。这里,将使用minicom软件对串口设备(/dev/ttyUSB0)进行操作:

$ sudo minicom -b 115200 -D /dev/ttyUSB0

这时,在系统开机时,你将会看到如下log:

当出现”Hit any key to stop autoboot: …” 时,按下任意键将进入交互模式。

可以看到,内置了很多常用的命令:

U-Boot> help
?       - alias for 'help'
android_ab_select- Select the slot used to boot from and register the boot attempt.
base    - print or set address offset
bdinfo  - print Board Info structure
blkcache- block cache diagnostics and control
boot    - boot default, i.e., run 'bootcmd'
boot_android- Execute the Android Bootloader flow.
bootd   - boot default, i.e., run 'bootcmd'
bootefi - Boots an EFI payload from memory
booti   - boot arm64 Linux Image image from memory
bootm   - boot application image from memory
bootp   - boot image via network using BOOTP/TFTP protocol
cmp     - memory compare
coninfo - print console devices and information
cp      - memory copy
crc32   - checksum calculation
dhcp    - boot image via network using DHCP/TFTP protocol
dm      - Driver model low level access
echo    - echo args to console
editenv - edit environment variable
env     - environment handling commands
exit    - exit script
ext2load- load binary file from a Ext2 filesystem
ext2ls  - list files in a directory (default /)
ext4load- load binary file from a Ext4 filesystem
ext4ls  - list files in a directory (default /)
ext4size- determine a file's size
false   - do nothing, unsuccessfully
fastboot- run as a fastboot usb or udp device
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls   - list files in a directory (default /)
fatsize - determine a file's size
fatwrite- write file into a dos filesystem
fdt     - flattened device tree utility commands
fstype  - Look up a filesystem type
go      - start application at address 'addr'
gpio    - query and control gpio pins
gzwrite - unzip and write memory to block device
help    - print command description/usage
iminfo  - print header information for application image
imxtract- extract a part of a multi-image
itest   - return true/false on integer compare
lcdputs - print string on video framebuffer
load    - load binary file from a filesystem
loadb   - load binary file over serial line (kermit mode)
loads   - load S-Record file over serial line
loadx   - load binary file over serial line (xmodem mode)
loady   - load binary file over serial line (ymodem mode)
loop    - infinite loop on address range
ls      - list files in a directory (default /)
lzmadec - lzma uncompress a memory region
md      - memory display
mdio    - MDIO utility commands
mii     - MII utility commands
mm      - memory modify (auto-incrementing address)
mmc     - MMC sub system
mmcinfo - display MMC info
mw      - memory write (fill)
nfs     - boot image via network using NFS protocol
nm      - memory modify (constant address)
part    - disk partition related commands
ping    - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
pxe     - commands to get and boot from pxe files
reset   - Perform RESET of the CPU
run     - run commands in an environment variable
save    - save file to a filesystem
saveenv - save environment variables to persistent storage
setcurs - set cursor position within screen
setenv  - set environment variables
setexpr - set environment variable as the result of eval expression
showvar - print local hushshell variables
size    - determine a file's size
sleep   - delay execution for some time
source  - run script from memory
sysboot - command to get and boot from syslinux files
test    - minimal test like /bin/sh
tftpboot- boot image via network using TFTP protocol
true    - do nothing, successfully
unzip   - unzip a memory region
usb     - USB sub-system
usbboot - boot from USB device
version - print monitor, compiler and linker version

1. 查看u-boot环境变量

U-Boot> printenv        
android_rootdev="/dev/mmcblk${android_root_devnum}p${android_root_partnum}"
autoload=no
board=rpi3
board_name=3 Model B+
board_rev=0xD
board_rev_scheme=1
board_revision=0xA020D3
bootcmd=\
  fdt addr "${fdt_addr}"; \
  fdt resize "${oem_overlay_max_size}"; \
  android_ab_select "android_slot" mmc "0;misc" || run fastbootcmd || reset; \
  test "${android_slot}" = "a" && env set oem_part "${oem_bootloader_a}"; \
  test "${android_slot}" = "b" && env set oem_part "${oem_bootloader_b}"; \
  ext2load mmc "0:${oem_part}" "${fdt_high}" /kernel.dtbo && \
      fdt apply "${fdt_high}"; \
  fdt get value bootargs /chosen bootargs; \
  setenv bootargs "${bootargs} androidboot.serialno=${serial#}"; \
  boot_android mmc "0;misc" "${android_slot}" "${kernel_addr_r}"; \
  reset
ethaddr=b8:27:eb:3a:df:1c
fastbootcmd=\
  usb start && \
  dhcp && \
  fastboot udp
fdt_addr=2eff9400
fdt_high=2e000000
fdtcontroladdr=375a1910
fdtfile=broadcom/bcm2837-rpi-3-b-plus.dtb
initrd_high=2d000000
kernel_addr_r=0x01000000
oem_bootloader_a=0x0d
oem_bootloader_b=0x0e
oem_overlay_max_size=0x4000
serial#=00000000ce3adf1c
stderr=serial,lcd
stdin=serial
stdout=serial,lcd
usbethaddr=b8:27:eb:3a:df:1c

Environment size: 1229/16380 bytes

从这里可以看到,在我们执行fastbootcmd做了:

- 1. 执行usb相关的操作:扫描usb设备

- 2. 执行dhcp命令,自动分配IP地址

- 3. 启动fastboot服务,走的是udp协议

2. 进入fastboot模式,更新系统文件:rpiboot.img, boot.img, system.img, vendor.img,如更新rpiboot.img

进入fastboot模式之前,需要将树莓派通过网线接入到与下载系统文件的PC同一网络中,在minicom中执行run fastbootcmd命令:

U-Boot> run fastbootcmd 
starting USB...
USB0:   scanning bus 0 for devices... 5 USB Device(s) found
       scanning usb for storage devices... 0 Storage Device(s) found
lan78xx_eth Waiting for PHY auto negotiation to complete..... done
BOOTP broadcast 1
DHCP client bound to address 192.168.5.211 (6 ms)
lan78xx_eth Waiting for PHY auto negotiation to complete...... done
Using lan78xx_eth device
Listening for fastboot command on 192.168.5.211

可以看到,当前树莓派已经连接成功,分配到的IP地址为192.168.5.211。这时,我们就可以在另一个终端上执行如下命令将rpiboot.img写入到rpiboot分区:

$ fastboot -s udp:192.168.5.211 flash rpiboot out/target/product/rpi3/rpiboot.img

而在minicom界面,你可以看到如下信息:

U-Boot> run fastbootcmd 
starting USB...
USB0:   scanning bus 0 for devices... 5 USB Device(s) found
       scanning usb for storage devices... 0 Storage Device(s) found
lan78xx_eth Waiting for PHY auto negotiation to complete..... done
BOOTP broadcast 1
DHCP client bound to address 192.168.5.211 (6 ms)
lan78xx_eth Waiting for PHY auto negotiation to complete...... done
Using lan78xx_eth device
Listening for fastboot command on 192.168.5.211
Starting download of 67108864 bytes
..........................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
..........................................................................
....................................................................
downloading of 67108864 bytes finished
Flashing Raw Image
........ wrote 67108864 bytes to 'rpiboot'

由于我们使用了A/B分区,当前我们只使用到A分区,所以更新boot.img, system.img, vendor.img的命令如下:

$ fastboot -s udp:192.168.5.211 flash boot_a out/target/product/rpi3/boot.img
$ fastboot -s udp:192.168.5.211 flash system_a out/target/product/rpi3/system.img
$ fastboot -s udp:192.168.5.211 flash vendor_a out/target/product/rpi3/vendor.img

擦除userdata分区:

$ fastboot -s udp:192.168.5.211 erase userdata
Erasing 'userdata'...
(bootloader) erasing
OKAY [  3.249s]
Finished. Total time: 3.288s

3. 查看device tree信息

在minicom中执行如下命令,可以查看系统启动时创建的device tree信息:

U-Boot> fdt addr ${fdt_addr}
U-Boot> fdt print
/ {
        memreserve = <0x37600000 0x08000000>;
        serial-number = "00000000ce3adf1c";
        compatible = "raspberrypi,3-model-b-plus", "brcm,bcm2837";
        model = "Raspberry Pi 3 Model B Plus Rev 1.3";
        interrupt-parent = <0x00000001>;
        #address-cells = <0x00000001>;
        #size-cells = <0x00000001>;
        system {
                linux,serial = <0x00000000 0xce3adf1c>;
                linux,revision = <0x00a020d3>;
        };
        axi {
                vc_mem {
                        reg = <0x3e200000 0x3f600000 0xc0000000>;
                };
        };
// ...
};

这在配置config.txt文件时特别有用。

4. 通过执行run bootcmd加载kernel启动Android系统

U-Boot> run bootcmd 
ANDROID: Booting slot: a
72 bytes read in 1 ms (70.3 KiB/s)
ANDROID: reboot reason: "(none)"
Booting kernel at 0x1000000 with fdt at 2eff9400...


## Booting Android Image at 0x01000000 ...
Kernel load addr 0x10080000 size 19627 KiB
Kernel command line: console=ttyS0,115200 buildvariant=eng buildvariant=eng
## Flattened Device Tree blob at 2eff9400
   Booting using the fdt blob at 0x2eff9400
   Loading Kernel Image ... OK
   reserving fdt memory region: addr=0 size=1000
   reserving fdt memory region: addr=2eff9400 size=ac00
   Loading Device Tree to 000000002dff2000, end 000000002dfffbff ... OK

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注