Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ading2210
GitHub Repository: ading2210/shimboot
Path: blob/main/bootloader/bin/bootstrap.sh
596 views
1
#!/bin/busybox sh
2
# Copyright 2015 The Chromium OS Authors. All rights reserved.
3
# Use of this source code is governed by a BSD-style license that can be
4
# found in the LICENSE file.
5
#
6
# To bootstrap the factory installer on rootfs. This file must be executed as
7
# PID=1 (exec).
8
# Note that this script uses the busybox shell (not bash, not dash).
9
10
#original: https://chromium.googlesource.com/chromiumos/platform/initramfs/+/refs/heads/main/factory_shim/bootstrap.sh
11
12
#set -x
13
set +x
14
15
rescue_mode=""
16
17
invoke_terminal() {
18
local tty="$1"
19
local title="$2"
20
shift
21
shift
22
# Copied from factory_installer/factory_shim_service.sh.
23
echo "${title}" >>${tty}
24
setsid sh -c "exec script -afqc '$*' /dev/null <${tty} >>${tty} 2>&1 &"
25
}
26
27
enable_debug_console() {
28
local tty="$1"
29
echo -e "debug console enabled on ${tty}"
30
invoke_terminal "${tty}" "[Bootstrap Debug Console]" "/bin/busybox sh"
31
}
32
33
#get a partition block device from a disk path and a part number
34
get_part_dev() {
35
local disk="$1"
36
local partition="$2"
37
38
#disk paths ending with a number will have a "p" before the partition number
39
last_char="$(echo -n "$disk" | tail -c 1)"
40
if [ "$last_char" -eq "$last_char" ] 2>/dev/null; then
41
echo "${disk}p${partition}"
42
else
43
echo "${disk}${partition}"
44
fi
45
}
46
47
find_rootfs_partitions() {
48
local disks=$(fdisk -l | sed -n "s/Disk \(\/dev\/.*\):.*/\1/p")
49
if [ ! "${disks}" ]; then
50
return 1
51
fi
52
53
for disk in $disks; do
54
local partitions=$(fdisk -l $disk | sed -n "s/^[ ]\+\([0-9]\+\).*shimboot_rootfs:\(.*\)$/\1:\2/p")
55
if [ ! "${partitions}" ]; then
56
continue
57
fi
58
for partition in $partitions; do
59
get_part_dev "$disk" "$partition"
60
done
61
done
62
}
63
64
find_chromeos_partitions() {
65
local roota_partitions="$(cgpt find -l ROOT-A)"
66
local rootb_partitions="$(cgpt find -l ROOT-B)"
67
68
if [ "$roota_partitions" ]; then
69
for partition in $roota_partitions; do
70
echo "${partition}:ChromeOS_ROOT-A:CrOS"
71
done
72
fi
73
74
if [ "$rootb_partitions" ]; then
75
for partition in $rootb_partitions; do
76
echo "${partition}:ChromeOS_ROOT-B:CrOS"
77
done
78
fi
79
}
80
81
find_all_partitions() {
82
echo "$(find_chromeos_partitions)"
83
echo "$(find_rootfs_partitions)"
84
}
85
86
#from original bootstrap.sh
87
move_mounts() {
88
local base_mounts="/sys /proc /dev"
89
local newroot_mnt="$1"
90
for mnt in $base_mounts; do
91
# $mnt is a full path (leading '/'), so no '/' joiner
92
mkdir -p "$newroot_mnt$mnt"
93
mount -n -o move "$mnt" "$newroot_mnt$mnt"
94
done
95
}
96
97
print_license() {
98
local shimboot_version="$(cat /opt/.shimboot_version)"
99
if [ -f "/opt/.shimboot_version_dev" ]; then
100
local git_hash="$(cat /opt/.shimboot_version_dev)"
101
local suffix="-dev-$git_hash"
102
fi
103
cat << EOF
104
Shimboot ${shimboot_version}${suffix}
105
106
ading2210/shimboot: Boot desktop Linux from a Chrome OS RMA shim.
107
Copyright (C) 2025 ading2210
108
109
This program is free software: you can redistribute it and/or modify
110
it under the terms of the GNU General Public License as published by
111
the Free Software Foundation, either version 3 of the License, or
112
(at your option) any later version.
113
114
This program is distributed in the hope that it will be useful,
115
but WITHOUT ANY WARRANTY; without even the implied warranty of
116
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
117
GNU General Public License for more details.
118
119
You should have received a copy of the GNU General Public License
120
along with this program. If not, see <https://www.gnu.org/licenses/>.
121
EOF
122
}
123
124
print_selector() {
125
local rootfs_partitions="$1"
126
local i=1
127
128
echo "┌──────────────────────┐"
129
echo "│ Shimboot OS Selector │"
130
echo "└──────────────────────┘"
131
132
if [ "${rootfs_partitions}" ]; then
133
for rootfs_partition in $rootfs_partitions; do
134
#i don't know of a better way to split a string in the busybox shell
135
local part_path=$(echo $rootfs_partition | cut -d ":" -f 1)
136
local part_name=$(echo $rootfs_partition | cut -d ":" -f 2)
137
echo "${i}) ${part_name} on ${part_path}"
138
i=$((i+1))
139
done
140
else
141
echo "no bootable partitions found. please see the shimboot documentation to mark a partition as bootable."
142
fi
143
144
echo "q) reboot"
145
echo "s) enter a shell"
146
echo "l) view license"
147
}
148
149
get_selection() {
150
local rootfs_partitions="$1"
151
local i=1
152
153
read -p "Your selection: " selection
154
if [ "$selection" = "q" ]; then
155
echo "rebooting now."
156
reboot -f
157
elif [ "$selection" = "s" ]; then
158
reset
159
enable_debug_console "$TTY1"
160
return 0
161
elif [ "$selection" = "l" ]; then
162
clear
163
print_license
164
echo
165
read -p "press [enter] to return to the bootloader menu"
166
return 1
167
fi
168
169
local selection_cmd="$(echo "$selection" | cut -d' ' -f1)"
170
if [ "$selection_cmd" = "rescue" ]; then
171
selection="$(echo "$selection" | cut -d' ' -f2-)"
172
rescue_mode="1"
173
else
174
rescue_mode=""
175
fi
176
177
for rootfs_partition in $rootfs_partitions; do
178
local part_path=$(echo $rootfs_partition | cut -d ":" -f 1)
179
local part_name=$(echo $rootfs_partition | cut -d ":" -f 2)
180
local part_flags=$(echo $rootfs_partition | cut -d ":" -f 3)
181
182
if [ "$selection" = "$i" ]; then
183
echo "selected $part_path"
184
if [ "$part_flags" = "CrOS" ]; then
185
echo "booting chrome os partition"
186
print_donor_selector "$rootfs_partitions"
187
get_donor_selection "$rootfs_partitions" "$part_path"
188
else
189
boot_target "$part_path"
190
fi
191
return 1
192
fi
193
194
i=$((i+1))
195
done
196
197
echo "invalid selection"
198
sleep 1
199
return 1
200
}
201
202
copy_progress() {
203
local source="$1"
204
local destination="$2"
205
mkdir -p "$destination"
206
tar -cf - -C "${source}" . | pv -f | tar -xf - -C "${destination}"
207
}
208
209
print_donor_selector() {
210
local rootfs_partitions="$1"
211
local i=1;
212
213
echo "Choose a partition to copy firmware and modules from:";
214
215
for rootfs_partition in $rootfs_partitions; do
216
local part_path=$(echo $rootfs_partition | cut -d ":" -f 1)
217
local part_name=$(echo $rootfs_partition | cut -d ":" -f 2)
218
local part_flags=$(echo $rootfs_partition | cut -d ":" -f 3)
219
220
if [ "$part_flags" = "CrOS" ]; then
221
continue;
222
fi
223
224
echo "${i}) ${part_name} on ${part_path}"
225
i=$((i+1))
226
done
227
}
228
229
yes_no_prompt() {
230
local prompt="$1"
231
local var_name="$2"
232
233
while true; do
234
read -p "$prompt" temp_result
235
236
if [ "$temp_result" = "y" ] || [ "$temp_result" = "n" ]; then
237
#the busybox shell has no other way to declare a variable from a string
238
#the declare command and printf -v are both bashisms
239
eval "$var_name='$temp_result'"
240
return 0
241
else
242
echo "invalid selection"
243
fi
244
done
245
}
246
247
get_donor_selection() {
248
local rootfs_partitions="$1"
249
local target="$2"
250
local i=1;
251
read -p "Your selection: " selection
252
253
for rootfs_partition in $rootfs_partitions; do
254
local part_path=$(echo $rootfs_partition | cut -d ":" -f 1)
255
local part_name=$(echo $rootfs_partition | cut -d ":" -f 2)
256
local part_flags=$(echo $rootfs_partition | cut -d ":" -f 3)
257
258
if [ "$part_flags" = "CrOS" ]; then
259
continue;
260
fi
261
262
if [ "$selection" = "$i" ]; then
263
echo "selected $part_path as the donor partition"
264
yes_no_prompt "would you like to spoof verified mode? this is useful if you're planning on using chrome os while enrolled. (y/n): " use_crossystem
265
yes_no_prompt "would you like to spoof an invalid hwid? this will forcibly prevent the device from being enrolled. (y/n): " invalid_hwid
266
boot_chromeos "$target" "$part_path" "$use_crossystem" "$invalid_hwid"
267
fi
268
269
i=$((i+1))
270
done
271
272
echo "invalid selection"
273
sleep 1
274
return 1
275
}
276
277
exec_init() {
278
if [ "$rescue_mode" = "1" ]; then
279
echo "entering a rescue shell instead of starting init"
280
echo "once you are done fixing whatever is broken, run 'exec /sbin/init' to continue booting the system normally"
281
282
if [ -f "/bin/bash" ]; then
283
exec /bin/bash < "$TTY1" >> "$TTY1" 2>&1
284
else
285
exec /bin/sh < "$TTY1" >> "$TTY1" 2>&1
286
fi
287
else
288
exec /sbin/init < "$TTY1" >> "$TTY1" 2>&1
289
fi
290
}
291
292
boot_target() {
293
local target="$1"
294
295
echo "moving mounts to newroot"
296
mkdir /newroot
297
#use cryptsetup to check if the rootfs is encrypted
298
if [ -x "$(command -v cryptsetup)" ] && cryptsetup luksDump "$target" >/dev/null 2>&1; then
299
cryptsetup open $target rootfs
300
mount /dev/mapper/rootfs /newroot
301
else
302
mount $target /newroot
303
fi
304
#bind mount /dev/console to show systemd boot msgs
305
if [ -f "/bin/frecon-lite" ]; then
306
rm -f /dev/console
307
touch /dev/console #this has to be a regular file otherwise the system crashes afterwards
308
mount -o bind "$TTY1" /dev/console
309
fi
310
move_mounts /newroot
311
312
echo "switching root"
313
mkdir -p /newroot/bootloader
314
pivot_root /newroot /newroot/bootloader
315
exec_init
316
}
317
318
boot_chromeos() {
319
local target="$1"
320
local donor="$2"
321
local use_crossystem="$3"
322
local invalid_hwid="$4"
323
324
echo "mounting target"
325
mkdir /newroot
326
mount -o ro $target /newroot
327
328
echo "mounting tmpfs"
329
mount -t tmpfs -o mode=1777 none /newroot/tmp
330
mount -t tmpfs -o mode=0555 run /newroot/run
331
mkdir -p -m 0755 /newroot/run/lock
332
333
echo "mounting donor partition"
334
local donor_mount="/newroot/tmp/donor_mnt"
335
local donor_files="/newroot/tmp/donor"
336
mkdir -p $donor_mount
337
mount -o ro $donor $donor_mount
338
echo "copying modules and firmware to tmpfs (this may take a while)"
339
copy_progress $donor_mount/lib/modules $donor_files/lib/modules
340
copy_progress $donor_mount/lib/firmware $donor_files/lib/firmware
341
mount -o bind $donor_files/lib/modules /newroot/lib/modules
342
mount -o bind $donor_files/lib/firmware /newroot/lib/firmware
343
umount $donor_mount
344
rm -rf $donor_mount
345
346
if [ -e "/newroot/etc/init/tpm-probe.conf" ]; then
347
echo "applying chrome os flex patches"
348
mkdir -p /newroot/tmp/empty
349
mount -o bind /newroot/tmp/empty /sys/class/tpm
350
351
cat /newroot/etc/lsb-release | sed "s/DEVICETYPE=OTHER/DEVICETYPE=CHROMEBOOK/" > /newroot/tmp/lsb-release
352
mount -o bind /newroot/tmp/lsb-release /newroot/etc/lsb-release
353
fi
354
355
echo "patching chrome os rootfs"
356
cat /newroot/etc/ui_use_flags.txt | sed "/reven_branding/d" | sed "/os_install_service/d" > /newroot/tmp/ui_use_flags.txt
357
mount -o bind /newroot/tmp/ui_use_flags.txt /newroot/etc/ui_use_flags.txt
358
359
cp /opt/mount-encrypted /newroot/tmp/mount-encrypted
360
cp /newroot/usr/sbin/mount-encrypted /newroot/tmp/mount-encrypted.real
361
mount -o bind /newroot/tmp/mount-encrypted /newroot/usr/sbin/mount-encrypted
362
363
cat /newroot/etc/init/boot-splash.conf | sed '/^script$/a \ pkill frecon-lite || true' > /newroot/tmp/boot-splash.conf
364
mount -o bind /newroot/tmp/boot-splash.conf /newroot/etc/init/boot-splash.conf
365
366
if [ "$use_crossystem" = "y" ]; then
367
echo "patching crossystem"
368
cp /opt/crossystem /newroot/tmp/crossystem
369
if [ "$invalid_hwid" = "y" ]; then
370
sed -i 's/block_devmode/hwid/' /newroot/tmp/crossystem
371
fi
372
373
cp /newroot/usr/bin/crossystem /newroot/tmp/crossystem_old
374
mount -o bind /newroot/tmp/crossystem /newroot/usr/bin/crossystem
375
fi
376
377
echo "moving mounts"
378
move_mounts /newroot
379
380
echo "switching root"
381
mkdir -p /newroot/tmp/bootloader
382
pivot_root /newroot /newroot/tmp/bootloader
383
384
echo "starting init"
385
/sbin/modprobe zram
386
exec_init
387
}
388
389
main() {
390
echo "starting the shimboot bootloader"
391
392
enable_debug_console "$TTY2"
393
394
local valid_partitions="$(find_all_partitions)"
395
396
while true; do
397
clear
398
print_selector "${valid_partitions}"
399
400
if get_selection "${valid_partitions}"; then
401
break
402
fi
403
done
404
}
405
406
trap - EXIT
407
main "$@"
408
sleep 1d
409
410