Appendix A: Using nv-U-Boot on the Samsung ARM Chromebook

Quickstart

tl;dr If you want to get nv-U-boot (a.k.a regular U-boot), on the original Samsung ARM Chromebook (not the Chromebook 2) and are impatient do the following:

# Download the latest nv_uboot-snow kernel partition
wget -O - http://commondatastorage.googleapis.com/chromeos-localmirror/distfiles/nv_uboot-snow.kpart.bz2 | bunzip2 > nv_uboot-snow.kpart

MY_SD_CARD=/dev/sdX
sudo dd if=nv_uboot-snow.kpart of=${MY_SD_CARD}2

# on boot up press and hold the space bar to drop into your favorite u-boot prompt

If you want to install this on eMMC replace the /dev/sdX with the corresponding partition. You can also dd to any partition and use cgpt to adjust which partition is selected for boot.

Or, if you prefer to use a nv-u-boot that keeps LCD on and will allow use of the simple framebuffer driver in recent upstream kernels, use this one instead:


# Download the latest nv_uboot-snow kernel partition
wget -O - http://commondatastorage.googleapis.com/chromeos-localmirror/distfiles/nv_uboot-snow-simplefb.kpart.bz2 | bunzip2 > nv_uboot-snow-simplefb.kpart

MY_SD_CARD=/dev/sdX
sudo dd if=nv_uboot-snow-simplefb.kpart of=${MY_SD_CARD}2

# on boot up press and hold the space bar to drop into your favorite u-boot prompt

Introduction

As talked about in the Custom Firmware page, Chromebooks are designed to run only verified BIOS.  This is great and super secure, but can introduce a few hurdles if you're trying to do kernel development.  Most notably:

  • The verified BIOS has its own rules for determining which kernel to boot and when to change the default.  If you are in the habit of building/installing non-working or partially-working kernels it can be a pain to get your device booting again after putting a bad kernel on.
  • The verified BIOS makes it hard to boot from external media.  Even after enabling the dev_boot_usb feature you still need to press Ctrl-U on every bootup to boot from a USB disk or SD card.
  • The verified BIOS makes it a little harder to tweak kernel command lines.  You need to rebuild a new kernel to get a new command line.
  • The officially-built verified BIOS will always silence your Linux console by changing the "console=blah" string on your command line to just "console=".
  • The verified BIOS doesn't allow way-cool workflows like grabbing your kernel from TFTP.

nv-U-Boot is "non-verified U-Boot" and at one point was also confusingly referred to as "legacy U-Boot".  The main differences between nv-U-Boot and the normal U-Boot are:

  • nv-U-Boot lets you break into the U-Boot command prompt, unleashing the awesome power of its command line.
  • nv-U-Boot can save/load its environment.
  • nv-U-Boot's default boot command doesn't use the Chrome OS logic to select the kernel and doesn't verify it.
  • nv-U-Boot doesn't properly pass things through the device tree, so that means that the crossystem command win't work.  This could be fixed but hasn't yet. [Anyone? Anyone?]

If you're doing kernel development and aren't worried about someone with physical access to your device hacking into it, you might want to consider using nv-U-Boot for your kernel development.  This page will describe how to get / use nv-U-Boot, focusing on the install method talked about in the Custom Firmware page of making your nv-U-Boot look like a developer-signed kernel.  All descriptions / examples here are relevant for the Samsung ARM Chromebook released in 2012.  It is possible that these instructions might still be relevant for other hardware.

Getting nv-U-Boot

At the moment nv-U-Boot is really just the same as the verified U-Boot with a different device tree.  nv-U-Boot is actually produced in addition to verified U-Boot by a normal build of chromeos-bootimage.  Since chromeos-bootimage is part of the standard packages built, you can just follow instructions in the Developer Guide to build packages for BOARD=daisy and then find /build/daisy/firmware/nv_image-snow.bin.

A few notes:

  • By default you'll get the tip of tree U-Boot, which should work for you.  However, if you have trouble you could try to get something closer to what actually shipped by building from the snow firmware branch.  To do this, add -b firmware-snow-2695.B to your repo init.  There have been a few bugs that have been fixed on ToT since then, though.
  • Speaking of bugs, there's some in the shipping U-Boot (37195 for the most part), the built-in U-Boot can't reliably boot a chained U-Boot that is loaded at address 0x43e00000.  That means that your chained U-Boot will need a CL to work around the issue.

nv-U-Boot with simplefb and simplified environment variables

If all you want to do is use a simple nv-u-boot so you can get a firmware prompt and load a non-Chrome OS kernel, then it might be easier for you to use the "simplefb" U-boot. The two CLs for enabling this are: Here and here.

There are a couple of things to keep in mind with it:
  • It instantiates the 'simplefb' device in the kernel device-tree, so that a kernel with the corresponding driver (CONFIG_FB_SIMPLE) will configure and use it
  • Since the framebuffer is kept alive from u-boot, it means that if you build your kernel with SYSMMU support, and use the regular FIMD graphics driver that Samsung provides, your system will crash on boot since the mapping that the simplefb is using will be invalidated when SYSMMU is enabled.
  • The simplefb firmware (linked at the top of the page above) has most of the environment variables removed, to make it easier to see what is actually used to boot the system, and edit them accordingly. The first time you boot it, you might want to run an "env default -f" to setup the environment with the default contents from the u-boot binary. NOTE: This will remove any customizations to the u-boot environment that you might have done before.

Installing nv-U-Boot (chained U-Boot method)

If you've got a Chromebook and want to get a U-Boot command line but don't want to reflash the BIOS (and risk bricking your device), this section is for you...

The Custom Firmware page describes a method to run a custom firmware without any reflashing.  That's the suggested method for running nv-U-Boot for people that don't have wires sticking out of their Chromebook.

A quick summary of the custom firmware page:
  1. The first portion of the BIOS is stored in read-only (RO) memory and can't be updated without disabling write protection on your device (a scary proposition that could brick your device).
  2. The BIOS can be updated by putting a new version in the read-write (RW) memory and the read-only version will jump there if it's present.  However, this portion of the BIOS must officially signed by Google.  There is no provision for installing a developer-signed BIOS here.
  3. The official BIOS will normally only look on the fixed disk to find a kernel to boot.  The kernel must be properly signed by the vbutil_kernel tool, but can be signed with developer keys if the device is in developer mode.
The "shortlist" of instructions for building a chained U-Boot.  This assumes you've got a Chromium OS chroot.  See the Developer Guide if you don't:

# This example uses the daisy board and snow device tree
BOARD=daisy
FDT=snow

cros_workon --board=${BOARD} start chromeos-u-boot

# Get the fixup to load U-Boot at an alternate address
pushd ~/trunk/src/third_party/u-boot/files

repo start altAddr .
git fetch \
https://gerrit.chromium.org/gerrit/chromiumos/third_party/u-boot \
refs/changes/49/39649/1 && git cherry-pick FETCH_HEAD

# If you want to apply the two patches for simplefb and simplified
# environment, then also do these two (commented-out) cherry-picks:

# git fetch \
#   https://gerrit.chromium.org/gerrit/chromiumos/third_party/u-boot \
#   refs/changes/58/49358/2 && git cherry-pick FETCH_HEAD

# git fetch \
#   https://gerrit.chromium.org/gerrit/chromiumos/third_party/u-boot \
#   refs/changes/48/50848/1 && git cherry-pick FETCH_HEAD


# Build things now that we've patched it up
emerge-${BOARD} chromeos-ec chromeos-u-boot chromeos-bootimage

# Don't leave the altAddr hack there
git checkout m/master
popd

# Produce U_BOOT file and find the text_start
dump_fmap -x /build/${BOARD}/firmware/nv_image-${FDT}.bin U_BOOT
TEXT_START=$(awk '$NF == "__text_start" { printf "0x"$1 }' \
/build/${BOARD}/firmware/System.map)


# Note::
# for the Samsung Chromebook (Daisy or snow)
# CONFIG_SYS_TEXT_BASE is 0x43e00000
# for the Samsung Chromebook 2
# CONFIG_SYS_TEXT_BASE is 0x23E00000

# Make it look like an image U-Boot will like:
# The "-a" and "-e" here are the "CONFIG_SYS_TEXT_BASE" from
include/configs/exynos5-common.h
sudo mkimage \
-A arm \
-O linux \
-T kernel \
-C none \
-a "${TEXT_START}" -e "${TEXT_START}" \
-n "Non-verified u-boot" \
-d U_BOOT /build/${BOARD}/firmware/nv_uboot-${FDT}.uimage

MY_BINARY=/build/${BOARD}/firmware/nv_uboot-${FDT}.uimage

# Sign the uimage
echo blah > dummy.txt
sudo vbutil_kernel \
--pack /build/${BOARD}/firmware/nv_uboot-${FDT}.kpart \
--keyblock /usr/share/vboot/devkeys/kernel.keyblock \
--signprivate /usr/share/vboot/devkeys/kernel_data_key.vbprivk \
--version 1 \
--vmlinuz ${MY_BINARY} \
--bootloader dummy.txt \
--config dummy.txt \
--arch arm

KPART=/build/${BOARD}/firmware/nv_uboot-${FDT}.kpart


After running the above, the ${KPART} variable has the path of a file that can be placed in a kernel partition and will boot nv-U-Boot.

SIDE NOTE: You could avoid building chromeos-bootimage above and build your own U_BOOT file by calling the right incantation of cros_bundle_firmware.  I have chosen to document as above because it seems slightly more likely to continue to working even if cros_bundle_firmware changes.

If you placed a full Chrome OS boot image onto an SD card or USB stick with "image_to_usb", you could place the above U-Boot onto it with:

NOTE: Replace /dev/sdX with your SD card/USB stick
WARNING: make sure you get it right or this will clobber your host hard disk

MY_SD_CARD=/dev/sdX
sudo dd if=${KPART} of=${MY_SD_CARD}2

If you want to install this on eMMC, it's probably best to follow the instructions on the Custom Firmware page.

Running update_kernel.sh in the presence of a chained U-Boot

If you've installed nv-U-Boot into the kernel partition of your SD card or eMMC then you must be always pass the --novboot option when updating the kernel using the ChromeOS update_kernel.sh script.  

Installing nv-U-Boot (servo method)

Most people can ignore this section, but if you've lucky enough to have a servo board attached to your Chromebook (and, I assume, write protect turned off), you can mess with the BIOS that's flashed with impunity since servo can be used to recover a bad flash.  The following commands might be useful to you.  Note that anything here is officially unsupported and if you've got a servo attached you've already voided any warranty you might have:

Build U-Boot and flash nv-U-Boot directly over an A-to-A USB cable (uses servo to select alternate boot mode).  Make sure to remove any SD cards:

USE=dev emerge-${BOARD} chromeos-u-boot
cros_bundle_firmware --add-config-int load_env 1 -d exynos5250-${FDT} -b ${BOARD} -F spi -w usb

Build a nv-U-Boot "flasher" SD card.  If you place this SD card in a machine and boot with alternate boot mode (requires servo), it will update the built-in U-Boot:

USE=dev emerge-${BOARD} chromeos-u-boot
cros_bundle_firmware --add-config-int load_env 1 -d exynos5250-${FDT} -b ${BOARD} -F spi -w sd:.

Build a nv-U-Boot SD card for booting directly.  If you place this SD card in a machine and boot with alternate boot mode (requires servo), the machine will boot U-Boot from the card.  The built-in BIOS will not be updated.

USE=dev emerge-${BOARD} chromeos-u-boot
cros_bundle_firmware --add-config-int load_env 1 -d exynos5250-${FDT} -b ${BOARD} -w sd:.

Tips and Tricks with nv-U-Boot

The default nv-U-Boot that's built for the Samsung ARM Chromebook has a bunch of environment variables that can be dizzying to figure out.  Some of the confusion stems from the fact that U-Boot stores "scripts" in environment variables.  These scripts can't pass parameters (so need to use other environment variables to communicate with each other) and are sorted alphabetically with data.  Ick!

To help you get some use out of the scripts, here are a few usage scenarios and explanations.  You might also want to look at some of the documentation in the source code, which is slightly easier to figure out.

Setting a non-zero bootdelay

In order to make nv-U-Boot useful you need a way to break into it.  By default, nv-U-Boot has a $bootdelay of 0.  You can break into U-Boot if you hold down the space bar while you're booting, but that can be a pain.  $bootdelay of 1 or higher is useful if you end up breaking into U-Boot a lot.

Saving your environment and getting back to the default

U-Boot has the ability to load/save your environment variables to SPI flash and loads up saved values on every bootup.  That means you can permanently change the way that U-Boot boots up your device by tweaking a few variables and saving them.  To save the current environment just use the saveenv command.  If you messed up your environment and want to get back to sane defaults, you can always do it with env default -f.

Adding to the kernel command line

The easiest way to add boot arguments is to use the $dev_extras environment variable.  You can clobber the obsolete "daisy" argument that's there by default.  So if you wanted to enable slub debugging on your kernel, you could just use:

setenv dev_extras slub_debug=FZPUA

Don't forget to saveenv afterwards if you want to apply these options every time.

Adjusting the kernel command line (doing more than just adding)

If you want to tweak kernel command line in interesting ways, you should be aware that the default boot scripts will clobber any changes you make to $bootargs.  If you really want to tweak things you should modify the $regen_all "script".  All of the standard boot scripts call this so that they can insert their various parameters into the kernel command line.

The default boot flow

By default the standard $bootcmd is run non_verified_boot.  That does the following:
  • Try to boot from TFTP/NFS (if a compatible USB ethernet adapter is present and $tftpserverip and perhaps $nfsserverip is set).
  • Try to boot from USB 2.0 using the "disk boot script" + $ext2_boot.
  • Try to boot from SD card (mmc1) using the "disk boot script" + $ext2_boot.
  • Try to boot from EMMC (mmc0) using the "disk boot script"+ $ext2_boot.

The "disk boot script" is designed to allow A-B swapping for the image_to_live.sh script, though it could be useful in other cases.  It's goal is to set the $kernelpart and $rootpart environment variables to point to the proper filesystem for getting the kernel (verified) and root filesystem (contains non-verified kernel).  The script is stored in the environment variable $run_disk_boot_script and loads up a U-Boot script from the FAT filesystem on partition 12 (aka $script_part, which is in hex).  The script named /u-boot/boot.scr.uimg (aka $script_img) should be a "compiled" u-boot script that adjusts the $kernelpart and $rootpart environment variables.  See the u-boot-scripts project for details.

The $ext2_boot command loads the kernel that's stored at /boot/vmlinux.uimg (aka $cros_bootfile) in the root filesystem (pointed to by $rootpart and $kernelpart is ignored).  It then adjust the command line parameters so that the kernel knows which disk/partition contains the root filesystem (requires kernel and U-Boot to agree about device IDs) and boots.

Booting from a backup kernel

A practical example of the adjusting the boot flow is to boot a backup kernel.  If you prepare ahead of time and make a backup kernel, like:

mount -oremount,rw /
cp /boot/vmlinux.uimg /boot/vmlinux.uimg.bak
sync
mount -oremount,ro /

Then you can always interrupt your boot flow (hold space at bootup) and boot from a backup kernel like this:

setenv cros_bootfile /boot/vmlinux.uimg.bak
boot

Running vboot, even though you've got nv-U-Boot

This is really only applicable to people who have removed the write protect (Danger, Will Robinson! Danger!) and flashed nv-U-Boot (rather than used the trick to put nv-U-Boot in the place of the signed kernel).  If you've done that, you can actually boot the device in a way that's very similar to the standard verified boot (you'll get the kernel from the fixed disk, verify it, update the priority, and boot with the standard command line parameters).  Just use the vboot_twostop command.

 
Comments