Skip to content

Commit d9d72c0

Browse files
committed
tools: labs: qemu: Add run-qemu.sh as alternative
TL;DR: In one window run `make -j$(ncproc) console` and `cd` to some lab's subdirectory (`skels/...`). In second window `cd` to matching `skels/...` subdirectory, edit source files and compile with something like `kmake` (`alias kmake='make -C "$HOME/src/linux/" M="$(pwd)"'`) as needed. The `skels` directory is shared between the host and the guest, thus in the first window, so you can just `insmod` and `rmmod` the compiled modules. You can kill the VM by `CTRL-a x` (if you made some writes from the VM it might be a good idea to run `sync` first). Samba and QEMU are required. Full description: To be honest I don't like the current QEMU setup. I am sure there are things it does that I don't understand yet, because I have not yet finished all the labs, but in any case I think that the setup can be improved. Some things in particular I don't like about the current setup: - "Huge" opaque `.ext4` images are used, even though the contents of the root file system are not that large. - While running QEMU newly built modules can't be copied to the image. - Mounting and unmounting the `.ext4` image for copying the modules requires `sudo`. - The networking setup seems too complex, requires `sudo` and was broken (at least for me - IIRC I didn't get IP through DHCP), thus I also didn't get `ssh` to work. I also seem to be not the only one having issues with this: linux-kernel-labs#240 (comment) - `dnsmasq` and `nttctp` mostly don't start correctly (they are not killed correctly by the previous run) - this isn't a problem on my end, as demonstrated by the output at https://linux-kernel-labs.github.io/refs/heads/master/info/vm.html, which shows the same issues. - Running `minicom` is required to access the serial port, thus at least two terminals are required for working with the VM (not a huge problem for me personally, since I use `tmux`, but I know some people complain about this). The setup also seems unnecessarily complex. - I remember a lot of the `.mon` `.pts` files being left uncleaned in some cases. - I recall warnigns about some of the entries added to `/etc/inittab` being ignored. - Even though root login requires no password I have to enter the `root` username. In this commit I introdoce an alternative way of running QEMU through a new `run-qemu.sh` script. The setup is laregely independent and its user interface consists of `console` and `gui` targets. I tried to make the script parameterizable through environment variables (inherited from Make variables), though it may be argued that the default values should be encoded in Makefile rather than in the script like they are now. I have no strong opinions about that, it's' just that the current state allows running the script in standalone fashion. What the setup brings: - A rootfs is extracted from the official Yocto Project tarball and kept in a directory that is shared through [Samba as network share](https://www.kernel.org/doc/html/latest/filesystems/cifs/cifsroot.html). The `skels` directory is shared as well. Thus the modules can be freely tweaked / compiled / ran from either the host or guest. - The QEMU stdio serial console setup is used (`ttyS0` on the kernel side). This means that running QEMU results in the serial console being mapped directly to standard input and output of the terminal - `minicom` is not needed. This is the console mode (`make console`). - The setup allows also allows the virtual machine to be run in graphical mode (`make gui`). - Root is logged in automatically in `console` mode (though similar thing could be done for the `gui` mode). - Although Samba (`smbd`) is required, `sudo` or root access is not. - Networking through QEMU's default [SLIRP backend](https://wiki.qemu.org/Documentation/Networking#User_Networking_.28SLIRP.29). DHCP is handled by the kernel, which overcomes some problems I had with the System V init system in the Yocto images. - The compilation can largely be done with something like this `kmake` alias: `alias kmake='make -C "$HOME/src/linux/" M="$(pwd)"'` (customize as needed). Though this is not enough for some labs (I no longer remember the details, but I think it was some of the earlier labs which had dependencies between modules, I think I used the classic `make build` for that. Known issues: - SSH support is currently missing. This both requires more featureful Yocto images and is IMO unnecessary, since it wouldn't bring much benefit over the console mode. Though it can be easily achieved by using QEMU option like `-nic user,hostfwd=tcp::2222-:22`, which would allow SSH'ing into the guest by something like `ssh -p 2222 root@localhost`. - I used a slightly less advanced setup while doing the labs, so the lab workflow with this particular setup is largely untested. There may be problems with file permissions due to the samba share. - The guest seems to fail to shutdown correctly in a timely manner. I just took the habbit of killing qemu with `CTRL-a` followed by `x`, potentially running `sync` first to ensure my work is saved (though rarely did I actually modify anything on the guest side). [The former setup](720bd64) I used contains some details of the SSH setup if anyone is interested in that. It was the basis for this PR, so some ideas can be seen there (Samba share for `skels`), but I didn't take particular care with [the kernel config](0290919) and the automounting didn't really work (the `init` would try to mount the filesystem before networking was up). What I evaluated and didn't use in the end: - At first I tried to extend my former setup by just automounting the Samba share. I didn't manage to do this - the (non)workings of init scripts seem to be beyond me. If anyone is interested here are a few pointers: [[1]](https://unix.stackexchange.com/questions/169697/how-does-netdev-mount-option-in-etc-fstab-work), `/etc/inittab`, `/etc/init.d/mountall.sh`, `/etc/init.d/mountnfs.sh`. - I tried using `9p` [[2]](https://wiki.qemu.org/Documentation/9p), [[3]](https://wiki.qemu.org/Documentation/9psetup) [[4]](https://wiki.qemu.org/Documentation/9p_root_fs) which is built into QEMU and can be compiled into the kernel. With `mapped-xattr` security model it would be too cumbersome to create the rootfs, and `passthrough` would require root privileges. It is also very slow. There are also some problems with trying to use it as rootfs, maybe specific to `linux-kernel-labs` kernel version or config. Ask me if interested. [[5]](https://lists.gnu.org/archive/html/qemu-devel/2016-09/msg07184.html) [[6]](https://lore.kernel.org/linux-fsdevel/[email protected]/) [[7]](https://lore.kernel.org/all/[email protected]/T/) - QEMU has an option to setup the Samba share on its own, though I found a custom config (based on the QEMU one) to be easier - allows customization like multiple shares, unix extensions, different port, etc.
1 parent 0252285 commit d9d72c0

File tree

4 files changed

+169
-1
lines changed

4 files changed

+169
-1
lines changed

tools/labs/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ rootfs.img
66
disk1.img
77
disk2.img
88
/*core-image-*.ext4
9+
/*core-image-*.bz2
910
.modinst
1011
/out/
12+
/rootfs

tools/labs/qemu/Makefile

+38-1
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,41 @@ clean::
9595
-rm -f disk1.img disk2.img
9696
-rm -f pipe1.in pipe1.out pipe2.in pipe2.out
9797

98-
.PHONY: boot gdb clean tap0 tap1
98+
# Run with one of the following:
99+
# make -j$(nproc) console
100+
# make -j$(nproc) gui
101+
102+
# Attach debugger with
103+
# make gdb
104+
105+
# Stop with
106+
# sync # make sure all filesystem changes are propagated
107+
# CTRL-A X # kill qemu, faster and more reliable than poweroff etc.
108+
109+
# Compile in skel directories with something like:
110+
# alias kmake='make -C "$HOME/src/linux/" M="$(pwd)"'
111+
# kmake
112+
113+
YOCTO_ROOTFS = core-image-minimal-qemu$(ARCH).tar.bz2
114+
115+
console: $(ZIMAGE) rootfs
116+
MODE=console qemu/run-qemu.sh
117+
118+
gui:
119+
MODE=gui qemu/run-qemu.sh
120+
121+
rootfs: $(YOCTO_ROOTFS)
122+
mkdir -p rootfs
123+
tar -xf core-image-minimal-qemux86.tar.bz2 -C rootfs
124+
sed -i 's@/sbin/getty@& -n -l "/sbin/rootlogin"@' rootfs/bin/start_getty
125+
printf '%s\n' '#!/bin/sh' '/bin/login -f root' > rootfs/sbin/rootlogin
126+
chmod +x rootfs/sbin/rootlogin
127+
mkdir -p rootfs/home/root/skels
128+
echo "//10.0.2.2/skels /home/root/skels cifs port=4450,guest,user=dummy 0 0" >> rootfs/etc/fstab
129+
130+
$(YOCTO_ROOTFS):
131+
wget $(YOCTO_URL)/$(YOCTO_ROOTFS)
132+
133+
134+
135+
.PHONY: console gui boot gdb clean tap0 tap1

tools/labs/qemu/kernel_config.x86

+15
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,18 @@ CONFIG_UPROBE_EVENTS=y
9797
CONFIG_LOCKDEP=y
9898
# kernel lock tracing:
9999
CONFIG_LOCK_STAT=y
100+
101+
# for qemu serial console ttyS0:
102+
CONFIG_SERIAL_8250=y
103+
CONFIG_SERIAL_8250_CONSOLE=y
104+
105+
# for qemu default netdev:
106+
CONFIG_E1000=y
107+
108+
# for CIFS/SMB/samba rootfs and share
109+
CONFIG_IP_PNP=y
110+
CONFIG_IP_PNP_DHCP=y
111+
CONFIG_CIFS=y
112+
CONFIG_CIFS_XATTR=y
113+
CONFIG_CIFS_POSIX=y
114+
CONFIG_CIFS_ROOT=y

tools/labs/qemu/run-qemu.sh

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/bin/bash
2+
3+
die() { echo "$0: error: $@" >&2; exit 1; }
4+
5+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null && pwd)"
6+
7+
base_dir=${ROOT:-"$(readlink -f "$script_dir/..")"}
8+
arch=${ARCH:-"x86"}
9+
kernel=${ZIMAGE:-"$(readlink -f "$base_dir/../../arch/$arch/boot/bzImage")"}
10+
rootfs=${ROOTFS:-"$(readlink -f "$base_dir/rootfs")"}
11+
skels=${SKELS:-"$(readlink -f "$base_dir/skels")"}
12+
13+
mode="${MODE:-console}"
14+
case "$mode" in
15+
console)
16+
qemu_display="-nographic"
17+
linux_console="console=ttyS0"
18+
;;
19+
gui)
20+
# QEMU_DISPLAY = sdl, gtk, ...
21+
qemu_display="-display ${QEMU_DISPLAY:-"sdl"}"
22+
linux_console=""
23+
;;
24+
*) echo "unknown mode '$MODE'" >&2; exit 1 ;;
25+
esac
26+
27+
case "$arch" in
28+
x86) qemu_arch=i386 ;;
29+
arm) qemu_arch=arm ;;
30+
*) echo "unknown architecture '$arch'" >&2; exit 1 ;;
31+
esac
32+
33+
smbd=${SMBD:-"smbd"}
34+
35+
qemu=${QEMU:-"qemu-system-$qemu_arch"}
36+
qemu_kvm=${QEMU_KVM:-"-enable-kvm -cpu host"}
37+
qemu_cpus=${QEMU_CPUS:-"1"}
38+
qemu_mem=${QEMU_MEM:-"512"}
39+
qemu_display=${QEMU_DISPLAY:-"$qemu_display"}
40+
qemu_addopts=${QEMU_ADD_OPTS:-""}
41+
linux_console=${LINUX_CONSOLE:-"$linux_console"}
42+
linux_loglevel=${LINUX_LOGLEVEL:-"15"}
43+
linux_term=${LINUX_TERM:-"TERM=xterm"}
44+
linux_addcmdline=${LINUX_ADD_CMDLINE:-""}
45+
46+
linux_cmdline=${LINUX_CMDLINE:-"root=/dev/cifs rw ip=dhcp cifsroot=//10.0.2.2/rootfs,port=4450,guest,user=dummy $linux_console loglevel=$linux_loglevel $linux_term $linux_addcmdline"}
47+
48+
tmp_dir=$(mktemp -d)
49+
user=$(id -un)
50+
51+
cat << EOF > "$tmp_dir/smbd.conf"
52+
[global]
53+
interfaces = 127.0.0.1
54+
smb ports = 4450
55+
private dir = $tmp_dir
56+
bind interfaces only = yes
57+
pid directory = $tmp_dir
58+
lock directory = $tmp_dir
59+
state directory = $tmp_dir
60+
cache directory = $tmp_dir
61+
ncalrpc dir = $tmp_dir/ncalrpc
62+
log file = $tmp_dir/log.smbd
63+
smb passwd file = $tmp_dir/smbpasswd
64+
security = user
65+
map to guest = Bad User
66+
load printers = no
67+
printing = bsd
68+
disable spoolss = yes
69+
usershare max shares = 0
70+
71+
server min protocol = NT1
72+
unix extensions = yes
73+
74+
server role = standalone server
75+
public = yes
76+
writeable = yes
77+
#admin users = root
78+
#create mask = 0777
79+
#directory mask = 0777
80+
force user = $user
81+
force group = $user
82+
83+
84+
[rootfs]
85+
path = $rootfs
86+
[skels]
87+
path = $skels
88+
EOF
89+
90+
[ -x "$(command -v "$smbd")" ] || die "samba ('$smbd') not found"
91+
[ -x "$(command -v "$qemu")" ] || die "qemu ('$qemu') not found"
92+
93+
mkdir -p "$skels"
94+
95+
"$smbd" --no-process-group -s "$tmp_dir/smbd.conf" -l "$tmp_dir" >/dev/null 2>/dev/null &
96+
97+
"$qemu" \
98+
$qemu_kvm \
99+
-smp "$qemu_cpus" -m "$qemu_mem" \
100+
-no-reboot \
101+
-kernel "$kernel" \
102+
-append "$linux_cmdline" \
103+
-gdb tcp::1234 \
104+
$qemu_display \
105+
$qemu_addopts
106+
107+
# This seems to reset to the mode the terminal was prior to launching QEMU
108+
# Inspired by
109+
# https://github.com/landley/toybox/blob/990e0e7a40e4509c7987a190febe5d867f412af6/toys/other/reset.c#L26-L28
110+
# man 4 console_codes, ESC [ ? 7 h
111+
printf '\e[?7h'
112+
113+
pkill -F "$tmp_dir/smbd.pid"
114+
rm -rf "$tmp_dir"

0 commit comments

Comments
 (0)