>> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=973484 >> if you have interest... > That sounds like a major road block indeed, unfortunately...
I found a workaround, then I met another obstacle... as below: One can attach a USB serial to QEMU (5.1) arm emulater with machine "virt" by -device qemu-xhci -device usb-serial,chardev=somedefinedID. But QEMU USB serial is much different from QEMU serial. The USB serial is considered unplugged from VM by QEMU, when there is no process on host associated with the host socket corresponding to USB serial in VM!!!. On the other hand, autopkgtest_virt_qemu opens and closes the UNIX socket associated with VM's ttyS1 many times. I made attached changes to autopkgtest_virt_qemu and setup-testbed, and the testbed can be executed until some point as below. In order to have QEMU autopkgtestbed on arm, one of the following may be necessary: (1) Modify QEMU to allow multiple "-serial" as /dev/ttyAMA? in VM, or (2) Modify autopkgtest_virt_qemu to keep the socket to VM's ttyS1 open all the time. Both of them involves major design decision by the upstreams. So I stop my investigation here unless a good idea occurs. There seems no easy fix. # autopkgtest -u debci -B bash -- qemu --debug --efi /var/lib/debci/qemu/sid-arm64.img autopkgtest [11:34:32]: starting date: 2020-11-01 autopkgtest [11:34:32]: version 5.15 autopkgtest [11:34:32]: host raspi4-bullseye; command line: /usr/bin/autopkgtest -u debci -B bash -- qemu --debug --efi /var/lib/debci/qemu/sid-arm64.img autopkgtest-virt-qemu: DBG: executing open autopkgtest-virt-qemu: DBG: execute-timeout: qemu-img info --output=json /var/lib/debci/qemu/sid-arm64.img autopkgtest-virt-qemu: DBG: Creating temporary overlay image in /tmp/autopkgtest-qemu.scy36yci/overlay.img autopkgtest-virt-qemu: DBG: execute-timeout: qemu-img create -f qcow2 -F qcow2 -b /var/lib/debci/qemu/sid-arm64.img /tmp/autopkgtest-qemu.scy36yci/overlay.img autopkgtest-virt-qemu: DBG: find_free_port: trying 10022 autopkgtest-virt-qemu: DBG: find_free_port: 10022 is free autopkgtest-virt-qemu: DBG: Forwarding local port 10022 to VM ssh port 22 autopkgtest-virt-qemu: DBG: ['qemu-system-aarch64', '-m', '1024', '-smp', '1', '-nographic', '-net', 'nic,model=virtio', '-net', 'user,hostfwd=tcp:127.0.0.1:10022-:22', '-object', 'rng-random,filename=/dev/urandom,id=rng0', '-device', 'virtio-rng-pci,rng=rng0,id=rng-device0', '-monitor', 'unix:/tmp/autopkgtest-qemu.scy36yci/monitor,server,nowait', '-serial', 'unix:/tmp/autopkgtest-qemu.scy36yci/ttyS0,server,nowait', '-virtfs', 'local,id=autopkgtest,path=/tmp/autopkgtest-qemu.scy36yci/shared,security_model=none,mount_tag=autopkgtest', '-drive', 'file=/tmp/autopkgtest-qemu.scy36yci/overlay.img,cache=unsafe,if=virtio,index=0,format=qcow2', '-machine', 'virt', '-cpu', 'host', '-chardev', 'socket,id=usbserial,path=/tmp/autopkgtest-qemu.scy36yci/ttyS1,server', '-device', 'qemu-xhci', '-device', 'usb-serial,chardev=usbserial', '-drive', 'if=pflash,format=raw,read-only,file=/usr/share/AAVMF/AAVMF_CODE.fd', '-drive', 'if=pflash,format=raw,file=/tmp/autopkgtest-qemu.scy36yci/efivars.fd', '-enable-kvm'] qemu-system-aarch64: -chardev socket,id=usbserial,path=/tmp/autopkgtest-qemu.scy36yci/ttyS1,server: info: QEMU waiting for connection on: disconnected:unix:/tmp/autopkgtest-qemu.scy36yci/ttyS1,server autopkgtest-virt-qemu: DBG: expect: " login: " autopkgtest-virt-qemu: DBG: expect: found ""login prompt on ttyS0"" autopkgtest-virt-qemu: DBG: expect: "ok" autopkgtest-virt-qemu: DBG: expect: found ""b'ok'"" autopkgtest-virt-qemu: DBG: setup_shell(): there already is a shell on ttyS1 autopkgtest-virt-qemu: DBG: expect: "#" autopkgtest-virt-qemu: DBG: expect: found ""b'#'"" autopkgtest-virt-qemu: DBG: expect: "#" autopkgtest-virt-qemu: DBG: expect: found ""b'#'"" autopkgtest-virt-qemu: DBG: expect: "# " autopkgtest-virt-qemu: DBG: expect: found ""b'# '"" autopkgtest-virt-qemu: DBG: Copying host timezone Asia/Tokyo to VM autopkgtest-virt-qemu: DBG: expect: "#" autopkgtest-virt-qemu: DBG: expect: found ""b'#'"" autopkgtest-virt-qemu: DBG: expect: "/python" autopkgtest-virt-qemu: DBG: expect: found ""b'/python'"" autopkgtest-virt-qemu: DBG: expect: "# " autopkgtest-virt-qemu: DBG: expect: found ""b'# '"" autopkgtest-virt-qemu: DBG: execute-timeout: /tmp/autopkgtest-qemu.scy36yci/runcmd true qemu-system-aarch64: terminating on signal 15 from pid 25851 (/usr/bin/python3) autopkgtest-virt-qemu: DBG: cleanup... Unexpected error: Traceback (most recent call last): File "/usr/share/autopkgtest/lib/VirtSubproc.py", line 739, in mainloop command() File "/usr/share/autopkgtest/lib/VirtSubproc.py", line 668, in command r = f(c, ce) File "/usr/share/autopkgtest/lib/VirtSubproc.py", line 258, in cmd_open caller.hook_open() File "/usr/bin/autopkgtest-virt-qemu", line 648, in hook_open make_auxverb(shareddir) File "/usr/bin/autopkgtest-virt-qemu", line 449, in make_auxverb status = VirtSubproc.execute_timeout(None, 5, VirtSubproc.auxverb + ['true'])[0] File "/usr/share/autopkgtest/lib/VirtSubproc.py", line 144, in execute_timeout (out, err) = sp.communicate(instr) File "/usr/lib/python3.8/subprocess.py", line 1016, in communicate self.wait() File "/usr/lib/python3.8/subprocess.py", line 1079, in wait return self._wait(timeout=timeout) File "/usr/lib/python3.8/subprocess.py", line 1804, in _wait (pid, sts) = self._try_wait(0) File "/usr/lib/python3.8/subprocess.py", line 1762, in _try_wait (pid, sts) = os.waitpid(self.pid, wait_flags) File "/usr/share/autopkgtest/lib/VirtSubproc.py", line 64, in alarm_handler raise Timeout() VirtSubproc.Timeout autopkgtest [11:35:05]: ERROR: testbed failure: cannot send to testbed: [Errno 32] Broken pipe Ryutaroh
--- usr/bin/autopkgtest-virt-qemu-orig 2020-10-31 13:39:28.389726390 +0900 +++ usr/bin/autopkgtest-virt-qemu 2020-11-01 11:32:03.355228922 +0900 @@ -53,7 +53,7 @@ normal_user = None qemu_cmd_default = None image0_format = None - +term_ttyS1 = None def parse_args(): global args, qemu_cmd_default @@ -155,14 +155,11 @@ def check_ttyS1_shell(): '''Check if there is a shell running on ttyS1''' - term = VirtSubproc.get_unix_socket(os.path.join(workdir, 'ttyS1')) - term.sendall(b'echo -n o; echo k\n') + term_ttyS1.sendall(b'echo -n o; echo k\n') try: - VirtSubproc.expect(term, b'ok', 1) - term.close() + VirtSubproc.expect(term_ttyS1, b'ok', 1) return True except VirtSubproc.Timeout: - term.close() return False @@ -205,7 +202,10 @@ adtlog.debug('login_tty: logged in') VirtSubproc.expect(term, b'LOGIN_OK', 120, 'logged in') - cmd = b'sh </dev/ttyS1 >/dev/ttyS1 2>&1' + if "arm" in os.uname()[4] or "aarch64" == os.uname()[4]: + cmd = b'sh </dev/ttyUSB0 >/dev/ttyUSB0 2>&1' + else: + cmd = b'sh </dev/ttyS1 >/dev/ttyS1 2>&1' # if we are a non-root user, run through sudo if args.user != 'root': @@ -223,16 +223,14 @@ def setup_baseimage(): '''setup /dev/baseimage in VM''' - term = VirtSubproc.get_unix_socket(os.path.join(workdir, 'ttyS1')) - # Setup udev rules for /dev/baseimage; set link_priority to -1024 so # that the duplicate UUIDs of the partitions will have no effect. - term.sendall(b'''mkdir -p -m 0755 /run/udev/rules.d ; printf '# Created by autopkgtest-virt-qemu\\n%s\\n%s\\n%s\\n' 'KERNEL=="vd*[!0-9]", ENV{ID_SERIAL}=="BASEIMAGE", OPTIONS+="link_priority=-1024", SYMLINK+="baseimage", MODE="0664"' 'KERNEL=="vd*[0-9]", ENV{ID_SERIAL}=="BASEIMAGE", OPTIONS+="link_priority=-1024"' 'KERNEL=="vd*", ENV{ID_SERIAL}=="BASEIMAGE", ENV{ID_FS_TYPE}:="", ENV{ID_FS_USAGE}:="", ENV{ID_FS_UUID}:=""' > /run/udev/rules.d/61-baseimage.rules\n''') - VirtSubproc.expect(term, b'#', 10) + term_ttyS1.sendall(b'''mkdir -p -m 0755 /run/udev/rules.d ; printf '# Created by autopkgtest-virt-qemu\\n%s\\n%s\\n%s\\n' 'KERNEL=="vd*[!0-9]", ENV{ID_SERIAL}=="BASEIMAGE", OPTIONS+="link_priority=-1024", SYMLINK+="baseimage", MODE="0664"' 'KERNEL=="vd*[0-9]", ENV{ID_SERIAL}=="BASEIMAGE", OPTIONS+="link_priority=-1024"' 'KERNEL=="vd*", ENV{ID_SERIAL}=="BASEIMAGE", ENV{ID_FS_TYPE}:="", ENV{ID_FS_USAGE}:="", ENV{ID_FS_UUID}:=""' > /run/udev/rules.d/61-baseimage.rules\n''') + VirtSubproc.expect(term_ttyS1, b'#', 10) # Reload udev to make sure the rules take effect (udev only auto- # rereads rules every 3 seconds) - term.sendall(b'udevadm control --reload\n') - VirtSubproc.expect(term, b'#', 10) + term_ttyS1.sendall(b'udevadm control --reload\n') + VirtSubproc.expect(term_ttyS1, b'#', 10) # Add the base image as an additional drive monitor = VirtSubproc.get_unix_socket(os.path.join(workdir, 'monitor')) @@ -250,9 +248,7 @@ def setup_shared(shared_dir): '''Set up shared dir''' - term = VirtSubproc.get_unix_socket(os.path.join(workdir, 'ttyS1')) - - term.sendall(b'''mkdir -p -m 1777 /run/autopkgtest/shared + term_ttyS1.sendall(b'''mkdir -p -m 1777 /run/autopkgtest/shared mount -t 9p -o trans=virtio,access=any autopkgtest /run/autopkgtest/shared chmod 1777 /run/autopkgtest/shared touch /run/autopkgtest/shared/done_shared @@ -262,11 +258,11 @@ flag = os.path.join(shared_dir, 'done_shared') while not os.path.exists(flag): time.sleep(0.2) - VirtSubproc.expect(term, b'#', 30) + VirtSubproc.expect(term_ttyS1, b'#', 30) # ensure that root has $HOME set - term.sendall(b'[ -n "$HOME" ] || export HOME=`getent passwd root|cut -f6 -d:`\n') - VirtSubproc.expect(term, b'#', 5) + term_ttyS1.sendall(b'[ -n "$HOME" ] || export HOME=`getent passwd root|cut -f6 -d:`\n') + VirtSubproc.expect(term_ttyS1, b'#', 5) # create helper for runcmd: cat data from its stdin (from a file) to stdout # eternally (like tail -f), but stop once either an "EOF" file exists and @@ -274,7 +270,7 @@ # arg), or an "exit flag" file exists. # We don't run that from /run/autopkgtest/shared as 9p from older QEMU # versions is buggy and causes "invalid numeric result" errors on that. - term.sendall(b'''PYTHON=$(which python3) || PYTHON=$(which python); cat <<EOF > /tmp/eofcat; chmod 755 /tmp/eofcat + term_ttyS1.sendall(b'''PYTHON=$(which python3) || PYTHON=$(which python); cat <<EOF > /tmp/eofcat; chmod 755 /tmp/eofcat #!$PYTHON import sys, os, fcntl, time, errno (feof, fexit) = sys.argv[1:] @@ -304,14 +300,12 @@ break EOF ''') - VirtSubproc.expect(term, b'# ', 5) - term.close() + VirtSubproc.expect(term_ttyS1, b'# ', 5) def setup_config(shared_dir): '''Set up configuration files''' - term = VirtSubproc.get_unix_socket(os.path.join(workdir, 'ttyS1')) # copy our timezone, to avoid time skews with the host if os.path.exists('/etc/timezone'): @@ -327,22 +321,20 @@ if tz: adtlog.debug('Copying host timezone %s to VM' % tz.decode()) - term.sendall(b'echo ' + tz + b' > /etc/timezone; DEBIAN_FRONTEND=noninteractive dpkg-reconfigure tzdata\n') - VirtSubproc.expect(term, b'#', 30) + term_ttyS1.sendall(b'echo ' + tz + b' > /etc/timezone; DEBIAN_FRONTEND=noninteractive dpkg-reconfigure tzdata\n') + VirtSubproc.expect(term_ttyS1, b'#', 30) else: adtlog.debug('Could not determine host timezone') # ensure that we have Python for our the auxverb helpers - term.sendall(b'type python3 2>/dev/null || type python 2>/dev/null\n') + term_ttyS1.sendall(b'type python3 2>/dev/null || type python 2>/dev/null\n') try: - out = VirtSubproc.expect(term, b'/python', 5) + out = VirtSubproc.expect(term_ttyS1, b'/python', 5) except VirtSubproc.Timeout: VirtSubproc.bomb('Neither python3 nor python is installed in the VM, ' 'one of them is required by autopkgtest') if b'\n# ' not in out: - VirtSubproc.expect(term, b'# ', 5) - - term.close() + VirtSubproc.expect(term_ttyS1, b'# ', 5) def make_auxverb(shared_dir): @@ -452,6 +444,7 @@ VirtSubproc.auxverb = [auxverb] + term_ttyS1.close() # verify that we can connect status = VirtSubproc.execute_timeout(None, 5, VirtSubproc.auxverb + ['true'])[0] if status == 0: @@ -531,8 +524,7 @@ # get the first UID in the Debian Policy ยง9.2.2 "dynamically allocated # user account" range - term = VirtSubproc.get_unix_socket(os.path.join(workdir, 'ttyS1')) - term.sendall(b"getent passwd | sort -t: -nk3 | " + term_ttyS1.sendall(b"getent passwd | sort -t: -nk3 | " b"awk -F: '{if ($3 >= 1000 && $3 <= 59999) { print $1; exit } }'" b"> /run/autopkgtest/shared/normal_user\n") with VirtSubproc.timeout(5, 'timed out on determining normal user'): @@ -546,7 +538,6 @@ adtlog.debug('determine_normal_user: got user "%s"' % normal_user) else: adtlog.debug('determine_normal_user: no uid in [1000,59999] available') - term.close() def hook_open(): @@ -578,11 +569,25 @@ '-object', 'rng-random,filename=/dev/urandom,id=rng0', '-device', 'virtio-rng-pci,rng=rng0,id=rng-device0', '-monitor', 'unix:%s/monitor,server,nowait' % workdir, '-serial', 'unix:%s/ttyS0,server,nowait' % workdir, - '-serial', 'unix:%s/ttyS1,server,nowait' % workdir, '-virtfs', 'local,id=autopkgtest,path=%s,security_model=none,mount_tag=autopkgtest' % shareddir, '-drive', 'file=%s,cache=unsafe,if=virtio,index=0,format=qcow2' % overlay] + if "arm" in os.uname()[4] or "aarch64" == os.uname()[4]: + argv.append('-machine') + argv.append('virt') + argv.append('-cpu') + argv.append('host') + argv.append('-chardev') + argv.append('socket,id=usbserial,path=%s/ttyS1,server' % workdir) + argv.append('-device') + argv.append('qemu-xhci') + argv.append('-device') + argv.append('usb-serial,chardev=usbserial') + else: + argv.append('-serial') + argv.append('unix:%s/ttyS1,server' % workdir) + if args.efi: code = None data = None @@ -623,7 +628,10 @@ if args.qemu_options: argv.extend(args.qemu_options.split()) + adtlog.debug(str(argv)) p_qemu = subprocess.Popen(argv) + global term_ttyS1 + term_ttyS1 = VirtSubproc.get_unix_socket(os.path.join(workdir, 'ttyS1')) try: try: --- usr/share/autopkgtest/setup-commands/setup-testbed-orig 2020-10-31 15:24:46.526303266 +0900 +++ usr/share/autopkgtest/setup-commands/setup-testbed 2020-11-01 11:32:59.930156349 +0900 @@ -5,7 +5,7 @@ # autopkgtest is Copyright (C) 2006-2014 Canonical Ltd. # # Setup script for e. g. vmdebootstrap, generic Debian/Ubuntu VM or container -# images to start a root serial console on ttyS1, set up networking for +# images to start a root serial console on ttyUSB0, set up networking for # ethernet, configure apt sources, install necessary and clean up unnecessary # packages, etc. This can be used both for building tailored autopkgtest images # as well as on a per-test basis as --setup-commands script (then some steps @@ -35,9 +35,9 @@ root=${1:-/} -# set up init script for root shell on ttyS1; necessary for autopkgtest-virt-qemu local +# set up init script for root shell on ttyUSB0; necessary for autopkgtest-virt-qemu local # images -if [ "$root" != "/" ] || [ -e /dev/ttyS1 ]; then +if [ "$root" != "/" ] || [ -e /dev/ttyUSB0 ]; then cat <<EOF > "$root/etc/init.d/autopkgtest" #!/bin/sh ### BEGIN INIT INFO @@ -49,8 +49,8 @@ ### END INIT INFO if [ "\$1" = start ]; then - echo "Starting root shell on ttyS1 for autopkgtest" - (setsid sh </dev/ttyS1 >/dev/ttyS1 2>&1) & + echo "Starting root shell on ttyUSB0 for autopkgtest" + (setsid sh </dev/ttyUSB0 >/dev/ttyUSB0 2>&1) & fi EOF @@ -60,17 +60,17 @@ if [ -d "$root/etc/systemd/system" ]; then cat <<EOF > "$root/etc/systemd/system/autopkgtest.service" [Unit] -Description=autopkgtest root shell on ttyS1 -ConditionPathExists=/dev/ttyS1 +Description=autopkgtest root shell on ttyUSB0 +ConditionPathExists=/dev/ttyUSB0 [Service] ExecStart=/bin/sh StandardInput=tty-fail StandardOutput=tty StandardError=tty -TTYPath=/dev/ttyS1 +TTYPath=/dev/ttyUSB0 SendSIGHUP=yes -# ignore I/O errors on unusable ttyS1 +# ignore I/O errors on unusable ttyUSB0 SuccessExitStatus=0 208 SIGHUP SIGINT SIGTERM SIGPIPE [Install] @@ -82,9 +82,9 @@ fi # serial console for upstart -if [ -e "$root/etc/init/tty2.conf" -a ! -e "$root/etc/init/ttyS0.conf" ]; then - sed 's/tty2/ttyS0/g; s! *exec.*$!exec /sbin/getty -L ttyS0 115200 vt102!' \ - "$root/etc/init/tty2.conf" > "$root/etc/init/ttyS0.conf" +if [ -e "$root/etc/init/tty2.conf" -a ! -e "$root/etc/init/ttyAMA0.conf" ]; then + sed 's/tty2/ttyAMA0/g; s! *exec.*$!exec /sbin/getty -L ttyAMA0 115200 vt102!' \ + "$root/etc/init/tty2.conf" > "$root/etc/init/ttyAMA0.conf" fi ARCH="$(chroot "$root" dpkg --print-architecture)" @@ -102,6 +102,10 @@ echo 'GRUB_CMDLINE_LINUX_DEFAULT="console=ttyS0"' > \ "$root/etc/default/grub.d/90-autopkgtest.cfg" changed=1 + elif [ "$ARCH" = "arm64" -o "$ARCH" = "armhf" -o "$ARCH" = "armel" ]; then + echo 'GRUB_CMDLINE_LINUX_DEFAULT="console=ttyAMA0"' > \ + "$root/etc/default/grub.d/90-autopkgtest.cfg" + changed=1 fi else # fallback for Ubuntu 12.04 @@ -111,6 +115,9 @@ elif [ "$ARCH" = "amd64" ]; then sed -i '/CMDLINE_LINUX_DEFAULT/ s/"$/ console=ttyS0"/' "$root/etc/default/grub" changed=1 + elif [ "$ARCH" = "arm64" -o "$ARCH" = "armhf" -o "$ARCH" = "armel" ]; then + sed -i '/CMDLINE_LINUX_DEFAULT/ s/"$/ console=ttyAMA0"/' "$root/etc/default/grub" + changed=1 fi if ! grep -q GRUB_HIDDEN_TIMEOUT=0 "$root/etc/default/grub" ; then sed -i '/^GRUB_TIMEOUT=/ s/=.*$/=1/' "$root/etc/default/grub"