>> 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"

Reply via email to