Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Alper Nebi Yasak edited this page Feb 11, 2022 · 14 revisions

About this repository

This is a personal repository for me to keep patches in development, which currently includes making U-Boot work on some chromebooks. This wiki page is just a convenient place for me to write how I build it for my chromebook.

For now, I'm only testing patches on a gru-kevin because that's what I have; and keeping a branch for gru-bob as it's a similar board, in case anyone wants to try testing on it.

I'm also trying to automate building "Chrome OS Kernel" and "RW_LEGACY" images for chromebooks that would let the Chrome OS firmware chainload into U-Boot. I may occasionally push out these kind of images, but I have no idea how good U-Boot actually works on those boards even if I pack them successfully in these formats.

Gru-Kevin status

I've found out how to make this work as a "Chrome OS Kernel" partition image, so that is the easiest way to use this. It also happens to be the best working way for now. I even automated building the image and will try to upstream it.

One more way this can work is by chainloading from depthcharge via its RW_LEGACY functionality. It slowly clears the screen, waits about half a minute, turns the screen off and back on, then proceeds to show U-Boot autoboot messages and boot into Debian.

Another way is as the full platform firmware on the SPI flash chip. U-Boot by itself appears to work well enough with some hacks, but has some issues like not being able to power off when shutting down. It's also possible to embed U-Boot into a coreboot build and use that, which doesn't seem to have that issue at the least.

Other RK3399 boards can boot U-Boot as platform firmware from MMC or microSD card in some configurations. I tried a few things like enabling MMC drivers in SPL and specifying boot order in the device-tree, but I couldn't get it to work here. It's hard for me to work on SPL as I don't have a serial console, hopefully someone else will fix it.

Building U-Boot

See build instructions for U-Boot. I use buildman which is part of the U-Boot repository:

tools/buildman/buildman --fetch-arch aarch64
tools/buildman/buildman -o "build/chromebook_kevin" --boards="chromebook_kevin" -w

I usually use the BL31.elf from Debian's arm-trusted-firmware package while building, by specifying it in ~/.buildman.conf:

[make-flags]
rk3399 = BL31=/usr/lib/arm-trusted-firmware/rk3399/bl31.elf
chromebook_bob = ${rk3399}
chromebook_kevin = ${rk3399}

You can also build arm-trusted-firmware yourself, but if you don't specify a working BL31.elf file the U-Boot you built will not work properly (if it works at all).

Building coreboot with U-Boot

On the U-Boot side, enable CONFIG_REMAKE_ELF to build a u-boot.elf file that happens to work as a coreboot payload. The default value of CONFIG_SYS_TEXT_BASE conflicts with some coreboot stage, it has to be changed or will cause an error later. I tried 0x18000000 as I saw that as the CONFIG_BASE_ADDRESS in the Depthcharge sources for my board.

I had already enabled both CONFIG_LINUX_KERNEL_IMAGE_HEADER and CONFIG_POSITION_INDEPENDENT for U-Boot while testing all my coreboot builds, they might or might not be actually necessary. I don't know.

Then, see build instructions for coreboot. Select vendor and board, maybe set CONFIG_ARM64_BL31_EXTERNAL_FILE to the BL31.elf you used for U-Boot, select CONFIG_PAYLOAD_ELF and set CONFIG_PAYLOAD_FILE to the u-boot.elf you built.

Packing as Chrome OS Kernel

On ARM64 boards, you must enable CONFIG_LINUX_KERNEL_IMAGE_HEADER and CONFIG_POSITION_INDEPENDENT for your board. Build as above, then create a FIT image from u-boot.bin and u-boot.dtb files, and sign it with vbutil_kernel:

# Create FIT image
mkimage -f auto -A arm64 -O linux -C none -n "U-Boot" \
    -b "u-boot.dtb" -d "u-boot.bin" "u-boot-depthcharge.itb"

# Create dummy files so vbutil_kernel doesn't complain
dd if=/dev/zero of=dummy.bin bs=512 count=1

# Sign and pack into the format Depthcharge wants
vbutil_kernel --version 1 --arch aarch64 \
    --vmlinuz "u-boot-depthcharge.itb" \
    --config "dummy.bin" --bootloader "dummy.bin" \
    --keyblock "/usr/share/vboot/devkeys/kernel.keyblock" \
    --signprivate "/usr/share/vboot/devkeys/kernel_data_key.vbprivk" \
    --pack "u-boot-depthcharge.kpart"

On 32-bit ARM boards the precise position of u-boot.bin in the FIT appears to be important, but I assume CONFIG_POSITION_INDEPENDENT gets rid of that restriction as well. (Tell me if it doesn't and I'll update things here.)

Using as Chrome OS Kernel

First make sure to enable developer mode. Then you need to enable booting non-Google-signed kernel partitions, either from Chrome OS (by launching a crosh shell) or Linux:

# You only need to do this once
sudo crossystem dev_boot_signed_only=0
sudo crossystem dev_boot_usb=1

Create or find an unused "Chrome OS Kernel" GPT partition, write the u-boot-depthcharge.kpart file to it directly, and use cgpt to set partition flags to try it only on next boot:

# Choose a partition from the output, say /dev/mmcblk8p9
sudo cgpt find -v -t kernel

# Write to partition
sudo dd if=u-boot-depthcharge.kpart of=/dev/mmcblk8p9

# Mark it as prioritized, temporarily bootable
sudo cgpt add -S 0 -T 1 -i 9 /dev/mmcblk8
sudo cgpt prioritize -i 9 /dev/mmcblk8

If it boots fine, you can mark it as permanently bootable:

sudo cgpt add -S 1 -T 1 -i 9 /dev/mmcblk8
sudo cgpt prioritize -i 9 /dev/mmcblk8

Packing as RW_LEGACY

The Chrome OS firmware/bootloader can chainload another bootloader, but it must be packed in to a CBFS image. See Chromium OS developer mode docs for more info.

For that, you'll need the cbfstool from the coreboot project. If your OS hasn't packaged coreboot utils yet, follow the coreboot tutorial and it should be available as build/util/cbfstool/cbfstool after you build coreboot.

The easy way

Set CONFIG_REMAKE_ELF=y in your board's defconfig file (or its config headers if the option is still not migrated to Kconfig), build, and run:

cbfstool u-boot.elf.cbfs create -s "$((2 * 1024 * 1024)" -m arm64
cbfstool u-boot.elf.cbfs add-payload -f u-boot.elf -n payload -c lzma

The resulting u-boot.elf.cbfs is the RW_LEGACY image.

(Or, you can set CONFIG_OF_EMBED=y and use the u-boot file, but that's more hackish than this.)

The hard way

See the build_cb_legacy_uboot.sh from Chromium OS, which merges u-boot with u-boot.dtb manually (and hackishly). I've slightly modified it to run the correct programs (objdump etc.) and to use arm64/aarch64 for cbfstool/objcopy arch parameters:

merge_uboot() {
    local uboot="$1"
    local dtb="$2"
    local output="$3"
    local arch="$4"
    local uboot_info="$(objdump -f "${uboot}")"
    local section_info="$(objdump -h "${uboot}")"
    local format="$(echo "${uboot_info}" | grep "file format " |
                    sed 's/.*file format //')"
    local entry="$(echo "${uboot_info}" | grep "^start address " |
                   sed 's/^start address //')"
    # Assume base address is the VMA of first section.
    local base="0x$(echo "${section_info}" | grep '^  0 ' |
                    awk '{print $4}')"
    # Convert into binary and concatenate DTB data.
    objcopy -O binary "${uboot}" "${output}"
    cat "${dtb}" >>"${output}" || die "Failed to append ${dtb}"
    # Build new ELF.
    objcopy -B "${arch}" -I binary -O "${format}" \
        --change-section-address .data="${base}" \
        --set-start "${entry}" "${output}"
    elfedit --output-type exec "${output}" \
        || die "Failed to change ELF type"
    # Set flags and construct program header (must done after elfedit)
    objcopy --set-section-flags .data=contents,alloc,load,code "${output}"
}

pack_cbfs() {
    local payload="$1"
    local output="$2"

    cbfs_size=$((2 * 1024 * 1024))
    cbfstool "$2" create -s "$cbfs_size" -m arm64
    cbfstool "$2" add-payload -f "$1" -n payload -c lzma
}

merge_uboot u-boot u-boot.dtb u-boot-dtb aarch64
pack_cbfs u-boot-dtb u-boot-dtb.cbfs

The resulting u-boot-dtb.cbfs is the RW_LEGACY image. There is really no need to do this manually as CONFIG_REMAKE_ELF handles it for you. I might remove this section eventually.

Flashing RW_LEGACY

First make sure to enable developer mode. Then you need to enable chainloading a RW_LEGACY payload, either from Chrome OS (by launching a crosh shell) or Linux:

# You only need to do this once
sudo crossystem dev_boot_legacy=1

To flash within Chrome OS, launch a crosh shell and run something like:

# This is a Chrome OS specific fork of flashrom, which can
# directly flash the cbfs file to the RW_LEGACY region
sudo flashrom -w -i RW_LEGACY:/path/to/u-boot-dtb.cbfs

Flashing from Linux is possible as well but slightly differently:

# Mainline flashrom needs files to match the SPI flash, so
# make an 8MiB file with RW_LEGACY at where it should be
dd if=u-boot-dtb.cbfs of=u-boot-dtb.cbfs.rom bs=2M seek=3 count=1
sudo flashrom -p linux_mtd --fmap -i RW_LEGACY -w u-boot-dtb.cbfs.rom

Flashing as full firmware

If you have disabled write-protect on your board, you can flash U-Boot as the full firmware. You can either flash internally or externally, but be aware that:

  • If you flash a broken build, you will need to reflash externally.
  • The SPI flash chip on gru-kevin runs on 1.8V.

The necessary SPI flash image is built automatically, as u-boot.rom in U-Boot builds, and build/coreboot.rom in coreboot builds. To flash:

# Make sure you have a backup of your original firmware, it
# contains irreplaceable data e.g. serial number, ethernet MAC
sudo flashrom -p <programmer> -r depthcharge.rom
sudo flashrom -p <programmer> -v depthcharge.rom

# Using mainline flashrom
sudo flashrom -p <programmer> -w u-boot.rom
sudo flashrom -p <programmer> -v u-boot.rom

Getting Debian to boot

U-Boot implements EFI to a degree that it can boot GRUB. On Debian you can create an EFI partition, add it to /etc/fstab, install the grub-efi-arm64 package and run sudo grub-install --no-nvram --force-extra-removable to get a system to a EFI-bootable state.

If U-Boot can't find an EFI binary to boot, it falls back to looking for an extlinux.conf. On Debian, install the u-boot-menu package and run u-boot-update, which creates /boot/extlinux/extlinux.conf. Mark the partition that includes this file as "Legacy BIOS Bootable" from a disk management utility (e.g. gnome-disks, gparted).