blob: 17f0bbf2dd79362bc40af64764397cfb75d9dbc5 [file] [log] [blame]
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Provides alert messages in boot stage, called by chromeos_startup.
# Two instances of this script should never be run in parallel: the alert
# animations will fight with each other, and there is a potential race in the
# emission of the boot-alert-request signal (see
# Since this script only provides messages, never abort.
set +e
# Prints usage help for commands supports
usage_help() {
echo "Usage: $0 mode [arg ...]
warn_dev: Message for warning about developer mode on normal firmware.
Arg #1: (optional, default=30) Countdown in seconds before continue
enter_dev: Message for entering developer mode from non-dev.
Arg #1: (optional, default=30) Countdown in seconds before continue
leave_dev: Message when leaving developer mode.
Arg: none
update_firmware: Message before starting firmware update.
Arg: none
wipe: Message when starting to erase stateful partition.
Arg #1: Text message file to show.
power_wash: Message when users wants to wipe the stateful partition.
Arg: none
self_repair: Message when starting to rebuild stateful partition.
dev_fwcheck: Message when staring with developer firmware, to check if there
is available updates.
block_devmode: Message shown to indicate that dev mode is blocked by request
of the device owner.
Arg: none
# Prints out system locale by searching cached settings or VPD.
find_current_locale() {
# TODO(hungte) Find some better way other than hard coding file path here.
local state_file='/mnt/stateful_partition/home/chronos/Local State'
local locale=""
if [ -f "$state_file" ]; then
locale="$(grep -w '"app_locale":' "$state_file" |
sed 's/.*"\([^"]*\)",$/\1/')" || locale=""
if [ -z "${locale}" ]; then
locale="$(cros_region_data -s locales)"
echo "${locale}"
# Determine the right console. On Freon systems, the default TTY (/dev/tty1) may
# not exist until we've invoked Frecon (via display_boot_message) and would
# cause problems when we try to write then read (for example, warn_dev). To
# prevent that, fall back to /dev/null if not available.
setup_tty() {
[ -e "${TTY}" ] || TTY=/dev/null
# Shows boot messages in assets folder on screen center if available.
# Arguments: message in /usr/share/chromeos-assets/text/boot_messages/$locale
show_assets_message() {
local message="$1"
local locale locale_list
# Build locale list
locale="$(find_current_locale)" || locale=""
# Starting from R34, the initial_locale from VPD may have multiple values,
# separated by ',' -- and we only want to try the primary one.
while [ "${locale%[-_]*}" != "$locale" ]; do
locale_list="$locale_list $locale"
locale_list="$locale_list en-US en"
if display_boot_message "${message}" "${locale_list}"; then
# Frecon may create the text terminal so we want to setup TTY again.
# Display the message code itself as fallback.
echo "${message}" >>"${TTY}"
# Prints the two byte hex code of the matched char or
# exists non-zero on timeout. It reads from the tty in arguments.
# Arguments: tty time_in_seconds two_byte_hex_match_1 two_byte_hex_match_2 ...
match_char_timeout() {
local tty="$1"
local delay_secs="$2"
local input=''
local match=''
local start_time=$(date +%s)
local stop_time=$((start_time + delay_secs))
local tty_config=$(stty -g -F "${tty}")
stty raw -echo cread -F "${tty}"
while [ $delay_secs -gt 0 ]; do
input=$(timeout -s KILL ${delay_secs}s head -c 1 "${tty}")
[ $? -eq 137 ] && break # Timed out.
input=$(printf "%02x" "'$input")
for char in "$@"; do
if [ "$input" = "$char" ]; then
[ -n "$match" ] && break
delay_secs=$((stop_time - $(date +%s) ))
# Restores the tty's settings.
stty $tty_config -F "${tty}"
[ -z "$match" ] && return 1
printf "$match"
return 0
# Returns if current device is using virtual developer switch.
has_virtual_dev_switch() {
local vdat_flags="$(crossystem vdat_flags || echo 0)"
[ "$((vdat_flags & VBSD_HONOR_VIRT_DEV_SWITCH))" != "0" ]
# Prints message when in developer mode
# Argument: time to countdown (in seconds)
mode_warn_dev() {
local delay_secs="${1:-30}"
# In bash and dash, the "NAME=VAL program" will set a temporary environment
# variable. However if the "program" is a shell function and then calls a
# standalone shell script, only bash can pass the variable properly.
# To work around that, here we have to explicitly set the variable with
# "export" and use a sub shell.
(export IMAGE_FONT_NAME="monospace"; show_assets_message "warn_dev")
# Read a space bar or Ctrl+D or timeout.
local input=$(match_char_timeout "${TTY}" "$delay_secs" 04 20)
local exit_code=$?
tput clear >>"${TTY}"
# If we timed out, we're done.
[ $exit_code -ne 0 ] && return 0
case "$input" in
"04") # Ctrl+D
# Done.
"20") # Spacebar
crossystem recovery_request=1
# To prevent the system from continuing to boot.
sleep infinity
return 0
# Prints message when entering developer mode
# Argument: time to countdown (in seconds)
mode_enter_dev() {
local delay_secs="${1:-30}"
if has_virtual_dev_switch; then
show_assets_message "enter_dev1_virtual"
show_assets_message "enter_dev1"
local format='\r %-30s'
for dev_count_down in $(seq $delay_secs -1 1); do
# Trailing spaces must exist to clear previous message when the printed
# counter width changed (ex, 100->99).
# TODO(hungte) merge this with assets messages so it can be localized.
printf "$format" "Starting in $dev_count_down second(s)..." >>"${TTY}"
sleep 1
# Count-down
tput clear >>"${TTY}"
show_assets_message "enter_dev2"
# TODO(wad,wfrichar) Request a root password here.
# TODO(wad,reinauer) Inform the user of chromeos-firmwareupdate --mode=todev
# Prints message when leaving developer mode
mode_leave_dev() {
show_assets_message "leave_dev"
# Prints messages before starting firmware update
mode_update_firmware() {
show_assets_message "update_firmware"
# Prints message before starting to erase stateful partition (wipe).
# Argument: Text file for message to show.
mode_wipe() {
local message_file="$1"
if display_boot_message "show_spinner" "${message_file}"; then
# Frecon may create the text terminal so we want to setup TTY again.
echo "Wiping" >>"${TTY}"
# Prints messages before starting user-initiated wipe.
mode_power_wash() {
show_assets_message "power_wash"
# Prints message before starting to rebuild a corrupted stateful partition.
mode_self_repair() {
show_assets_message "self_repair"
# Prints message when starting with developer firmware, to check if there's
# available updates for firmware.
mode_dev_fwcheck() {
# TODO(hungte) Find some better way to store firmware value, like using
# /etc/lsb-release. Currently the firmware updater may contain fields like
# TARGET_* in the beginning of updater script.
local TARGET_FWID=""
local TARGET_ECID=""
# The magic value 40 is verified on current updaters (using line 23-24)
eval "$(head -40 /usr/sbin/chromeos-firmwareupdate |
grep '^ *TARGET_..ID=')"
if [ -z "$TARGET_FWID" -a -z "$TARGET_ECID" ]; then
local fwid="$(crossystem fwid)"
local ec_info
ec_info="$(mosys -k ec info 2>/dev/null)" || ec_info=""
local ecid="$(fw_version="Unknown"; eval "$ec_info"; echo "$fw_version")"
# Ignore known firmware images carried by updaters with multiple images
# TODO(hungte) Replace this by a "compatible list" in updater if such request
# becomes a common feature.
case "$(mosys platform name 2>/dev/null):$ecid" in
"ZGB:0.14" | "Alex:00VFA616" )
local notify_update=0
if [ "$TARGET_FWID" != "$fwid" ] &&
[ "$TARGET_FWID" != "IGNORE" ]; then
echo "
System firmware update available: [$TARGET_FWID]
Currently installed system firmware: [$fwid]
" >>"${TTY}"
if [ "$TARGET_ECID" != "$ecid" ] &&
[ "$TARGET_ECID" != "IGNORE" ]; then
echo "
EC firmware update available: [$TARGET_ECID]
Currently installed EC firmware: [$ecid]
" >>"${TTY}"
if [ $notify_update -ne 0 ]; then
echo "
Firmware auto updating is disabled for developer mode. If you want to
manually update your firmware, please run the following command from a
root shell:
sudo chromeos-firmwareupdate --force --mode=recovery
" >>"${TTY}"
# Prints a message telling the user that developer mode has been disabled for
# the device upon request by the device owner. Depending on hardware support,
# the system switches back to verified boot mode automatically or prompts the
# user to do so. The system reboots after the user confirms by pressing space or
# after timeout.
mode_block_devmode() {
local delay_secs=30
if has_virtual_dev_switch; then
show_assets_message "block_devmode_virtual"
# Leave the notification on the screen for 5 minutes to increase chances
# the user actually sees it before shutting down the device.
show_assets_message "block_devmode"
# Read a space bar or timeout.
local input=$(match_char_timeout "${TTY}" "${delay_secs}" 20)
tput clear >>"${TTY}"
if has_virtual_dev_switch; then
# Return to verified mode.
crossystem disable_dev_request=1
# Shut down instead of rebooting in a loop.
# To prevent the system from continuing to boot.
sleep infinity
# Main initialization and dispatcher
main() {
# process args
if [ $# -lt 1 ]; then
exit 1
local mode="$1"
# For headless devices, we want to provide some messages so people can know
# what goes wrong.
local output
for output in /dev/kmsg /dev/console; do
(echo "chromeos-boot-alert: ${mode}" >>"${output}") 2>/dev/null || true
if [ -x /usr/sbin/board_boot_alert ]; then
# Allow overriding boot-alert behavior (usually for headless devices).
/usr/sbin/board_boot_alert "${mode}" "$@" && exit
# Wait until the boot-alert-ready abstract job has started, indicating that
# it's safe to display an alert onscreen.
if ! initctl status boot-alert-ready | grep -q running; then
initctl emit boot-alert-request
# Light up the screen if possible.
backlight_tool --set_brightness_percent=100 || true
# Hides cursor and prevents console from blanking after long inactivity.
setterm -cursor off -blank 0 -powersave off -powerdown 0 >>"${TTY}" || true
case "$mode" in
"warn_dev" | "enter_dev" | "leave_dev" | "dev_fwcheck" | \
"update_firmware" | "wipe" | "power_wash" | "self_repair" | \
"block_devmode" )
mode_"$mode" "$@"
* )
exit 1
# Main Entry
main "$@"