-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathq
executable file
·377 lines (319 loc) · 10.4 KB
/
q
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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
#!/bin/bash
# based on: https://github.com/fomichev/dotfiles/blob/master/bin/q
# based on: https://github.com/cs423-uiuc/q-script/blob/main/cs423-q
# Note:
#
# Host machine has to have `resize` program installed
#
# sudo apt install xterm
#
# Todo:
#
# <del>1. Optionally do not require /dev/kvm permissions</del>
# 2. Verify if this script works under host /mnt/ directory
IS_GUEST=${IS_GUEST:-false}
if $IS_GUEST; then
# To-do: Configure by directly modifying the below lines
GUEST_HOSTNAME=${GUEST_HOSTNAME:-guest}
else
# To-do: Configure by directly modifying the below lines
VM_NUM_CPU=${VM_NUM_CPU:-2}
VM_MEMORY=${VM_MEMORY:-1024}
# Configure by command line arguments
ENABLE_GDB=""
VM_COMMANDS=""
# Auto-detected in "main"
VM_ARCH=""
fi
usage() {
if [[ -n "$*" ]]; then
echo "error: $@"
echo
fi
echo "q [OPTION]"
echo
echo "Run it from the kernel build directory (make sure .config is there)"
echo
echo "OPTION:"
echo " g - support being attached with gdb"
echo " c COMMANDS - run COMMANDS instead of interactive bash"
echo
exit 0
}
# This function is called _BEFORE_ QEMU starts (on host).
host() {
local kernel_image="$1"
[[ -e ".config" ]] || usage
# Somehow for kernel 4.x, 5.4.y, 5.10.y etc this mount tag has to be /dev/root
local kernel_major_version
local kernel_minor_version
local rootfs_mount_tag
kernel_major_version=$( grep 'VERSION = [0-9]' Makefile |\
rev | cut -d ' ' -f 1 | rev )
kernel_minor_version=$( grep 'PATCHLEVEL = [0-9]' Makefile |\
rev | cut -d ' ' -f 1 | rev )
if [ "${kernel_major_version}" -le "4" ]; then
rootfs_mount_tag="/dev/root"
elif [ "${kernel_major_version}" -eq "5" ]; then
if [[ "${kernel_minor_version}" -le "15" ]]; then
rootfs_mount_tag="/dev/root"
fi
else
rootfs_mount_tag="9p-root"
fi
local cmdline
local fs
# Root:
# 9p --> overlay, changes within the guest will NOT be reflected outside
# Except kernel source directory:
# 9p, changes within the guest WILL be reflected outside
fs+=" -fsdev local,multidevs=remap,id=vfs1,path=/,security_model=none,readonly=on"
fs+=" -fsdev local,id=vfs2,path=$(pwd),security_model=none"
fs+=" -device virtio-9p-pci,fsdev=vfs1,mount_tag=$rootfs_mount_tag"
fs+=" -device virtio-9p-pci,fsdev=vfs2,mount_tag=9p-kernel-src"
local console
console+=" -display none"
console+=" -serial mon:stdio"
cmdline+=" earlyprintk=serial,ttyS0,115200"
if [[ "${VM_ARCH}" != "arm64" ]]; then
cmdline+=" console=ttyS0"
cmdline+=" kgdboc=ttyS1,115200"
fi
cmdline+=" oops=panic retbleed=off"
cmdline+=" root=$rootfs_mount_tag"
cmdline+=" rootfstype=9p"
cmdline+=" rootflags=version=9p2000.L,trans=virtio,access=any"
cmdline+=" ro"
cmdline+=" nokaslr"
local gdb
$ENABLE_GDB && gdb+=" -s"
local accel
if [[ "$(uname -m)" = "${VM_ARCH}" ]]; then
if [[ -e /dev/kvm && -r /dev/kvm && -w /dev/kvm ]]; then
accel+=" -machine accel=kvm:tcg"
accel+=" -enable-kvm"
fi
fi
local cpu
local qemu_suffix
local tty
case "${VM_ARCH}" in
x86_64)
if [[ "$(uname -m)" = "${VM_ARCH}" ]]; then
if [[ -e /dev/kvm && -r /dev/kvm && -w /dev/kvm ]]; then
cpu="host"
else
cpu="max"
fi
fi
qemu_suffix=x86_64
tty=ttyS0
;;
arm64)
if [[ "$(uname -m)" = "${VM_ARCH}" ]]; then
cpu="host"
else
accel+=" -machine virt -accel tcg "
cpu="max"
fi
qemu_suffix=aarch64
tty=ttyAMA0
;;
esac
local init
init+="IS_GUEST='true' "
init+="GUEST_TTY='$tty' "
init+="GUEST_HOME='$HOME' "
init+="GUEST_KERNEL_SRC_DIR='$(pwd)' "
init+="GUEST_COMMANDS='$VM_COMMANDS' "
init+=" $(realpath $0) "
cmdline+=" init=/bin/sh -- -c \"$init\""
# ???
qemu-system-${qemu_suffix} \
-nographic \
-no-reboot \
$accel \
-device i6300esb,id=watchdog0 \
-watchdog-action pause \
-device virtio-rng-pci \
-cpu $cpu \
-smp $VM_NUM_CPU \
-m $VM_MEMORY \
$fs \
$console \
$gdb \
-kernel "$kernel_image" \
-append "$cmdline"
}
# FIXME ANSI color code may interleave with kernel messages and eventually
# corrupt the rest of this session.
say() {
printf "\33[32m"
echo ">" "$@"
printf "\33(B\33[m"
}
# This function is called _AFTER_ QEMU starts (within guest).
guest() {
# At this point, what the guest sees by `findmnt` is the same as the host
say "(oldroot) mount /proc"
mount -n -t proc -o nosuid,noexec,nodev proc /proc
# After mounting proc, 9p takes effect:
#
# TARGET SOURCE FSTYPE OPTIONS
# / 9p-root 9p ro,...
# |-/dev devtmpfs devtmpfs
# `-/proc proc proc
# Create a writable tmpfs before configuring overlay. The mount point should
# exist on the host.
say "(oldroot) mount /tmp"
mount -n -t tmpfs tmpfs /tmp
say "(oldroot) configure overlay"
local overlay=/tmp/rootdir-overlay
mkdir -p $overlay/{lower,upper,work,newroot}
mount --bind / $overlay/lower
mount -t overlay overlay \
-o lowerdir=$overlay/lower,upperdir=$overlay/upper,workdir=$overlay/work \
$overlay/newroot
# TARGET SOURCE FSTYPE OPTIONS
# / 9p-root 9p ro,...
# |-/dev devtmpfs devtmpf
# |-/proc proc proc
# `-/tmp tmpfs tmpfs rw,...
# |-/tmp/rootdir-overlay/lower
# | 9p-root 9p ro,...
# `-/tmp/rootdir-overlay/newroot
# overlay overlay rw,...
say pivot_root
mkdir -p $overlay/newroot/oldroot
# pivot_root: let the 1st arg be the new root and move the old root to the
# 2nd arg. At this point, what the guest sees by `findmnt` is the same as
# the host.
pivot_root $overlay/newroot $overlay/newroot/oldroot
say mount /proc
mount -n -t proc -o nosuid,noexec,nodev proc /proc
# After mounting proc, ealier settings within guest take effect again:
#
# TARGET SOURCE FSTYPE OPTIONS
# / overlay overlay rw,...
# |-/oldroot 9p-root 9p ro,...
# | |-/oldroot/dev devtmpfs devtmpf
# | |-/oldroot/proc proc proc
# | `-/oldroot/tmp tmpfs tmpfs rw,...
# | `-/oldroot/tmp/rootdir-overlay/lower
# | 9p-root 9p ro,...
# `-/proc proc proc
say umount no longer useful fs
umount /oldroot/tmp/rootdir-overlay/lower
umount /oldroot/tmp
umount /oldroot/proc
umount /oldroot/dev
say move oldroot under /mnt
mkdir /mnt/host-root-readonly
mount --move /oldroot /mnt/host-root-readonly
# TARGET SOURCE FSTYPE OPTIONS
# / overlay overlay rw,...
# |-/mnt/host-root-readonly
# | 9p-root 9p ro,...
# `-/proc proc proc
say empty /etc/fstab
>/etc/fstab
say mount /sys, /tmp, /run, /dev
# /sys
mount -n -t sysfs -o nosuid,noexec,nodev sys /sys
# TODO grep for the following pattern and decide whether to mount or not
#
# CONFIG_DEBUG_FS=y
# CONFIG_CONFIGFS_FS=y
#
mount -n -t configfs configfs /sys/kernel/config
mount -n -t debugfs debugfs /sys/kernel/debug
if [[ -d /sys/kernel/security ]]; then
mount -n -t securityfs security /sys/kernel/security
fi
# /tmp
mount -n -t tmpfs tmpfs /tmp
# /run
mount -n -t tmpfs tmpfs /run
# /dev
mount -n -t devtmpfs -o mode=0755,nosuid,noexec devtmpfs /dev
mkdir -p -m 0755 /dev/shm /dev/pts
mount -n -t devpts -o gid=tty,mode=620,noexec,nosuid devpts /dev/pts
mount -n -t tmpfs -o mode=1777,nosuid,nodev tmpfs /dev/shm
say mount kernel source directory
mount -n -t 9p -o trans=virtio 9p-kernel-src "$GUEST_KERNEL_SRC_DIR"
say "mount /boot (as a tmpfs) and put current kernel config there"
local kernel_version="$(uname -r)"
mount -n -t tmpfs tmpfs /boot
ln -s "$GUEST_KERNEL_SRC_DIR/.config" /boot/config-$kernel_version
say configure \$HOME
export HOME=$GUEST_HOME
say configure hostname
hostname "$GUEST_HOSTNAME"
echo "$GUEST_HOSTNAME" > /etc/hostname
say configure bash environmnet
# Clear environment variables only used in this q script
local tty=$GUEST_TTY
local kernel_src_dir=$GUEST_KERNEL_SRC_DIR
local commands=$GUEST_COMMANDS
unset IS_GUEST
# unset GUEST_HOSTNAME
unset GUEST_TTY
unset GUEST_HOME
unset GUEST_KERNEL_SRC_DIR
unset GUEST_COMMANDS
# All host environment variables from the host will be lost except those set
# in ~/.bashrc
>/etc/profile
>/etc/bash.bashrc
local rcfile=/tmp/.bashrc
cat << EOF > $rcfile
source \$HOME/.bashrc
# Terminal color and width etc
resize &> /dev/null
EOF
cd $kernel_src_dir
if [[ -n "$commands" ]]; then
say "non-interactive bash command(s)"
setsid bash --rcfile $rcfile -c "$commands"
if [[ ! $? -eq 0 ]]; then
say "command(s) failed, starting interactive bash"
setsid bash --rcfile $rcfile 0<>"/dev/$tty" 1>&0 2>&0
# ^^^^^^ ^^^^^^^^^^^^^^ ^^^^ ^^^^ ???
fi
else
say interactive bash with rcfile $rcfile
setsid bash --rcfile $rcfile 0<>"/dev/$tty" 1>&0 2>&0
fi
echo
say poweroff
# TODO relies on CONFIG_MAGIC_SYSRQ=y
echo o >/proc/sysrq-trigger
sleep 30
}
# main() {
if $IS_GUEST; then
guest
exit 0
fi
while getopts "hgc:" opt; do
case $opt in
h) usage ;;
g) ENABLE_GDB=true ;;
c) VM_COMMANDS="$OPTARG" ;;
esac
done
shift $((OPTIND - 1))
if [[ -e "arch/x86/boot/bzImage" ]]; then
kernel_image="arch/x86/boot/bzImage"
elif [[ -e "arch/arm64/boot/Image" ]]; then
kernel_image="arch/arm64/boot/Image"
fi
[ -n "$kernel_image" ] || usage "kernel image not found or not supported"
if file $kernel_image | grep -q x86; then
VM_ARCH=x86_64
elif file $kernel_image | grep -q ARM64; then
VM_ARCH=arm64
fi
[ -n "$VM_ARCH" ] || usage "kernel architecture not supported"
host "$kernel_image"
# }