U-Boot porting

From postmarketOS Wiki


Why bother?

Porting U-boot to your device gives you more control when booting mainline kernels, namely:

  1. Adjusting boot command line
  2. Dual-boot without hacks
  3. Prevents the stock bootloader from accidentally modifying your kernel / device tree

Supported Devices

Device Codename Chipset Primary Bootloader Secondary Bootloader Mainline Internal storage SD USB Host USB Peripheral Screen Keyboard Buttons
Acer Chromebook 13 (CB5-311) google-nyan-big Nvidia Tegra K1 (T124) Y Y Y Y - N Y -
Apple Mac mini M1 apple-j274ap Apple M1 - Y Y Y - Y Y Y Y -
Apple MacBook Air apple-tonga Apple M1 - Y Y Y - Y Y Y Y -
Apple Tonga tonga-generic Apple M1 - Y Y Y - Y Y Y Y -
Arrow DragonBoard 410c arrow-db410c Qualcomm Snapdragon 410 (APQ8016) Y Y Y Y Y Y - Y
Asus Chromebook C201 google-veyron-speedy Rockchip RK3288 Y Y Y Y Y P - Y Y -
Asus Chromebook Flip C100 google-veyron-minnie Rockchip RK3288 Y N Y Y - Y Y -
Asus Eee Pad Transformer asus-tf101 Nvidia Tegra 2 (T20) Y Y Y Y Y Y Y Y N Y
Asus Eee Pad Transformer Prime asus-tf201 Nvidia Tegra 3 (T30) Y Y Y Y Y Y Y Y N Y
Asus Transformer Infinity asus-tf700t Nvidia Tegra 3 (T33) Y Y Y Y Y Y Y P N Y
Asus Transformer Pad asus-tf300t Nvidia Tegra 3 (T30L) Y Y Y Y Y Y Y Y N Y
Asus Transformer Pad (TF701T) asus-tf701t Nvidia Tegra 4 (T114) P P Y Y Y Y Y Y N Y
Asus Transformer Pad 3G asus-tf300tg Nvidia Tegra 3 (T30L) Y Y Y Y Y Y Y Y N Y
Asus VivoTab RT asus-tf600t Nvidia Tegra 3 (T30) Y - Y Y Y Y Y N N Y
Beelink gt1 (Ultimate) beelink-gt1 Amlogic S912 Y Y Y Y Y Y Y Y Y
CDI EduGear Chromebook K google-veyron-jerry Rockchip RK3288 Y N Y - Y Y -
CTL Chromebook J2 / J4 google-veyron-jerry Rockchip RK3288 Y N Y - Y Y -
Digma 1538E 4G digma-ps1150ml MediaTek MT8735 Y Y N Y N -
DongshanPi NeZha STU dongshanpi-nezhastu Allwinner D1-H Y - Y Y Y Y N - -
Epik Chromebook ELB1101 google-veyron-jerry Rockchip RK3288 Y N Y - Y Y -
Google (ASUS) Nexus 7 (2012) asus-grouper Nvidia Tegra 3 (T30L) Y N Y Y - N Y Y - Y
Google (ASUS) Nexus 7 (2012) GSM asus-tilapia Nvidia Tegra 3 (T30L) Y N Y Y - N Y Y - Y
Hisense Chromebook 11 google-veyron-jerry Rockchip RK3288 Y N Y - Y Y -
HP Chromebook 14 G3 google-nyan-blaze Nvidia Tegra K1 (T124) Y Y N Y N - Y Y -
HTC One X htc-endeavoru Nvidia Tegra 3 (AP33) Y N Y Y - N Y Y - Y
iNet Tek D70 A33 inet-a33 Allwinner A33 N Y Y Y -
LG Optimus 4X HD lg-p880 Nvidia Tegra 3 (AP33H) Y P Y Y Y - Y Y - Y
LG Optimus Vu lg-p895 Nvidia Tegra 3 (AP33) Y P Y Y - - Y Y - Y
MangoPi MQ-Pro mangopi-mq-pro Allwinner D1-H Y - Y - Y Y Y N - -
Mecer Xpression V2 google-veyron-jerry Rockchip RK3288 Y N Y - Y Y -
Medion Chromebook S2013 google-veyron-jaq Rockchip RK3288
Microsoft Surface RT microsoft-surface-rt Nvidia Tegra 3 (T30) P P Y Y Y Y Y N Y
NComputing Chromebook CX100 / CX110 google-veyron-jerry Rockchip RK3288 Y N Y - Y Y -
Nokia N900 nokia-n900 TI OMAP 3430 Y Y Y Y - Y Y Y Y
Nvidia Tegra Note 7 nvidia-tegratab Nvidia Tegra 4 (T114) P P Y Y Y N Y Y - Y
ODROID HC2 odroid-hc2 Samsung Exynos 5422 Y - Y Y -
Oysters T74ER oysters t74er Allwinner A33 Y Y N Y Y Y Y
Oysters T84Bi 4G oysters-t84bi-4g MediaTek MT8735 Y Y N Y N -
Poin2 Chromebook 11 google-veyron-jerry Rockchip RK3288 Y Y Y Y Y Y - Y Y -
Positivo Chromebook CH1190 google-veyron-jerry Rockchip RK3288 Y N Y - Y Y -
Qualcomm Snapdragon 410 qcom-msm8916 Qualcomm Snapdragon 410 (MSM8916) N P P Y Y N N Y Y Y
Qualcomm Snapdragon 845 qualcomm-sdm845 Qualcomm Snapdragon 845 (SDM845) N Y P Y Y P Y Y Y Y
Radxa Zero radxa-zero Amlogic S905Y2 Y - Y Y Y Y Y Y Y
Samsung Chromebook google-snow Samsung Exynos 5250 Y Y Y Y Y - Y Y -
Samsung Chromebook 2 11.6" google-peach-pit Samsung Exynos 5420 Y Y Y Y Y - Y Y -
Samsung Chromebook 2 13.3" google-peach-pi Samsung Exynos 5800 Y Y Y Y Y - Y Y -
Samsung Chromebook Plus google-kevin Rockchip RK3399 Y Y Y Y Y Y - Y Y N
Samsung Galaxy Ace 2 GT-I8160 samsung-codina ST-Ericsson NovaThor U8500 N Y Y Y Y Y Y - Y
Samsung Galaxy Amp (SGH-I407) samsung-kyle ST-Ericsson NovaThor U8500 N Y Y Y Y Y Y - Y
Samsung Galaxy Beam samsung-gavini ST-Ericsson NovaThor U8500 N Y Y Y Y Y Y - Y
Samsung Galaxy Exhibit (SGH-T599) samsung-codina-tmo ST-Ericsson NovaThor U8500 N Y Y Y Y Y Y - Y
Samsung Galaxy Grand Prime+ samsung-grandpplte MediaTek MT6737T N Y N Y N N - Y
Samsung Galaxy S Advance samsung-janice ST-Ericsson NovaThor U8500 N Y Y Y Y Y Y - Y
Samsung Galaxy S II samsung-i9100 Samsung Exynos 4210 Dual Y N -
Samsung Galaxy S III samsung-m0 Samsung Exynos 4412 Quad Y - N -
Samsung Galaxy S III Mini samsung-golden ST-Ericsson NovaThor U8500 N Y Y Y N Y Y Y - Y
Samsung Galaxy S9 (SM-G9600/DS) samsung-starqltechn Qualcomm Snapdragon 845 (SDM845) N Y N - Y
Samsung Galaxy Xcover 2 GT-S7710 samsung-skomer ST-Ericsson NovaThor U8500 N Y Y Y Y Y Y - Y
VideoNet Chromebook BL10 google-veyron-jerry Rockchip RK3288 Y N Y - Y Y -
Xiaomi Mi Pad xiaomi-mocha Nvidia Tegra K1 (T124) Y Y Y Y Y N Y P - Y
Xiaomi POCO X3 NFC xiaomi-surya Qualcomm Snapdragon 732G (SM7150-AC) - Y N P N N P Y - Y
Xiaomi Redmi Note 12 Pro (4G) xiaomi-sweet_k6a Qualcomm Snapdragon 732G (SM7150-AC) - Y N P N N P Y - Y
Xunlong Orange Pi 3 xunlong-orangepi3 Allwinner H6 Y - Y Y Y N N -
Xunlong Orange Pi 5 xunlong-orangepi-5 Rockchip RK3588S
Xunlong Orange Pi PC xunlong-orangepi-pc Allwinner H3 Y - Y Y -

About

This article will be a guide for you to port U-Boot to your phone. It's possible, even if there is no complete SOC support in the U-Boot tree (you only need the UART driver for your device), because the stock bootloader initializes hardware for us. First thing to focus on should be getting u-boot shell prompt.

Preparation

  1. Find U-Boot code
  2. Before starting U-Boot porting, you should get access to the stock bootloaders UART port. Make sure you can see stock bootloader logs.
  3. Search the U-Boot code for a UART driver that is compatible with your SOC. It is good, if it support debug(i.e. include debug_uart.h)
  4. Get familiar with your phones RAM map
  5. Some useful links:

- bring up process
- configuration process

Read the following readme files:

  • top README
  • doc/README.kconfig
  • doc/usage/environment.rst

Create basic configuration, building and running

Add board files

Use guide from Free Electrons pdf or video version

Examples

  1. Add exynos7420 espresso board
  2. Add exynos7420 SOC support

Configure

The U-Boot configuration system comes from the Linux kernel. Put config options that are not supposed to be changed by user in /include/configs/.*.h files, in *_defconfig otherwise

Options, you DO NOT need

  • All SPL related options. SPL(Secondary Program Loader) splits U-Boot in two parts. You don't need this, since the stock bootloader will load the whole U-Boot image into RAM.
  • CONFIG_BOARD_EARLY_INIT_F
  • CONFIG_CUSTOM_SYS_INIT_SP_ADDR - address of early C runtime environment stack. Calculated automatically even if you use CONFIG_POSITION_INDEPENDENT, but if you want to specify it manually this is how.

Options, you need

  • CONFIG_SYS_MALLOC_LEN - size of early C runtime environment heap
  • CONFIG_SYS_INIT_SP_BSS_OFFSET - when CONFIG_POSITION_INDEPENDENT=y, defines offset to early c runtime stack from bss section from bss section. Default is 512K.
  • CONFIG_TEXT_BASE - u-boot base address. It's the address in RAM, where u-boot is loaded by stock bootloader. Not obligatory, if you use position independent build with CONFIG_POSITION_INDEPENDENT=y. See instructions below, how to find it.
  • CFG_SYS_SDRAM_BASE - start address of the dynamic RAM. Can be found in memory node in linux device tree.
  • CONFIG_DEBUG_UART=y
  • CONFIG_DEBUG_UART_SKIP_INIT=y - make sure to comment _debug_uart_init function code in you serial driver with that option
  • CONFIG_DEBUG_UART_BASE=0x13820000 // your uart address from downstream device tree
  • CONFIG_LOGLEVEL=8 // log all
  • CONFIG_POSITION_INDEPENDENT - Since Galaxy S8, Samsung randomizes kernel load address. You have to enable this option, if Kernel code start address is random, i.e. changes each boot. Find Kernel code address with cat /proc/iomem
  • CONFIG_LINUX_KERNEL_IMAGE_HEADER - if you want U-boot binary to look as much as Linux kernel image as possible, to pack it inside android boot.img so stock android bootloader can recognize it

Building

Make u-boot

Install arm cross compiler Build:

export CROSS_COMPILE=aarch64-linux-gnu- 
make <$board_name>_defconfig
make

Create android boot image

mkbootimg --base 0x40000000 --kernel_offset 0x00008000 --ramdisk_offset 0x01000000 --tags_offset 0x00000100 --pagesize 2048 --second_offset 0x00f00000 --kernel {path to u-boot.bin} -o {output file}

Replace the default offsets with your device offsets, found from downstream boot.img.

Running

Flash android image, or follow Bootloaders_porting_using_linux guide

Getting UART debug output

Raw

This may be useful, when no C runtime environment is set up yet. You should find UART address to write

C

*(volatile unsigned char *)(0x13820020/*uart register to write characters*/) = 0x48; // print H letter

Assembly

mov x26, #0x20 // move 0x13820020 low 16 bit to x26 register
movk x26, #0x1382, lsl #16 // move 0x13820020 high 16 bit to x26 register
mov x27, #0x48 // H letter ascii code
str x27, [x26] // move H to uart register

Using debug() function

You can get debug output from particular *.c file by adding #define DEBUG at the top of file, and calling debug("debug output"); where you need.

Using printascii()

When using CONFIG_DEBUG_UART, you can #include <debug_uart.h> and call printascii("something") wherever needed. There are other similar functions for printing non-strings, you can find them in include/debug_uart.h.

Finding base address

Unless you build u-boot with CONFIG_POSITION_INDEPENDENT=y, you should tell u-boot base address, i.e. address in RAM, where stock bootloader load u-boot. This section describes, how to find it.

Without the knowledge of base address u-boot will fail on /common/board_f.c file when calling functions from init_sequence_f. This is a list of initcall addresses, and if CONFIG_TEXT_BASE does not match with address u-boot resides, you CPU will jump in wrong address and execute irrelevant code, leaving you with unpredictable uart output and results.

cat /proc/iomem | grep "Kernel code" will output something like: 2c0400000-2c0e031d0 : Kernel code

address range start will be your CONFIG_TEXT_BASE address.

Creating basic device tree

Will contain node for uart driver, fixed clock and chosen.

a5y17lte device tree example

exynos7880.dtsi:

// SPDX-License-Identifier: GPL-2.0+

/dts-v1/;
#include "skeleton.dtsi"
/ {
	compatible = "samsung,exynos7880";

	fin_pll: xxti {
		compatible = "fixed-clock";
		clock-output-names = "fin_pll";
		u-boot,dm-pre-reloc;
		#clock-cells = <0>;
	};

	uart2: serial@13820000 {
		compatible = "samsung,exynos4210-uart";
		reg = <0x13820000 0x100>;
		u-boot,dm-pre-reloc;
	};
};

a5y17lte.dts:

// SPDX-License-Identifier: GPL-2.0+

/dts-v1/;
#include "exynos7880.dtsi"
/ {
	compatible = "samsung,exynos7880";

    aliases {
    	console = &uart2;
    };

    chosen {
        stdout-path = &uart2;
    };
};
 
&fin_pll {
	clock-frequency = <26000000>;
};


Final changes

You may need to comment some serial driver initialization code, as it may not work properly without clock and pinctrl drivers.

Congrats! You should now get a u-boot console prompt!

Testing future u-boot builds

Once you entered in u-boot console, you may upload new u-boot build in RAM via uart and run it

Upload a file into RAM

  • on u-boot console: load{b|x|y|s} <address>. If no address specified, file will be loaded at CONFIG_SYS_LOAD_ADDR
  • exit u-boot console to release tty device

With ckermit

  • install ckermit (build it yourself with `-DSCO_OSR504` flag, for 115200+ baudrates)
  • create a script to send file:
#!/bin/ckermit +

set port /dev/ttyUSB0
set speed 921600
set carrier-watch off
set flow-control none
set prefixing all
send \%1
exit


Source

====

Icon TODO: Figure out how to transfer files from picocom with ymodem or xmodem or gkermit

====

Run uboot test version

On u-boot console: go <address of loaded file>

Hush shell

There is scripting possibility in u-boot with hush shell.

  • Build u-boot with CONFIG_HUSH_PARSER.
  • Write simple uboot_script.sh file like (Note, that empty line in you file will repeat previous command!)
for i in a b c; do
    echo $i
done
  • Make uboot image mkimage -T script -C none -n 'Demo Script File' -d uboot_script.sh uboot_script.img
  • Upload file to RAM as described above
  • Run
    • iminfo <address of loaded script> - check and print script info
    • source <address of loaded script> - run script


Booting linux with u-boot

Using initramfs in boot partition

This method is simple, and doesn't require storage driver in secondary bootloader.

Requirements:

  • device with u-boot support as secondary bootloader
  • secondary bootloader supports at least one button (for the boot variant selection)

It works by creating and flashing android boot image with your secondary bootloader replacing linux kernel, and secondary bootloader payload replacing initramfs. Multiboot implemented by having multiple payloads in initramfs replacement, and a script to pick one. Boot sequence in this case would be as follows: -

stock BL -> u-boot(replaces linux)->|-> linux
         |               ^          |-> initramfs 
         |               |          |-> dtb 
         |-> u-boot's payload(replaces initramfs)


Android booting has some peculiarities:

  • we should use dtb, loaded by stock BL(because it usually adjusts it)
  • repacking only kernel and initramfs from stock android boot image into u-boot payload

Boot sequence in such case would be:

stock BL -> u-boot(replaces linux)->|-> linux---------------|-> Android
         |               ^          |-> initramfs----------/|
         |               |                                  | 
         |-> u-boot's payload(replaces initramfs)           |
         |                                                  |
         |-> android's DTB---------------------------------/

Related merge request: boot-deploy!10

Implement multiboot involving Android in a device aport

See pmaports!3661 starqltechn example

  • check device meets requirement in #Using_initramfs_in_boot_partition
  • create source files:
    • create FIT image source file for Android android.its
    • create find_and_boot.sh script to find all images in memory, and select needed. Create FIT source file for it.
  • add android.itb file to deviceinfo_bootimg_override_initramfs option. boot-deploy script will build all provided files, and put it together in initramfs replacement file
  • aports: enable deviceinfo options; see also:
    • deviceinfo_bootimg_vendor_dependent
    • deviceinfo_bootimg_vendor_android_boot_image
    • deviceinfo_bootimg_vendor_device_tree_identifiers
  • rerun pmbootstrap init. It will prompt for vendor boot image location.
  • run pmbootstrap install && pmbootstrap export.
  • Backup boot partition
  • Flash new image

Suppose, you wrote find_and_boot.sh so that it boots Android, when volume down button pressed. To boot Android, press power on, wait for vendor logo, then press volume down.

Related merge requests:

Troubleshooting

Failed to reserve memory...

You may need to increase LMB_MAX_REGIONS config to be higher, than reserved memory regions count in your linux dts. This is because u-boot tries to reserved all reserved memory regions, and it should have regions in lmb library available.

See also