1#!/bin/sh
2
3fslist="proc sys dev run"
4rodir=run/initramfs/ro
5rwdir=run/initramfs/rw
6upper=$rwdir/cow
7work=$rwdir/work
8
9cd /
10mkdir -p $fslist
11mount dev dev -tdevtmpfs
12mount sys sys -tsysfs
13mount proc proc -tproc
14if ! grep run proc/mounts
15then
16	mount tmpfs run -t tmpfs -o mode=755,nodev
17fi
18
19mkdir -p $rodir $rwdir
20
21cp -rp init shutdown update whitelist bin sbin usr lib etc var run/initramfs
22
23# To start a interactive shell with job control at this point, run
24# getty 38400 ttyS4
25
26findmtd() {
27	m=$(grep -xl "$1" /sys/class/mtd/*/name)
28	m=${m%/name}
29	m=${m##*/}
30	echo $m
31}
32
33blkid_fs_type() {
34	# Emulate util-linux's `blkid -s TYPE -o value $1`
35	# Example busybox blkid output:
36	#    # blkid /dev/mtdblock5
37	#    /dev/mtdblock5: TYPE="squashfs"
38	# Process output to extract TYPE value "squashfs".
39	blkid $1 | sed -e 's/^.*TYPE="//' -e 's/".*$//'
40}
41
42probe_fs_type() {
43	fst=$(blkid_fs_type $1)
44	echo ${fst:=jffs2}
45}
46
47# This fw_get_env_var is a possibly broken version of fw_printenv that
48# does not check the crc or flag byte.
49# The u-boot environment starts with a crc32, followed by a flag byte
50# when a redundannt environment is configured, followed by var=value\0 sets.
51# The flag byte for nand is a 1 byte counter; for nor it is a 1 or 0 byte.
52
53get_fw_env_var() {
54	# do we have 1 or 2 copies of the environment?
55	# count non-blank non-comment lines
56	# copies=$(grep -v ^# /etc/fw_env.config | grep -c [::alnum::])
57	# ... we could if we had the fw_env.config in the initramfs
58	copies=2
59
60	# * Change \n to \r and \0 to \n
61	# * Skip to the 5th byte to skip over crc
62	# * then skip to the first or 2nd byte to skip over flag if it exists
63	# * stop parsing at first empty line corresponding to the
64	#   double \0 at the end of the environment.
65	# * print the value of the variable name passed as argument
66
67	envdev=$(findmtd u-boot-env)
68	if test -n $envdev
69	then
70		cat /dev/$envdev |
71		tr '\n\000' '\r\n' |
72		tail -c +5 | tail -c +${copies-1} |
73		sed -ne '/^$/,$d' -e "s/^$1=//p"
74	fi
75}
76
77setup_resolv() {
78	runresolv=/run/systemd/resolve/resolv.conf
79	etcresolv=/etc/resolv.conf
80
81	if test ! -e $etcresolv -a ! -L $etcresolv
82	then
83		mkdir -p ${runresolv%/*}
84		ln -s $runresolv $etcresolv
85	fi
86	if test ! -f $runresolv
87	then
88		cat  /proc/net/pnp > $runresolv
89	fi
90
91	return 0
92}
93
94try_tftp() {
95	# split into  tftp:// host:port/ path/on/remote
96	# then spilt off / and then :port from the end of host:port/
97	# and : from the beginning of port
98
99	rest="${1#tftp://}"
100	path=${rest#*/}
101	host=${rest%$path}
102	host="${host%/}"
103	port="${host#${host%:*}}"
104	host="${host%$port}"
105	port="${port#:}"
106
107	setup_resolv
108
109	if test -z "$host" -o -z "$path"
110	then
111		debug_takeover "Invalid tftp download url '$url'."
112	elif echo "Downloading '$url' from $host ..."  &&
113		! tftp -g -r "$path" -l /run/image-rofs "$host" ${port+"$port"}
114	then
115		debug_takeover "Download of '$url' failed."
116	fi
117}
118
119try_wget() {
120	setup_resolv
121
122	echo "Downloading '$1' ..."
123	if ! wget -O /run/image-rofs "$1"
124	then
125		debug_takeover "Download of '$url' failed."
126	fi
127}
128
129debug_takeover() {
130	echo "$@"
131	test -n "$@" && echo Enter password to try to manually fix.
132	cat << HERE
133After fixing run exit to continue this script, or reboot -f to retry, or
134touch /takeover and exit to become PID 1 allowing editing of this script.
135HERE
136
137	while ! sulogin && ! test -f /takeover
138	do
139		echo getty failed, retrying
140	done
141
142	# Touch /takeover in the above getty to become pid 1
143	if test -e /takeover
144	then
145		cat << HERE
146
147Takeover of init requested.  Executing /bin/sh as PID 1.
148When finished exec new init or cleanup and run reboot -f.
149
150Warning: No job control!  Shell exit will panic the system!
151HERE
152		export PS1=init#\
153		exec /bin/sh
154	fi
155}
156
157rofs=$(findmtd rofs)
158rwfs=$(findmtd rwfs)
159
160rodev=/dev/mtdblock${rofs#mtd}
161rwdev=/dev/mtdblock${rwfs#mtd}
162
163# Set to y for yes, anything else for no.
164force_rwfst_jffs2=y
165flash_images_before_init=n
166consider_download_files=y
167consider_download_tftp=y
168consider_download_http=y
169consider_download_ftp=y
170
171rofst=squashfs
172rwfst=$(probe_fs_type $rwdev)
173roopts=ro
174rwopts=rw
175
176image=/run/initramfs/image-
177trigger=${image}rwfs
178
179init=/sbin/init
180fsckbase=/sbin/fsck.
181fsck=$fsckbase$rwfst
182fsckopts=-a
183optfile=/run/initramfs/init-options
184optbase=/run/initramfs/init-options-base
185urlfile=/run/initramfs/init-download-url
186update=/run/initramfs/update
187
188if test -e /${optfile##*/}
189then
190	cp /${optfile##*/} $optfile
191fi
192
193if test -e /${optbase##*/}
194then
195	cp /${optbase##*/} $optbase
196else
197	touch $optbase
198fi
199
200if test ! -f $optfile
201then
202	cat /proc/cmdline $optbase > $optfile
203	get_fw_env_var openbmcinit >> $optfile
204	get_fw_env_var openbmconce >> $optfile
205fi
206
207echo rofs = $rofs $rofst   rwfs = $rwfs $rwfst
208
209if grep -w debug-init-sh $optfile
210then
211	debug_takeover "Debug initial shell requested by command line."
212fi
213
214if test "x$consider_download_files" = xy &&
215	grep -w openbmc-init-download-files $optfile
216then
217	if test -f ${urlfile##*/}
218	then
219		cp ${urlfile##*/} $urlfile
220	fi
221	if test ! -f $urlfile
222	then
223		get_fw_env_var openbmcinitdownloadurl > $urlfile
224	fi
225	url="$(cat $urlfile)"
226	rest="${url#*://}"
227	proto="${url%$rest}"
228
229	if test -z "$url"
230	then
231		echo "Download url empty.  Ignoring download request."
232	elif test -z "$proto"
233	then
234		echo "Download failed."
235	elif test "$proto" = tftp://
236	then
237		if test "x$consider_download_tftp" = xy
238		then
239			try_tftp "$url"
240		else
241			echo "Download failed."
242		fi
243	elif test "$proto" = http://
244	then
245		if test "x$consider_download_http" = xy
246		then
247			try_wget "$url"
248		else
249			echo "Download failed."
250		fi
251	elif test "$proto" = ftp://
252	then
253		if test "x$consider_download_ftp" = xy
254		then
255			try_wget "$url"
256		else
257			echo "Download failed."
258		fi
259	else
260		echo "Download failed."
261	fi
262fi
263
264# If there are images in root move them to /run/initramfs/ or /run/ now.
265imagebasename=${image##*/}
266if test -n "${imagebasename}" && ls /${imagebasename}* > /dev/null 2>&1
267then
268	if test "x$flash_images_before_init" = xy
269	then
270		echo "Flash images found, will update before starting init."
271		mv /${imagebasename}* ${image%$imagebasename}
272	else
273		echo "Flash images found, will use but deferring flash update."
274		mv /${imagebasename}* /run/
275	fi
276fi
277
278if grep -w clean-rwfs-filesystem $optfile
279then
280	echo "Cleaning of read-write overlay filesystem requested."
281	touch $trigger
282fi
283
284if grep -w factory-reset $optfile
285then
286	echo "Factory reset requested."
287	touch $trigger
288	do_save=--no-save-files
289else
290	do_save=--save-files
291fi
292
293if test "x$force_rwfst_jffs2" = xy -a $rwfst != jffs2 -a ! -f $trigger
294then
295	echo "Converting read-write overlay filesystem to jffs2 forced."
296	touch $trigger
297fi
298
299if ls $image* > /dev/null 2>&1
300then
301	if ! test -x $update
302	then
303		debug_takeover "Flash update requested but $update missing!"
304	elif test -f $trigger -a ! -s $trigger
305	then
306		if [ $do_save = "--save-files" ]
307		then
308			echo "Saving selected files from read-write overlay filesystem."
309		else
310			echo "No files will be selected for save."
311		fi
312		$update --no-restore-files $do_save
313		echo "Clearing read-write overlay filesystem."
314		flash_eraseall /dev/$rwfs
315		echo "Restoring saved files to read-write overlay filesystem."
316		touch $trigger
317		$update --no-save-files --clean-saved-files
318	else
319		$update --clean-saved-files $do_save
320	fi
321
322	rwfst=$(probe_fs_type $rwdev)
323	fsck=$fsckbase$rwfst
324fi
325
326if grep -w overlay-filesystem-in-ram $optfile
327then
328	rwfst=none
329fi
330
331copyfiles=
332if grep -w copy-files-to-ram $optfile
333then
334	rwfst=none
335	copyfiles=y
336fi
337
338# It would be nice to do this after fsck but that mean rofs is mounted
339# which triggers the mtd is mounted check
340if test "$rwfst$copyfiles" = noney
341then
342	touch $trigger
343	$update --copy-files --clean-saved-files --no-restore-files
344fi
345
346if grep -w copy-base-filesystem-to-ram $optfile &&
347	test ! -e /run/image-rofs && ! cp $rodev /run/image-rofs
348then
349	# Remove any partial copy to avoid attempted usage later
350	if test -e  /run/image-rofs
351	then
352		ls -l /run/image-rofs
353		rm -f /run/image-rofs
354	fi
355	debug_takeover "Copying $rodev to /run/image-rofs failed."
356fi
357
358if test -s /run/image-rofs
359then
360	rodev=/run/image-rofs
361	roopts=$roopts,loop
362fi
363
364mount $rodev $rodir -t $rofst -o $roopts
365
366if test -x $rodir$fsck
367then
368	for fs in $fslist
369	do
370		mount --bind $fs $rodir/$fs
371	done
372	chroot $rodir $fsck $fsckopts $rwdev
373	rc=$?
374	for fs in $fslist
375	do
376		umount $rodir/$fs
377	done
378	if test $rc -gt 1
379	then
380		debug_takeover "fsck of read-write fs on $rwdev failed (rc=$rc)"
381	fi
382elif test "$rwfst" != jffs2 -a "$rwfst" != none
383then
384	echo "No '$fsck' in read only fs, skipping fsck."
385fi
386
387if test "$rwfst" = none
388then
389	echo "Running with read-write overlay in RAM for this boot."
390	echo "No state will be preserved unless flash update performed."
391elif ! mount $rwdev $rwdir -t $rwfst -o $rwopts
392then
393	msg="$(cat)" << HERE
394
395Mounting read-write $rwdev filesystem failed.  Please fix and run
396	mount $rwdev $rwdir -t $rwfst -o $rwopts
397to to continue, or do change nothing to run from RAM for this boot.
398HERE
399	debug_takeover "$msg"
400fi
401
402rm -rf $work
403mkdir -p $upper $work
404
405mount -t overlay -o lowerdir=$rodir,upperdir=$upper,workdir=$work cow /root
406
407while ! chroot /root /bin/sh -c "test -x '$init' -a -s '$init'"
408do
409	msg="$(cat)" << HERE
410
411Unable to confirm /sbin/init is an executable non-empty file
412in merged file system mounted at /root.
413
414Change Root test failed!  Invoking emergency shell.
415HERE
416	debug_takeover "$msg"
417done
418
419for f in $fslist
420do
421	mount --move $f root/$f
422done
423
424# switch_root /root $init
425exec chroot /root $init
426
427