| # Copyright 2017 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| description "Firmware updating task before UI" |
| author "chromium-os-dev@chromium.org" |
| |
| oom score -100 |
| |
| start on starting ui |
| |
| # This job blocks the start of UI and calls all the tasks sequentially at boot |
| # if the tasks: |
| # 1. Show up the boot message by calling `chromeos-boot-alert`. |
| # 2. Leave the message and enter Chrome UI normally after it is finished. |
| # Please make sure the tasks running here DO NOT take a long time in the no-op |
| # case because this job BLOCKS the UI startup. |
| |
| # It has no main script so that it only runs once at boot before UI starts. |
| pre-start script |
| lsbval() { |
| local key="$1" |
| local lsbfile="${2:-/etc/lsb-release}" |
| |
| if ! echo "${key}" | grep -Eq '^[a-zA-Z0-9_]+$'; then |
| return 1 |
| fi |
| |
| sed -E -n -e \ |
| "/^[[:space:]]*${key}[[:space:]]*=/{ |
| s:^[^=]+=[[:space:]]*:: |
| s:[[:space:]]+$:: |
| p |
| }" "${lsbfile}" |
| } |
| |
| logit() { |
| logger -t "${UPSTART_JOB}" "$@" |
| } |
| |
| # Updaters will touch this file to request a reboot. |
| REBOOT_REQUEST_FILE="/tmp/force_reboot_after_fw_update" |
| # Used by reboot_queue to signal a reboot after all updaters |
| # have run. This will be set if REBOOT_REQUEST_FILE existed. |
| reboot_queued="false" |
| |
| # Run after each updater invocation to log whether that updater |
| # requested a reboot/reset and queue a reboot to occur at the end |
| # of script. |
| # |
| # Usage: reboot_queue <updater_name> |
| reboot_queue() { |
| local updater_name="$1" |
| |
| if [ -e "${REBOOT_REQUEST_FILE}" ]; then |
| reboot_queued="true" |
| rm -f "${REBOOT_REQUEST_FILE}" # croslint: disable: rm temp reboot request |
| logit "Updater ${updater_name} requested reboot." |
| fi |
| } |
| |
| # This file contains a cached version of the last booted OS version that |
| # successfully updated firmware. |
| CACHED_OS_VERSION_FILE='/var/lib/boot-update-firmware/last-os-version' |
| |
| # Checks if the OS version is the same as the last time firmware was updated. |
| cached_os_version_matches() { |
| local cache_version cur_version |
| |
| cache_version="$(head -c1024 ${CACHED_OS_VERSION_FILE})" || : |
| cur_version="$(lsbval CHROMEOS_RELEASE_VERSION)" |
| [ "${cur_version}" = "${cache_version}" ] |
| } |
| |
| update_os_version_cache() { |
| if ! lsbval CHROMEOS_RELEASE_VERSION > "${CACHED_OS_VERSION_FILE}"; then |
| echo '' > "${CACHED_OS_VERSION_FILE}" |
| return 1 |
| fi |
| } |
| |
| # Update FPMCU firmware. |
| FP_SCRIPT='/usr/sbin/bio_fw_updater' |
| FP_LOG_DIR='/var/log/biod' |
| FP_PRESTART_LOG='/var/log/bio_fw_updater.out' |
| |
| do_update_fp_firmware() { |
| logit "Update FPMCU firmware." |
| "${FP_SCRIPT}" "--log_dir=${FP_LOG_DIR}" >"${FP_PRESTART_LOG}" 2>&1 || \ |
| logit "Failed to update FPMCU firmware." |
| logit "-f" "${FP_PRESTART_LOG}" |
| } |
| |
| FACTORY_UTILS="/usr/share/cros/factory_utils.sh" |
| |
| FACTORY_MODE=0 |
| if [ -f "${FACTORY_UTILS}" ]; then |
| . "${FACTORY_UTILS}" |
| if is_factory_test_mode; then |
| FACTORY_MODE=1 |
| fi |
| fi |
| |
| if [ -e "${FP_SCRIPT}" ]; then |
| if [ ${FACTORY_MODE} -eq 1 ]; then |
| logit "Skip FPMCU firmware update in factory mode." |
| else |
| do_update_fp_firmware |
| reboot_queue "FPMCU" |
| fi |
| fi |
| |
| # Update CSME firmware. |
| CSME_SCRIPT='/opt/google/csme/scripts/chromeos-csme-update.sh' |
| if [ -e "${CSME_SCRIPT}" ]; then |
| if cached_os_version_matches; then |
| logit "CSME firmware updated already for this OS version; skipping until \ |
| next OS update." |
| else |
| logit "Update CSME firmware." |
| if "${CSME_SCRIPT}"; then |
| if ! update_os_version_cache; then |
| logit "Failed to update OS version cache, CSME firmware updater will \ |
| retry" |
| fi |
| else |
| logit "Failed to update CSME firmware." |
| fi |
| reboot_queue "CSME" |
| fi |
| fi |
| |
| # Update detachable keyboard firmware. |
| HAMMERD_SCRIPT='/usr/share/cros/init/hammerd-at-boot.sh' |
| if [ -e "${HAMMERD_SCRIPT}" ]; then |
| logit "Update keyboard firmware." |
| "${HAMMERD_SCRIPT}" || logit "Failed to update keyboard firmware." |
| reboot_queue "hammerd" |
| fi |
| |
| # Update KBMCU firmware. |
| # TODO (b/233121300): Check if this can be combined with hammerd |
| KBMCU_SCRIPT='/usr/share/cros/init/kbmcu-fw-update.sh' |
| if [ -e "${KBMCU_SCRIPT}" ]; then |
| logit "Update kbmcu firmware." |
| "${KBMCU_SCRIPT}" || logit "Failed to update kbmcu firmware." |
| reboot_queue "KBMCU" |
| fi |
| |
| # Update touch firmware. |
| TOUCH_SCRIPT='/opt/google/touch/scripts/chromeos-touch-update.sh' |
| if [ -e "${TOUCH_SCRIPT}" ]; then |
| logit "Update touch firmware." |
| "${TOUCH_SCRIPT}" || logit "Failed to update touch firmware." |
| reboot_queue "touch" |
| fi |
| |
| # Update fwupd firmware. |
| FWUPD_SCRIPT='/usr/share/cros/init/fwupd-at-boot.sh' |
| if [ -e "${FWUPD_SCRIPT}" ]; then |
| logit "Update fwupd firmware." |
| "${FWUPD_SCRIPT}" || logit "Failed to update fwupd firmware." |
| reboot_queue "FWUPD" |
| fi |
| |
| # Update TCON (display timing controller) firmware. |
| TCON_SCRIPT='/opt/google/tcon/scripts/chromeos-tcon-update.sh' |
| if [ -e "${TCON_SCRIPT}" ]; then |
| logit "Update TCON firmware." |
| "${TCON_SCRIPT}" || logit "Failed to update TCON firmware." |
| reboot_queue "TCON" |
| fi |
| |
| # Update the firmware of Intel's AX211 WiFi chipset. |
| AX211_SCRIPT='/usr/share/cros/init/ax211-updater.sh' |
| if [ -e "${AX211_SCRIPT}" ]; then |
| logit "Update AX211." |
| "${AX211_SCRIPT}" || logit "Failed to update AX211." |
| reboot_queue "AX211" |
| fi |
| |
| # Some firmware updaters require a reboot immediately after the update. This |
| # checks if one of them has created a file /tmp/force_reboot_after_fw_update. |
| # If yes, and we're not booting from a removable device, then reboot. |
| . /usr/share/misc/chromeos-common.sh |
| . /usr/sbin/write_gpt.sh |
| |
| # Check if rootfs is mounted on a removable device. |
| rootdev_removable() { |
| load_base_vars |
| |
| local dst_drive="$(get_fixed_dst_drive)" |
| local root_drive="$(rootdev -s -d)" |
| |
| if [ -z "${dst_drive}" ]; then |
| logit "no known device" |
| elif [ "${dst_drive}" != "${root_drive}" ]; then |
| logit "running on removable device ${dst_drive}, not ${root_drive}" |
| return 0 |
| else |
| logit "running on disk ${root_drive}" |
| fi |
| return 1 |
| } |
| |
| # Do a cold reset. |
| do_cold_reset() { |
| sync |
| # Sleep for hardware to flush physical cache. Otherwise fs metadata may be |
| # corrupted. |
| sleep 5 |
| ectool reboot_ec cold |
| # Sleep instead of exiting immediately. This long annoying sleep is just for |
| # safety until the cold reboot kicks in. |
| sleep 60 |
| exit 0 |
| } |
| |
| # If a reboot request is still present, we have an untracked updater. |
| reboot_queue "ERROR/UNKNOWN" |
| |
| COLD_BOOT_FILE="/tmp/force_cold_boot_after_fw_update" |
| if [ -e "${COLD_BOOT_FILE}" ]; then |
| # Immediately delete the file to avoid bootloops. |
| rm -f "${COLD_BOOT_FILE}" |
| do_cold_reset |
| elif "${reboot_queued}"; then |
| # If we're *not* booting from a removable device, then reboot the device. |
| if rootdev_removable; then |
| logit "rootfs on removable device, not rebooting" |
| else |
| logit "reboot required" |
| reboot |
| exit 0 |
| fi |
| fi |
| |
| display_boot_message action restore_frecon |
| end script |