Fallback mechanism

From coreboot
Revision as of 17:37, 17 August 2014 by GNUtoo (Talk | contribs)

Jump to: navigation, search

Introduction

The fallback mecanism permits to be able to use two different prefix(normal/ and fallback/) for the romstage,ramstage and payload, in the same coreboot image.

The switch between the two prefixes can be governed by an nvram configuration parameter.

Uses cases

  • Test new images way faster: if the image doesn't boot it will fallback on the old known-working image and save a long reflashing procedure
  • Test new images more safely: Despite of the recommendations of having a way to externally reflash, many new user don't. Assuming that the user don't screw up the fallback/ procedure (which adds a layer of complexity) he can test new images more safely because it will fallback on the known good image.
  • More compact testing setup: Since reflashing tools are not mandatory anymore, the tests can be done with less voluminous hardware, which means that the test setup is easier to bring with you while travelling.

How it works (summary)

Coreboot will switch to fallback/ if the boot count is higher than CONFIG_MAX_REBOOT_CNT (or if normal/ isn't present).

Coreboot increments the reboot count at each boot.

Here, clearing the boot count is delegated to what is run after coreboot.

To get the maximum safety out of it, clearing the boot count after the last step of the boot is advised.

Example of use

For instance once the system is fully booted, a systemd unit can reset the boot count.

That way if the coreboot changes makes it impossible to boot a linux kernel or even if GNU/Linux can't fully boot, the boot count won't be reset.

Then the user will power off the computer, and at the next boot CONFIG_MAX_REBOOT_CNT will hopefully be reached. Then coreboot will boot on the good known working image and the boot will complete.

At that point the user is expected to reflash a good image in order not to go in normal/ again at the next boot.

Current limitations

  • scripts exist only for the systemd init system, but they are easy to adapt to other init systems
  • suspend/resume systemd scripts not written yet
  • some issues can arrise when the nvram layout is not the same between normal/ and fallback/
  • The number of failed boot is 3 by default (for all boards that don't set CONFIG_MAX_REBOOT_CNT)
  • In order to fully boot, some boards do reboot once during the boot procedure. The issue is that it reboot conditionally, and no code has been written yet to take that into account.

Example of use with systemd

  • The code dependencies can be found in gerrit

Mandatory configuration (in make menuconfig)

You will have to make two configurations:

  • one for the fallback image
  • one for the normal image

First image

start configuring the first image with:

make menuconfig

Then configure it like that:

Go in the following menu:

Architecture (x86)  --->

And then select that:

Bootblock behaviour (Switch to normal if CMOS says so)  --->

Which will bring that menu:

( ) Always load fallback
(X) Switch to normal if CMOS says so

Select the "Switch to normal if CMOS says so" line like described above.

In order to know if your computer booted correctly the last time, coreboot reads it in the nvram. There are two ways to make it know that it booted fine the last time:

  • The automatic way, which happens inside the ramstage of coreboot.
  • The manual way, which happens when you want after the ramstage.

If you want it to happen after the ramstage Select the following menu:

General setup  --->

And inside select the following if you want the manual way:

[*] Keep boot count

Or don't select it if you want the automatic way:

[ ] Keep boot count

Then choose the number of times you want it to try to boot, before switching back to fallback/

(1) Number of failed boot attempts before switching back to fallback/

Note that the minimum number could be device specific. Setting the minimum to 1 on the Lenovo x60 worked well.

In any case, make sure that you have:

(fallback-mode) Local version string
(fallback) CBFS prefix to use

Verify that you have the following in .config (that make menuconfig just generated if you followed the previous instructions correctly)

CONFIG_X86_BOOTBLOCK_NORMAL=y
CONFIG_BOOTBLOCK_SOURCE="bootblock_normal.c"

And that you have:

CONFIG_LOCALVERSION="fallback-mode"
CONFIG_CBFS_PREFIX="fallback"

If you selected "Keep boot count", also verify that you have:

CONFIG_KEEP_BOOT_COUNT=y

At the end copy the .config to defconfig-fallback (that will erase the file named defconfig-fallback if there was one):

cp .config defconfig-fallback

Second image

After configuring the first image, you should configure the second one. use "make menuconfig" again to change the current configuration in .config (you already copied it to defconfig-fallback, so you will only modify a copy of it).

make menuconfig

Then go in "General setup"

General setup  --->

And modify the prefix and the version string to look like that:

(normal-mode) Local version string
(normal) CBFS prefix to use

So that the second image that we will build later will be put in the "normal/" prefix and not in the "fallback/" one.

Then go in Architecture:

Architecture (x86)  --->

And enable the "Update existing coreboot.rom image" option:

[*] Update existing coreboot.rom image

At the end copy the .config to defconfig-normal (that will erase the file named defconfig-normal if there was one):

cp .config defconfig-normal

Pseudo-diff

Then compare the two resulting configurations to be sure of what you did:

$ diff -u defconfig-fallback defconfig-normal

The output should look a bit like that but with more context lines(the lines not starting with a "+" or a "-"):

--- defconfig-fallback	2013-10-26 22:27:19.471326092 +0200
+++ defconfig-normal	2013-10-26 22:26:44.471328732 +0200 
-CONFIG_LOCALVERSION="fallback-mode"
-CONFIG_CBFS_PREFIX="fallback"
+CONFIG_LOCALVERSION="normal-mode"
+CONFIG_CBFS_PREFIX="normal"
-# CONFIG_UPDATE_IMAGE is not set
+CONFIG_UPDATE_IMAGE=y

Compilation

Build script

This is a build script for the first build that will contains both /fallback and /normal:

#!/bin/sh
# In the cases where this work is copyrightable, it falls under the GPLv2
# or later license that is available here:
# https://www.gnu.org/licenses/gpl-2.0.txt

#verbose="V=1"

die() {
	echo
	echo "!!!! Compilation failed !!!!"
	exit 1
}

success() {
	echo
	echo "!!!! Compilation finished !!!!"
	echo
}

separator() {
	echo
	echo "!!!! First prefix compilation finished !!!!"
	echo
}

fallback() {
	make clean || die

	#fallback image
	cp defconfig-fallback .config  || die
	make ${verbose}  || die
	./build/cbfstool ./build/coreboot.rom add -f .config -n config-fallback -t raw  || die

	#because it could be re-included it in the second build...
	#./build/cbfstool ./build/coreboot.rom remove -n etc/ps2-keyboard-spinup  || die
	#./build/cbfstool ./build/coreboot.rom remove -n pci8086,109a.rom  || die
}


save_clean_and_restore_fallback() {
	cp ./build/coreboot.rom ./build-save/coreboot.rom.fallback || die

	make clean || die
	mkdir -p build/
	cp ./build-save/coreboot.rom.fallback ./build/coreboot.rom || die

	separator
}

normal() {
	#normal image
	cp defconfig-normal .config  || die
	make ${verbose} || die
	./build/cbfstool ./build/coreboot.rom add -f .config -n config-normal -t raw  || die

}

add_external_cbfs() {
	#Add the remaining files
	./build/cbfstool ./build/coreboot.rom add -f /home/gnutoo/x86/ipxe/src/bin/8086109a.rom -n pci8086,109a.rom -t raw || die
}

fallback
save_clean_and_restore_fallback
normal
add_external_cbfs
success

Update script

Before using that script, you should do:

make menuconfig #change the options if you need it, and save

And:

cp .config defconfig-normal-update

This script is meant for updating an existing coreboot.rom image while not touching the fallback/ part

#!/bin/sh
# In the cases where this work is copyrightable, it falls under the GPLv2
# or later license that is available here:
# https://www.gnu.org/licenses/gpl-2.0.txt

#verbose="V=1"

die() {
	echo
	echo "!!!! Compilation failed !!!!"
	exit 1
}

success() {
	echo
	echo "!!!! Compilation finished !!!!"
	echo
}

separator() {
	echo
	echo "!!!! First prefix compilation finished !!!!"
	echo
}

build_cbfstool() {
	make -C util/cbfstool
}
save_clean_and_restore_image() {
	if [ -f ./build/coreboot.rom ] ; then
		cp ./build/coreboot.rom ./build-save/coreboot.rom.fallback || die
	fi

	make clean || die
	mkdir -p build/
	cp ./build-save/coreboot.rom.fallback ./build/coreboot.rom || die

	separator
}

remove_normal_from_image() {
	./util/cbfstool/cbfstool ./build/coreboot.rom remove -n normal/romstage || die
	./util/cbfstool/cbfstool ./build/coreboot.rom remove -n normal/coreboot_ram || die
	./util/cbfstool/cbfstool ./build/coreboot.rom remove -n normal/payload || die
	./util/cbfstool/cbfstool ./build/coreboot.rom remove -n config-normal
}

normal() {
	#normal image
	cp defconfig-normal-update .config  || die
	make ${verbose} || die
	./util/cbfstool/cbfstool ./build/coreboot.rom add -f .config -n config-normal -t raw  || die

}

remove_external_cbfs() {
	./util/cbfstool/cbfstool ./build/coreboot.rom remove -n pci8086,109a.rom
}

re_add_external_cbfs() {
	./util/cbfstool/cbfstool ./build/coreboot.rom add -f /home/gnutoo/x86/ipxe/src/bin/8086109a.rom -n pci8086,109a.rom -t raw || die
}

build_cbfstool
save_clean_and_restore_image
remove_normal_from_image
remove_external_cbfs
normal
re_add_external_cbfs
success

Use it

If you chose the following option:

 [*] Keep boot count

Then you or something will need to tell coreboot that the computer booted correctly. Here are some example scripts.

set-fallback-1.sh

#!/bin/sh
nvramtool -w boot_option=Fallback
nvramtool -w last_boot=Fallback
nvramtool -w reboot_bits=1

set-normal-0.sh

#!/bin/sh
nvramtool -w boot_option=Normal
nvramtool -w last_boot=Normal
nvramtool -w reboot_bits=0

get-nvram.sh

#!/bin/sh
nvramtool -a | grep -e boot_option -e last_boot -e reboot_bits

Systemd units

/etc/systemd/system/coreboot-booted-ok.service:

#  This file is not part of systemd.
#
#  this file is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Tell coreboot that the computer booted fine.
DefaultDependencies=no
Wants=display-manager.service
After=display-manager.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/nvramtool -w boot_option=Normal
ExecStart=/usr/local/sbin/nvramtool -w last_boot=Normal
ExecStart=/usr/local/sbin/nvramtool -w reboot_bits=0

[Install]
WantedBy=multi-user.target

Update build script

#!/bin/sh

if [ $# -ne 1 ] ; then
  echo "Usage $0 <image>"
  exit 1
fi

image="$1"

die() {
  echo "Failed"
  exit 1
}

cbfs_remove() {
  file=$1
  ./util/cbfstool/cbfstool ./build/coreboot.rom remove -n ${file} 
}

make oldconfig || die
make clean || die
mkdir build/ || die

cp ${image} ./build/coreboot.rom || die

cbfs_remove normal/romstage
cbfs_remove normal/coreboot_ram
cbfs_remove normal/payload
cbfs_remove config
cbfs_remove etc/grub.cfg

make || die

Old Howto (will be replaced)

  • build the coreboot image as usual, it will produce an image in build/coreboot.rom
  • After the first build run:
make menuconfig
  • Optionally change the payload.
  • Go in
General setup  --->
  • Change:
(fallback) CBFS prefix to use

To:

(normal) CBFS prefix to use
  • Go back to the main menu and select:
Architecture (x86)  --->

select the following option:

[*] Update existing coreboot.rom image

Exit and save and rebuild...

The image will then have fallback and normal:

Name                           Offset     Type         Size
cmos_layout.bin                0x0        cmos_layout  1776
pci1002,9710.rom               0x740      optionrom    60928
fallback/romstage              0xf580     stage        92823
fallback/coreboot_ram          0x26080    stage        66639
fallback/payload               0x36540    payload      54976
config                         0x43c40    raw          4455
normal/romstage                0x44e00    stage        92823
normal/coreboot_ram            0x5b8c0    stage        68820
normal/payload                 0x6c600    payload      159949
(empty)                        0x93700    null         442136