#!/bin/bash # Copyright 2017-2019 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # provide a couple of places in the RO root filesystem # that can be made RW with an overlayfs log() { [ -c /dev/kmsg ] && echo "init: $*" > /dev/kmsg echo "init: $*" } # start with /proc and /tmp mounted [ -e /proc/mounts ] || mount -t proc proc /proc # FIXME: add size limits to /tmp grep -q /tmp /proc/mounts || mount -t tmpfs -o rw,nosuid,nodev tmp /tmp grep -q /sys /proc/mounts || mount -t sysfs -o rw,nosuid,nodev,noexec sys /sys # fix up /srv to be RW mkdir -p /tmp/srv mount --bind /tmp/srv /srv if grep -q debug-init /proc/cmdline; then exec > /tmp/init.log 2>&1 set -x env else # Suppress any stray output but we want to see any errors exec >/dev/null 2>/dev/kmsg fi # list of things that need to be rw at boot NV_OVERLAYS="/etc /var /home" # place to mount the overlay backing store OVERLAY_MNT=/tmp/.overlay # OVERLAY_SIZE=16384 # place to mount NV RWFS_MNT=/tmp/.rwfs # NV overlay storage OVERLAY_SYNC=${RWFS_MNT}/.overlay if grep -q "$RWFS_MNT" /proc/mounts; then # quit - we have already run exit 0 fi mkdir -p "$OVERLAY_MNT" # TODO: remount the overlay with a size limit? # mount -t tmpfs -o rw,size=${OVERLAY_SIZE} oltmp ${OVERLAY_MNT} mtd_by_name() { local name="$1" echo "/dev/$(grep "$name" /proc/mtd | cut -d : -f 1)" } mtdnum_by_name() { local name="$1" grep "$name" /proc/mtd | cut -c 4 } NV_MTD=rwfs nvrw() { local p="$1" # Clear the work dir doing overlay mount rm -rf "${OVERLAY_MNT}${p}.work" mkdir -p "${OVERLAY_MNT}${p}" "${OVERLAY_MNT}${p}.work" local mname mname=$(echo "ol${p}" | sed 's,/,,g') local opts="lowerdir=${p},upperdir=${OVERLAY_MNT}${p},workdir=${OVERLAY_MNT}${p}.work,sync" mount -t overlay -o "$opts" "$mname" "$p" } targeted_clean() { log "restore-defaults: targeted_clean" # Do not delete FRU info, ssh/ssl certs, or machine-id ( cd "${OVERLAY_SYNC}/etc" || exit find . ! -regex '.*/\(ssl\|dropbear\|machine-id\|fru\).*' -a ! -path '.' \ -exec rm -rf {} + ) # nothing should be in the workdir, but clear it just in case rm -rf "${OVERLAY_SYNC}/etc.work" # clean everything out of /home rm -rf "${OVERLAY_SYNC}/home" "${OVERLAY_SYNC}/home.work" # clean everything out of /var rm -rf "${OVERLAY_SYNC}/var" "${OVERLAY_SYNC}/var.work" echo "Files remaining: $(find $OVERLAY_SYNC/)" sync } full_clean() { log "restore-defaults: full_clean" local OVL='' for OVL in $NV_OVERLAYS; do rm -rf "${OVERLAY_SYNC}${OVL}" "${OVERLAY_SYNC}${OVL}.work" done sync } jffs2_mount() { mtd_name=$1 mnt=$2 mount -t jffs2 -o sync,ro mtd:"$mtd_name" "$mnt" } reformat_jffs2_partition() { local mtd_name="$1" local mnt="$2" # unmount the partition to reformat it umount -f "$mnt" flash_erase "$(mtd_by_name "$mtd_name")" 0 0 # remount the JFFS2 if ! jffs2_mount "$mtd_name" "$mnt"; then log "Failed to mount reformatted NV volume; system unstable" fi } clear_ubenv() { log "Clearing U-Boot environment" flash_erase "$(mtd_by_name u-boot-env)" 0 0 } # mount NV filesystem mkdir -p "$RWFS_MNT" if ! jffs2_mount "$NV_MTD" "$RWFS_MNT"; then log "Failed to mount NV volume; attempting recovery" reformat_jffs2_partition $NV_MTD $RWFS_MNT fi # check for full factory reset: if so, format $NV_MTD_DEV RESTORE_FLAG=$RWFS_MNT/.restore_op if [ -f "$RESTORE_FLAG" ]; then mount -o remount,rw "$RWFS_MNT" restore_op=$(cat $RESTORE_FLAG) # read from NV modified_on=$(stat -c %y $RESTORE_FLAG) # get last modified time log "restore_op: $restore_op modified on: $modified_on" # To rule out stale file mounted, Write unique, unused value 0x10 # (last 2-bits are b00) before removing the file. echo 16 > $RESTORE_FLAG # set default value 0 if RESTORE_FLAG file was empty restore_op=${restore_op:-0} restore_op=$((restore_op & 3)) # mask off 2 bits if [ $restore_op -eq 1 ]; then targeted_clean elif [ $restore_op -eq 2 ]; then full_clean clear_ubenv elif [ $restore_op -eq 3 ]; then log "restore-defaults: reformat" reformat_jffs2_partition $NV_MTD $RWFS_MNT clear_ubenv fi rm -f $RESTORE_FLAG mount -o remount,ro "$RWFS_MNT" fi # Restore the overlay saved in the sync rsync -a --delete "${OVERLAY_SYNC}/" "${OVERLAY_MNT}" log "Restored overlay from sync location" for FS in $NV_OVERLAYS; do nvrw "$FS" done # work around bug where /etc/machine-id will be mounted with a temporary file # if rootfs is read-only and the file is empty MACHINE_ID=/etc/machine-id generate_machine_id() { systemd-machine-id-setup cp -pf "$MACHINE_ID" "${MACHINE_ID}_bkup" } if [ ! -s "$MACHINE_ID" ]; then # work around - Bug: Overlay fs fails for machine-id due to # origin mismatch. Clean it up, from overlay fs before re-creating # the same. if [ -e "$OVERLAY_MNT$MACHINE_ID" ]; then umount "/etc" rm -f "$OVERLAY_MNT$MACHINE_ID" nvrw "/etc" # Restore the machine-id from backup, else generate it. if [ -s "${MACHINE_ID}_bkup" ]; then cp -pf "${MACHINE_ID}_bkup" "${MACHINE_ID}" else generate_machine_id fi log "Remounted /etc for machine-id origin mismatch" else generate_machine_id fi fi # mount persistent NV filesystem, where immortal settings live SOFS_MNT=/var/sofs if ! grep -q sofs /proc/mounts; then mkdir -p $SOFS_MNT SOFS_MTD=sofs # mount a JFFS2 on the partition if ! jffs2_mount "$SOFS_MTD" "$SOFS_MNT"; then log "Failed to mount SOFS volume; attempting recovery" reformat_jffs2_partition $SOFS_MTD $SOFS_MNT fi fi log "Finished mounting nv and overlays" # Detect the non-legacy node in cooper city and boot in to special mode. readonly COOPER_CITY=40 # Board id of cooper city is_nl_node() { typeset -i nid1=$(gpioget $(gpiofind "FM_NODE_ID_1")) typeset -i nid2=$(gpioget $(gpiofind "FM_NODE_ID_2")) echo $((nid1|nid2)) } read_board_id() { local idx=0 local result=0 local value=0 for ((idx=0; idx<6; idx++)) do typeset -i value=$(gpioget $(gpiofind "FM_BMC_BOARD_SKU_ID${idx}_N")) value=$((value << idx)) result=$((result | value)) done echo $result } pfr_write() { [ $# -ne 2 ] && return 1 local PFR_BUS=4 local PFR_ADDR=0x38 local reg=$1 local val=$2 i2cset -y $PFR_BUS $PFR_ADDR "$reg" "$val" >&/dev/null } board_id=$(read_board_id) if [ "$board_id" -eq $COOPER_CITY ]; then if [ "$(is_nl_node)" -ne 0 ]; then systemctl set-default multi-node-nl.target PFR_BMC_CHECKPOINT_REG=0xf PFR_BMC_CHECKPOINT_COMPLETE=0x9 pfr_write $PFR_BMC_CHECKPOINT_REG $PFR_BMC_CHECKPOINT_COMPLETE fi fi exec /lib/systemd/systemd