summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/preinit-mounts/preinit-mounts/init
blob: 2065f94eecb5c80606c9ead306dd4c352c818173 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#!/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