0 前言
转载请附上原文出处链接
稀土掘金本文链接:juejin.cn/spost/74181…
CSDN本文链接:blog.csdn.net/qq_43252610…
1 问题概述
1.1 基础环境
【系统】Kylin-Desktop-V10-GFB-Release-2207-Build11-ARM64.iso(5.4.18-65-generic)
【CPU】vCPU
【显卡】virtio-gpu
1.2 问题现象
虚拟机添加多个串口设备后,启动失败
报错信息
kvm: -device isa-serial,chardev=serial1: 'isa-serial' is not a valid device model name
TASK ERROR: start failed: QEMU exited with code 1
2 问题定位
从报错来看,是isa-serial这种类型不支持
2.1 X86架构对比验证
PVE也有x86架构的版本,x86架构添加多个串口设备并不会出现启动失败的问题
对比x86和aarch64下qemu支持的device
x86:
/usr/bin/qemu-system-x86_64 -device help | grep serial
name "isa-serial", bus ISA
name "pci-serial", bus PCI
name "pci-serial-2x", bus PCI
name "pci-serial-4x", bus PCI
name "usb-serial", bus usb-bus
name "virtconsole", bus virtio-serial-bus
name "virtio-serial-device", bus virtio-bus
name "virtio-serial-pci", bus PCI, alias "virtio-serial"
name "virtio-serial-pci-non-transitional", bus PCI
name "virtio-serial-pci-transitional", bus PCI
name "virtserialport", bus virtio-serial-bus
aarch64:
/usr/bin/qemu-system-aarch64 -device help | grep serial
name "pci-serial", bus PCI
name "pci-serial-2x", bus PCI
name "pci-serial-4x", bus PCI
name "usb-serial", bus usb-bus
name "virtconsole", bus virtio-serial-bus
name "virtio-serial-device", bus virtio-bus
name "virtio-serial-pci", bus PCI, alias "virtio-serial"
name "virtio-serial-pci-non-transitional", bus PCI
name "virtio-serial-pci-transitional", bus PCI
name "virtserialport", bus virtio-serial-bus
可以看出aarch64架构中确实不包含isa-serial设备
2.2 kvm启动参数分析
那么aarch64架构下,第一个串口设备是如何正常工作的呢?
对比下虚拟机的PVE配置文件的串口部分(去掉了无关信息)
x86:
cat /etc/pve/qemu-server/103.conf
serial0: socket
serial1: socket
aarch64:
cat /etc/pve/qemu-server/142.conf
serial0: socket
serial1: socket
从配置文件来看,串口配置信息是完全一致的
PVE底层是使用kvm启动虚拟机,对比看一下虚拟机在kvm的启动参数的串口部分(去掉了无关信息)
PVE自身提供对应的接口查看
x86:
qm showcmd 103 --pretty
-chardev 'socket,id=serial0,path=/var/run/qemu-server/103.serial0,server=on,wait=off' \
-device 'isa-serial,chardev=serial0' \
-chardev 'socket,id=serial1,path=/var/run/qemu-server/103.serial1,server=on,wait=off' \
-device 'isa-serial,chardev=serial1' \
aarch64:
qm showcmd 142 --pretty
-chardev 'socket,id=serial0,path=/var/run/qemu-server/142.serial0,server=on,wait=off' \
-serial chardev:serial0 \
-chardev 'socket,id=serial1,path=/var/run/qemu-server/142.serial1,server=on,wait=off' \
-device 'isa-serial,chardev=serial1' \
x86架构下第一个串口设备配置的是-device 'isa-serial,chardev=serial0',aarch64架构下第一个串口设备配置的却是-serial chardev:serial0
这也是为什么aarch64架构下第一个串口是能正常启动的,因为第一个串口没有使用isa-serial设备
PVE的虚拟机配置文件到kvm启动参数之间的转换,是由PVE实现并控制的,同样的虚拟机配置文件出现不同架构下的不同表现,需要分析PVE的相关代码
2.3 PVE代码分析
PVE的github官方只读镜像项目路径:github.com/proxmox
PVE的虚拟机配置文件到kvm启动参数之间的转换代码位于qemu-server的组件中,具体实现的代码文件如下:github.com/proxmox/qem…
# serial devices
for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
my $path = $conf->{"serial$i"} or next;
if ($path eq 'socket') {
my $socket = "/var/run/qemu-server/${vmid}.serial$i";
push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
# On aarch64, serial0 is the UART device. QEMU only allows
# connecting UART devices via the '-serial' command line, as
# the device has a fixed slot on the hardware...
if ($arch eq 'aarch64' && $i == 0) {
push @$devices, '-serial', "chardev:serial$i";
} else {
push @$devices, '-device', "isa-serial,chardev=serial$i";
}
} else {
die "no such serial device\n" if ! -c $path;
push @$devices, '-chardev', "serial,id=serial$i,path=$path";
push @$devices, '-device', "isa-serial,chardev=serial$i";
}
}
PVE在aarch64的架构情况下,第一个串口设备会进行特殊处理,添加的实际是一个UART设备
2.3.1 PVE对于最大串口数量的限制
在这里稍微多说一点,MAX_SERIAL_PORTS的值等于4
因为isa-serial支持的最大数量是4,如果虚拟机启动时超过4个isa-serial时,qemu会报错
kvm: -device isa-serial,chardev=serial4: Max. supported number of ISA serial ports is 4.
然而使用libvirt管理并创建的虚拟机并不会有最大4个串口数量的限制,这是因为两者对于串口设备的创建方式及设备选型的不同
后续章节会简单解析libvirt在串口这一块的处理
2.4 定位结论
如上述分析,虚拟机多串口启动失败的原因为:aarch64架构下kvm不支持isa-serial的设备类型
3 机理分析
3.1 ISA总线介绍
以下内容引用维基百科:zh.wikipedia.org/wiki/工业标准结构
工业标准结构(Industry Standard Architecture,简称ISA)是IBM PC兼容机上的一种总线。
ISA在1981年诞生,并作为IBM PC的8位系统,1983年,ISA被升级作为XT总线体系。后来16位的ISA总线在1984年发布。由于ISA设计出来的目的是为了连接扩展卡和主板,因此ISA的协议同样允许总线控制,尽管只有前16MB的存储器可以直接访问。
PCI是第一个在物理展上集成了ISA、MCA、EISA优点的扩展接口,并且它的出现直接地挤压了ISA在主板上的地位。起初,主板上依然是ISA占主流地位,但已经出现了PCI槽了。到了20世纪90年代中叶,两种插槽已经在主板上平分秋色了,而ISA插槽很快就在消费PC市场上成为了少数派。微软的PC 97规范更劝说ISA插槽应该完全被除去,尽管当时的系统架构依然需要ISA存在于一些内部发育不良的管线去操作软驱、串口、等等。ISA接口在随后的几年里依然存在,甚至看见AGP接口的诞生,之后遗留在主板上的ISA接口也退出历史了。
虽然物理设备上ISA已经退出历史了,但是在PVE中,串口仍然默认使用的是ISA总线的串口设备
从上述介绍来看,aarch64架构不支持ISA总线也是完全正常的,毕竟ISA总线已经是历史遗留的产物了
从qemu中也能看出aarch64架构完全不支持ISA总线
x86:
/usr/bin/qemu-system-x86_64 -device help | grep isa
name "isa-fdc", bus ISA, desc "virtual floppy controller"
name "isa-ide", bus ISA
name "ne2k_isa", bus ISA
name "isa-parallel", bus ISA
name "isa-serial", bus ISA
name "isa-cirrus-vga", bus ISA
name "isa-vga", bus ISA
name "isa-applesmc", bus ISA
name "isa-debug-exit", bus ISA
name "isa-debugcon", bus ISA
name "isa-ipmi-bt", bus ISA
name "isa-ipmi-kcs", bus ISA
aarch64:
/usr/bin/qemu-system-aarch64 -device help | grep isa
4 解决措施
根据问题定位和机理分析章节,解决该问题需要更换aarch64架构下默认创建的串口设备类型
4.1 libvirt中的串口设备默认类型
libvirt是另一套提供管理虚拟化平台的工具包,可以参考一下libvirt在aarch64架构下是如何创建串口设备的
libvirt是使用xml格式管理虚拟机的配置文件,和PVE类似,虚拟机配置文件到kvm启动参数的转换逻辑是由libvirt处理的
virt-manager提供基于libvirt图形化界面管理虚拟机,看一下aarch64架构下默认添加的串口类型
重点关注写入的xml文件内容
<serial type="pty">
<target type="system-serial" port="2">
<model name="pl011"/>
</target>
</serial>
以下内容引用libvirt的官方文档:libvirt.org/formatdomai…
The target element can have an optional port attribute, which specifies the port number (starting from 0), and an optional type attribute: valid values are, since 1.0.2, isa-serial (usable with x86 guests), usb-serial (usable whenever USB support is available) and pci-serial (usable whenever PCI support is available); since 3.10.0, spapr-vio-serial (usable with ppc64/pseries guests), system-serial (usable with aarch64/virt and, since 4.7.0, riscv/virt guests), sclp-serial (usable with s390 and s390x guests) are available as well and since 8.1.0 isa-debug (usable with x86 guests).
Since 3.10.0, the target element can have an optional model subelement; valid values for its name attribute are: isa-serial (usable with the isa-serial target type); usb-serial (usable with the usb-serial target type); pci-serial (usable with the pci-serial target type); spapr-vty (usable with the spapr-vio-serial target type); pl011 and, since 4.7.0, 16550a (usable with the system-serial target type); sclpconsole and sclplmconsole (usable with the sclp-serial target type). Since: 8.1.0, isa-debugcon (usable with the isa-debug target type); provides a virtual console for receiving debug messages from the firmware on x86 platforms. Providing a target model is usually unnecessary: libvirt will automatically pick one that's suitable for the chosen target type, and overriding that value is generally not recommended.
If any of the attributes is not specified by the user, libvirt will choose a value suitable for most users.
Most target types support configuring the guest-visible device address as documented in the Device Addresses section; more specifically, acceptable address types are isa (for isa-serial), usb (for usb-serial), pci (for pci-serial) and spapr-vio (for spapr-vio-serial). The system-serial and sclp-serial target types don't support specifying an address.
For the relationship between serial ports and consoles, refer to the Relationship between serial ports and consoles section.
-
serial:有两种类型
pty以伪终端(Pseudo Terminal)形式创建,可以用source标签指定物理机中的pts终端设备,例如<source path='/dev/pts/3'/>file以文件形式创建,可以用source标签指定物理机中的文件路径,例如<source path='/tmp/file' append='on'> -
target:类型很多,可以参考引用内容,其中aarch64架构下默认类型为sysem-serial -
model:类型很多,可以参考引用内容,其中aarch64架构下默认类型为pl011
最终虚拟机的kvm启动参数,可以在虚拟机启动后用ps -aux | grep kvm命令查看
-chardev pty,id=charserial0
-serial chardev:charserial0
-chardev pty,id=charserial1
-serial chardev:charserial1
-chardev pty,id=charserial2
-serial chardev:charserial2
对比PVE平台的虚拟机的kvm启动参数,libvirt其实是创建UART类型的串口设备(chardev类型libvirt使用的是pty的形式,PVE使用的是socket的形式)
注意:libvirt此时添加的多个串口设备,实际只有一个设备生效,后续章节会进行详细说明
4.2 qemu monitor
这里简单提一下qemu monitor几个调试命令,后续确认设备类型和设备状态都会用到
开启qemu monitor需要在虚拟机的kvm启动参数中添加-monitor stdio,且确保没有-daemonize参数
开启后在终端通过命令启动虚拟机,会自动跳转到qemu monitor界面
4.2.1 info pci
显示虚拟机的pci设备,例如
info pci
Bus 0, device 30, function 0:
PCI bridge: PCI device 1b36:000c
IRQ 255, pin A
BUS 0.
secondary bus 1.
subordinate bus 1.
IO range [0x1000, 0x1fff]
memory range [0x10000000, 0x101fffff]
prefetchable memory range [0x8000000000, 0x80001fffff]
BAR0: 32 bit memory at 0x10444000 [0x10444fff].
id "pcie.1"
4.2.2 info qtree
显示虚拟机的所有设备,例如
info qtree
dev: kvm-arm-gicv3, id ""
gpio-out "sysbus-irq" 32
gpio-in "" 512
num-cpu = 8 (0x8)
num-irq = 288 (0x120)
revision = 3 (0x3)
has-lpi = false
has-security-extensions = false
force-8-bit-prio = false
len-redist-region-count = 1 (0x1)
mmio 0000000008000000/0000000000010000
mmio 00000000080a0000/0000000000100000
4.2.3 info usb
显示虚拟机的usb设备,例如
info usb
Device 0.1, Port 1, Speed 480 Mb/s, Product QEMU USB Tablet, ID: tablet
Device 0.2, Port 2, Speed 480 Mb/s, Product QEMU USB Keyboard, ID: keyboard
4.3 qemu中串口设备类型介绍
一般虚拟机要添加串口设备,和chardev、serial、device3个配置项相关
下面列举几个典型的例子:
-
虚拟机中的串口设备重定向到物理机的
/dev/ttyS1设备-serial /dev/ttyS1 -
虚拟机中的串口设备重定向到物理机的伪终端(
qemu自动创建新的伪终端)-chardev pty,id=charserial0 -serial chardev:charserial0 -
虚拟机中的串口设备(类型为
usb-serial)重定向到物理机的伪终端(qemu自动创建新的伪终端)-chardev pty,id=charserial0 -device usb-serial,chardev=charserial0,always-plugged=true
4.3.1 chardev
字符设备完整的详细参数可参照官方文档:www.qemu.org/docs/master…
参数格式:-chardev backend,id=id[,mux=on|off][,options]
可用的backend类型:
Backend is one of:
null,socket,udp,msmouse,vc,ringbuf,file,pipe,console,serial,pty,stdio,braille,parallel,spicevmc,spiceport. The specific backend will determine the applicable options.
这里挑几个常用的,贴一下详细的参数说明
-
socket
PVE默认创建的就是该类型(xterm.js的串口控制台就是使用该socket实现)-chardev 'socket,id=serial0,path=/var/run/qemu-server/103.serial0,server=on,wait=off'-chardev socket,id=id[,TCP options or unix options][,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect=seconds][,tls-creds=id][,tls-authz=id]Create a two-way stream socket, which can be either a TCP or a unix socket. A unix socket will be created if
pathis specified. Behaviour is undefined if TCP options are specified for a unix socket.server=on|offspecifies that the socket shall be a listening socket.wait=on|offspecifies that QEMU should not block waiting for a client to connect to a listening socket.telnet=on|offspecifies that traffic on the socket should interpret telnet escape sequences.websocket=on|offspecifies that the socket uses WebSocket protocol for communication.reconnectsets the timeout for reconnecting on non-server sockets when the remote end goes away. qemu will delay this many seconds and then attempt to reconnect. Zero disables reconnecting, and is the default.tls-credsrequests enablement of the TLS protocol for encryption, and specifies the id of the TLS credentials to use for the handshake. The credentials must be previously created with the-object tls-credsargument.tls-authprovides the ID of the QAuthZ authorization object against which the client’s x509 distinguished name will be validated. This object is only resolved at time of use, so can be deleted and recreated on the fly while the chardev server is active. If missing, it will default to denying access.TCP and unix socket options are given below:
-
TCP options: port=port[,host=host][,to=to][,ipv4=on|off][,ipv6=on|off][,nodelay=on|off]hostfor a listening socket specifies the local address to be bound. For a connecting socket species the remote host to connect to.hostis optional for listening sockets. If not specified it defaults to0.0.0.0.portfor a listening socket specifies the local port to be bound. For a connecting socket specifies the port on the remote host to connect to.portcan be given as either a port number or a service name.portis required.tois only relevant to listening sockets. If it is specified, andportcannot be bound, QEMU will attempt to bind to subsequent ports up to and includingtountil it succeeds.tomust be specified as a port number.ipv4=on|offandipv6=on|offspecify that either IPv4 or IPv6 must be used. If neither is specified the socket may use either protocol.nodelay=on|offdisables the Nagle algorithm. -
unix options: path=path[,abstract=on|off][,tight=on|off]pathspecifies the local path of the unix socket.pathis required.abstract=on|offspecifies the use of the abstract socket namespace, rather than the filesystem. Optional, defaults to false.tight=on|offsets the socket length of abstract sockets to their minimum, rather than the full sun_path length. Optional, defaults to true.
-
-
serial
将字符设备映射到物理机串口设备中
-chardev serial,id=serial0,path=/dev/ttyS0-chardev serial,id=id,path=pathSend traffic from the guest to a serial device on the host.
On Unix hosts serial will actually accept any tty device, not only serial lines.
pathspecifies the name of the serial device to open. -
pty
将字符设备映射到物理机的伪终端(
qemu自动创建新的伪终端)-chardev pty,id=serial0-chardev pty,id=idCreate a new pseudo-terminal on the host and connect to it.
ptydoes not take any options.ptyis not available on Windows hosts.
4.3.2 serial
serial是一种简写的方式,后端实现其实也是创建了chardev设备的
参数格式:-serial dev
这里挑几个常用的,贴一下详细的参数说明
-
pty
将串口映射到物理机的伪终端(
qemu自动创建新的伪终端)-serial pty[Linux only] Pseudo TTY (a new PTY is automatically allocated)
等同于
-chardev pty,id=serial0 -serial chardev:serial0 -
chardev:id
使用
-chardev默认的设备创建串口(存在不同架构下的差异表现)aarch64:默认设备模型为pl011x86:默认设备类型为isa-serial,设备模型为16550AUse a named character device defined with the
-chardevoption. -
/dev/XXX
将字符设备映射到物理机串口设备中
-serial /dev/ttyS0[Linux only] Use host tty, e.g.
/dev/ttyS0. The host serial port parameters are set according to the emulated ones.等同于
-chardev serial,id=serial0,path=/dev/ttyS0 -serial chardev:serial0
4.3.3 device
4.3.3.1 isa-serial
仅x86架构可用,需要有isa总线的支持,设备模型为16550A
在x86架构中默认创建的串口类型
在虚拟机中一般默认为ttyS*设备
dmesg | grep tty
[ 0.052049] printk: console [tty0] enabled
[ 0.395284] 00:03: ttyS3 at I/O 0x2e8 (irq = 3, base_baud = 115200) is a 16550A
[ 0.418666] 00:04: ttyS2 at I/O 0x3e8 (irq = 4, base_baud = 115200) is a 16550A
[ 0.441999] 00:05: ttyS1 at I/O 0x2f8 (irq = 3, base_baud = 115200) is a 16550A
[ 0.465141] 00:06: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A
创建示例:
-chardev pty,id=charserial0
-device isa-serial,id=isaserial0,chardev=charserial0
注意:单个虚拟机最大支持的isa-serial数量为4
qemu monitor中用info qtree确认设备树中最多只有4个isa-serial设备
dev: isa-serial, id ""
index = 3 (0x3)
iobase = 744 (0x2e8)
irq = 3 (0x3)
dev: isa-serial, id ""
index = 2 (0x2)
iobase = 1000 (0x3e8)
irq = 4 (0x4)
dev: isa-serial, id ""
index = 1 (0x1)
iobase = 760 (0x2f8)
irq = 3 (0x3)
dev: isa-serial, id ""
index = 0 (0x0)
iobase = 1016 (0x3f8)
irq = 4 (0x4)
4.3.3.2 usb-serial
设备模型为FTDI FT232BM,系统需要加载相关驱动(usbserial、ftdi_sio)
支持的最大数量取决于USB控制器类型(PVE中MAX_USB_DEVICES写死为14)
在虚拟机中一般默认为ttyUSB*设备
dmesg | grep tty
[ 0.123973] ARMH0011:00: ttyAMA0 at MMIO 0x9000000 (irq = 4, base_baud = 0) is a SBSA
[ 0.258444] console [ttyAMA0] enabled
[ 3.552636] usb 1-4: FTDI USB Serial Device converter now attached to ttyUSB0
[ 3.555623] usb 1-5: FTDI USB Serial Device converter now attached to ttyUSB1
[ 3.558408] usb 1-6: FTDI USB Serial Device converter now attached to ttyUSB2
[ 3.561142] usb 1-7: FTDI USB Serial Device converter now attached to ttyUSB3
lsusb | grep Serial
Bus 001 Device 009: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 008: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 007: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 006: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 005: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 004: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
实际测试需要添加always-plugged=true才能正常识别
always-plugged参数的提交:lists.nongnu.org/archive/htm…
简单来说就是某些系统拔插USB设备后设备ID会变化,需要将变化通知到系统。这个参数则保证任何情况下始终保证USB的连接状态,不受后端chardev的影响
Some operating systems will generate a new device ID when a USB device is
unplugged
and then replugged into the USB. If this is done whilst switching between
multiple
applications over a virtual serial port, the change of device ID requires going
back into the OS/application to locate the new device accordingly.
Add a new always-plugged property that if specified will ensure that the device
always remains attached to the USB regardless of the state of the backend
chardev.
创建示例:
在USB控制器使用qemu-xhci时,可不指定bus
-chardev pty,id=charserial0
-device usb-serial,id=usbserial0,chardev=charserial0,always-plugged=true,bus=xhci.0
4.3.3.3 pci-serial
设备模型为对16550 UART的pci包装,详见:www.qemu.org/docs/master…
在虚拟机中一般默认为ttyS*设备
dmesg | grep tty
[ 5.440488] device: 'ttyS0': device_unregister
[ 5.440622] PM: Removing info for No Bus:ttyS0
[ 5.467484] 0000:00:02.0: ttyS0 at I/O 0x3120 (irq = 39, base_baud = 115200) is a 16550A
[ 5.467576] device: 'ttyS0': device_add
[ 5.467625] PM: Adding info for No Bus:ttyS0
[ 5.478735] device: 'ttyS1': device_unregister
[ 5.478856] PM: Removing info for No Bus:ttyS1
[ 5.504207] 0000:00:03.0: ttyS1 at I/O 0x3128 (irq = 40, base_baud = 115200) is a 16550A
[ 5.504304] device: 'ttyS1': device_add
[ 5.504356] PM: Adding info for No Bus:ttyS1
[ 5.506137] device: 'ttyS2': device_unregister
[ 5.506233] PM: Removing info for No Bus:ttyS2
[ 5.532378] 0000:00:04.0: ttyS2 at I/O 0x3130 (irq = 37, base_baud = 115200) is a 16550A
[ 5.532486] device: 'ttyS2': device_add
[ 5.532517] PM: Adding info for No Bus:ttyS2
lspci | grep Serial
00:02.0 Serial controller: Red Hat, Inc. QEMU PCI 16550A Adapter (rev 01)
00:03.0 Serial controller: Red Hat, Inc. QEMU PCI 16550A Adapter (rev 01)
00:04.0 Serial controller: Red Hat, Inc. QEMU PCI 16550A Adapter (rev 01)
创建示例:
-chardev pty,id=charserial0
-device pci-serial,id=pciserial0,chardev=charserial0,bus=pcie.0
4.3.3.4 virtio-serial
virtio-serial不同于其他的serial设备,基于virtio设备框架,除了支持标准的串行端口操作,还可以用于传输命令、日志、文件等,在qemu-guest-agent、spice中都有成熟的应用
virtio-serial原生支持多端口(单个设备最大支持31个端口),支持端口热拔插
在虚拟机中一般默认为vport*设备(/dev/virtio-ports/{portname})
dmesg | grep vport
[ 0.903643] device: 'vport0p1': device_add
[ 0.903666] PM: Adding info for No Bus:vport0p1
[ 0.903851] device: 'vport0p2': device_add
[ 0.903889] PM: Adding info for No Bus:vport0p2
[ 0.904031] device: 'vport0p3': device_add
[ 0.904045] PM: Adding info for No Bus:vport0p3
创建示例:
-device virtio-serial,id=virtioserial,bus=pcie.0,addr=0x2
-chardev pty,id=charserial0
-device virtserialport,chardev=charserial0,name=virtserialport.0,bus=virtioserial.0
4.3.3.5 UART(aarch64)
aarch64架构中默认创建的类型,设备模型为pl011(x86架构中无此类型)
在虚拟机中一般默认为ttyAMA*设备
dmesg | grep tty
[ 0.135970] ARMH0011:00: ttyAMA0 at MMIO 0x9000000 (irq = 4, base_baud = 0) is a SBSA
[ 0.272514] console [ttyAMA0] enabled
[ 175.413222] audit: type=1006 audit(1726283302.633:306): pid=3980 uid=0 old-auid=4294967295 auid=0 tty=(none) old-ses=4294967295 ses=3 res=1
创建示例:
-chardev pty,id=charserial0
-serial chardev:charserial0
注意:aarch64架构中单个虚拟机最大支持的数量为1
qemu monitor中用info qtree确认设备树中最多只有1个pl011设备
dev: pl011, id ""
gpio-out "sysbus-irq" 6
clock-in "clk" freq_hz=0 Hz
chardev = "charserial0"
migrate-clk = true
mmio ffffffffffffffff/0000000000001000
4.4 aarch64架构下串口设备选型及验证
4.4.1 usb-serial
4.4.1.1 修改方案
暂不处理$MAX_SERIAL_PORTS的最大4个串口限制
# serial devices
for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
my $path = $conf->{"serial$i"} or next;
if ($path eq 'socket') {
my $socket = "/var/run/qemu-server/${vmid}.serial$i";
push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
# On aarch64, serial0 is the UART device. QEMU only allows
# connecting UART devices via the '-serial' command line, as
# the device has a fixed slot on the hardware...
if ($arch eq 'aarch64') {
if ($i == 0) {
push @$devices, '-serial', "chardev:serial$i";
} else {
push @$devices, '-device', "usb-serial,id=usbserial$i,chardev=serial$i,always-plugged=true";
}
} else {
push @$devices, '-device', "isa-serial,chardev=serial$i";
}
} else {
die "no such serial device\n" if ! -c $path;
push @$devices, '-chardev', "serial,id=serial$i,path=$path";
push @$devices, '-device', "isa-serial,chardev=serial$i";
}
}
4.4.1.2 测试验证
为了方便测试,验证时chardev使用pty
4.4.1.2.1 PVE默认最大数量测试(1个UART+3个usb-serial)
测试参数:
-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-chardev pty,id=serial1
-device usb-serial,id=usbserial1,chardev=serial1,always-plugged=true
-chardev pty,id=serial2
-device usb-serial,id=usbserial2,chardev=serial2,always-plugged=true
-chardev pty,id=serial3
-device usb-serial,id=usbserial3,chardev=serial3,always-plugged=true
测试方法:
虚拟机串口发送测试(minicom):
虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符
虚拟机串口接收测试(minicom):
物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符
虚拟机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/ttyUSB0
物理机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/pts/1
期望结果:
虚拟机串口设备能正常接收或发送字符
测试结果:
| 系统 | 串口id | 虚拟机串口发送测试 | 虚拟机串口接收测试 |
|---|---|---|---|
| Kylin-V10-Desktop(2207) | usbserial1 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | usbserial2 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | usbserial3 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial1 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial2 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial3 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial1 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial2 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial3 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial1 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial2 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial3 | 通过 | 通过 |
4.4.1.2.2 数量测试(1个UART+9个usb-serial)
使用命令行参数启动虚拟机,手动修改命令行参数以跳出PVE的最大限制
测试参数:
-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-chardev pty,id=serial1
-device usb-serial,id=usbserial1,chardev=serial1,always-plugged=true
-chardev pty,id=serial2
-device usb-serial,id=usbserial2,chardev=serial2,always-plugged=true
-chardev pty,id=serial3
-device usb-serial,id=usbserial3,chardev=serial3,always-plugged=true
-chardev pty,id=serial4
-device usb-serial,id=usbserial4,chardev=serial4,always-plugged=true
-chardev pty,id=serial5
-device usb-serial,id=usbserial5,chardev=serial5,always-plugged=true
-chardev pty,id=serial6
-device usb-serial,id=usbserial6,chardev=serial6,always-plugged=true
-chardev pty,id=serial7
-device usb-serial,id=usbserial7,chardev=serial7,always-plugged=true
-chardev pty,id=serial8
-device usb-serial,id=usbserial8,chardev=serial8,always-plugged=true
-chardev pty,id=serial9
-device usb-serial,id=usbserial9,chardev=serial9,always-plugged=true
测试方法:
虚拟机串口发送测试(minicom):
虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符
虚拟机串口接收测试(minicom):
物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符
虚拟机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/ttyUSB0
物理机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/pts/1
期望结果:
虚拟机串口设备能正常接收或发送字符
测试结果:
| 系统 | 串口id | 虚拟机串口发送测试 | 虚拟机串口接收测试 |
|---|---|---|---|
| Kylin-V10-Desktop(2207) | usbserial1 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | usbserial2 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | usbserial3 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | usbserial4 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | usbserial5 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | usbserial6 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | usbserial7 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | usbserial8 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | usbserial9 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial1 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial2 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial3 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial4 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial5 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial6 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial7 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial8 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial9 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial1 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial2 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial3 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial4 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial5 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial6 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial7 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial8 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial9 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial1 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial2 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial3 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial4 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial5 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial6 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial7 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial8 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial9 | 通过 | 通过 |
4.4.1.2.3 热拔插测试
需要使用qemu monitor对USB设备进行热拔插
chardev类型无法进行热拔插,仅能对usb-serial热拔插,故如果需要实现串口灵活热拔插,需要提前添加足额的chardev
测试参数:
-monitor stdio
-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-chardev pty,id=serial1
-device usb-serial,id=usbserial1,chardev=serial1,always-plugged=true
-chardev pty,id=serial2
-device usb-serial,id=usbserial2,chardev=serial2,always-plugged=true
-chardev pty,id=serial3
-device usb-serial,id=usbserial3,chardev=serial3,always-plugged=true
-chardev pty,id=serial4
测试方法:
热拔插USB设备(qemu monitor):
# 拔出设备
device_del usbserial1
# 插入设备(id1)
device_add usb-serial,id=usbserial1,chardev=serial1,always-plugged=true
# 插入设备(id4)
device_add usb-serial,id=usbserial4,chardev=serial4,always-plugged=true
虚拟机串口发送测试(minicom):
虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符
虚拟机串口接收测试(minicom):
物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符
虚拟机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/ttyUSB0
物理机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/pts/1
期望结果:
热拔插USB设备后,虚拟机串口设备能正常接收或发送字符
测试结果:
| 系统 | 拔出串口id | 插入串口id | 虚拟机串口发送测试 | 虚拟机串口接收测试 |
|---|---|---|---|---|
| Kylin-V10-Desktop(2207) | usbserial1 | usbserial1 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | / | usbserial4 | 通过 | 通过 |
| Kylin-V10-Server(2204) | usbserial1 | usbserial1 | 通过 | 通过 |
| Kylin-V10-Server(2204) | / | usbserial4 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | usbserial1 | usbserial1 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | / | usbserial4 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | usbserial1 | usbserial1 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | / | usbserial4 | 通过 | 通过 |
4.4.2 pci-serial
4.4.2.1 修改方案
暂不处理$MAX_SERIAL_PORTS的最大4个串口限制
# serial devices
for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
my $path = $conf->{"serial$i"} or next;
if ($path eq 'socket') {
my $socket = "/var/run/qemu-server/${vmid}.serial$i";
push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
# On aarch64, serial0 is the UART device. QEMU only allows
# connecting UART devices via the '-serial' command line, as
# the device has a fixed slot on the hardware...
if ($arch eq 'aarch64') {
if ($i == 0) {
push @$devices, '-serial', "chardev:serial$i";
} else {
push @$devices, '-device', "pci-serial,id=pciserial$i,chardev=serial$i";
}
} else {
push @$devices, '-device', "isa-serial,chardev=serial$i";
}
} else {
die "no such serial device\n" if ! -c $path;
push @$devices, '-chardev', "serial,id=serial$i,path=$path";
push @$devices, '-device', "isa-serial,chardev=serial$i";
}
}
4.4.2.2 测试验证
为了方便测试,验证时chardev使用pty
4.4.2.2.1 PVE默认最大数量测试(1个UART+3个pci-serial)
测试参数:
-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-chardev pty,id=serial1
-device pci-serial,id=pciserial1,chardev=serial1,bus=pcie.0,addr=0x2
-chardev pty,id=serial2
-device pci-serial,id=pciserial2,chardev=serial2,bus=pcie.0,addr=0x3
-chardev pty,id=serial3
-device pci-serial,id=pciserial3,chardev=serial3,bus=pcie.0,addr=0x4
测试方法:
虚拟机串口发送测试(minicom):
虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符
虚拟机串口接收测试(minicom):
物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符
虚拟机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/ttyS0
物理机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/pts/1
期望结果:
虚拟机串口设备能正常接收或发送字符
测试结果:
| 系统 | 串口id | 虚拟机串口发送测试 | 虚拟机串口接收测试 |
|---|---|---|---|
| Kylin-V10-Desktop(2207) | pciserial1 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | pciserial2 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | pciserial3 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial1 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial2 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial3 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial1 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial2 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial3 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial1 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial2 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial3 | 通过 | 通过 |
4.4.2.2.2 数量测试(1个UART+9个pci-serial)
使用命令行参数启动虚拟机,手动修改命令行参数以跳出PVE的最大限制
测试参数:
-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-chardev pty,id=serial1
-device pci-serial,id=pciserial1,chardev=serial1,bus=pcie.0,addr=0x2
-chardev pty,id=serial2
-device pci-serial,id=pciserial2,chardev=serial2,bus=pcie.0,addr=0x3
-chardev pty,id=serial3
-device pci-serial,id=pciserial3,chardev=serial3,bus=pcie.0,addr=0x4
-chardev pty,id=serial4
-device pci-serial,id=pciserial4,chardev=serial4,bus=pcie.0,addr=0x5
-chardev pty,id=serial5
-device pci-serial,id=pciserial5,chardev=serial5,bus=pcie.0,addr=0x6
-chardev pty,id=serial6
-device pci-serial,id=pciserial6,chardev=serial6,bus=pcie.0,addr=0x7
-chardev pty,id=serial7
-device pci-serial,id=pciserial7,chardev=serial7,bus=pcie.0,addr=0x8
-chardev pty,id=serial8
-device pci-serial,id=pciserial8,chardev=serial8,bus=pcie.0,addr=0x9
-chardev pty,id=serial9
-device pci-serial,id=pciserial9,chardev=serial9,bus=pcie.0,addr=0x10
测试方法:
虚拟机串口发送测试(minicom):
虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符
虚拟机串口接收测试(minicom):
物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符
虚拟机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/ttyS0
物理机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/pts/1
期望结果:
虚拟机串口设备能正常接收或发送字符
测试结果:
| 系统 | 串口id | 虚拟机串口发送测试 | 虚拟机串口接收测试 |
|---|---|---|---|
| Kylin-V10-Desktop(2207) | pciserial1 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | pciserial2 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | pciserial3 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | pciserial4 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | pciserial5 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | pciserial6 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | pciserial7 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | pciserial8 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | pciserial9 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial1 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial2 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial3 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial4 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial5 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial6 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial7 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial8 | 通过 | 通过 |
| Kylin-V10-Server(2204) | pciserial9 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial1 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial2 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial3 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial4 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial5 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial6 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial7 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial8 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | pciserial9 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial1 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial2 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial3 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial4 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial5 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial6 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial7 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial8 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | pciserial9 | 通过 | 通过 |
4.4.2.2.3 热拔插测试
需要使用qemu monitor对PCI设备进行热拔插
chardev类型无法进行热拔插,仅能对pci-serial热拔插,故如果需要实现串口灵活热拔插,需要提前添加足额的chardev
PCI设备热拔插需要具备以下条件:
-
guest系统支持,打开内核参数CONFIG_HOTPLUG_PCI_PCIEzcat /proc/config.gz | grep HOTPLUG CONFIG_HOTPLUG_PCI_PCIE=y CONFIG_HOTPLUG_PCI=y CONFIG_HOTPLUG_PCI_ACPI=y CONFIG_HOTPLUG_PCI_ACPI_IBM=m CONFIG_HOTPLUG_PCI_CPCI=y CONFIG_HOTPLUG_PCI_SHPC=y # 或者在/boot/config-{uname -r} cat /boot/config-5.15.0-122-generic | grep HOTPLUG CONFIG_HOTPLUG_PCI_PCIE=y CONFIG_HOTPLUG_PCI=y CONFIG_HOTPLUG_PCI_ACPI=y CONFIG_HOTPLUG_PCI_ACPI_IBM=m CONFIG_HOTPLUG_PCI_CPCI=y CONFIG_HOTPLUG_PCI_CPCI_ZT5550=m CONFIG_HOTPLUG_PCI_CPCI_GENERIC=m CONFIG_HOTPLUG_PCI_SHPC=y -
guest系统支持,有pciehp、acpiphp、pci_hotplug驱动并加载lsmod | grep pciehp lsmod | grep acpiphp lsmod | grep pci_hotplug # 有些系统这些模块编译到内核里面了,modinfo才能看到 modinfo pciehp name: pciehp filename: (builtin) parm: pciehp_poll_mode:Using polling mechanism for hot-plug events or not (bool) parm: pciehp_poll_time:Polling mechanism frequency, in seconds (int) modinfo acpiphp name: acpiphp filename: (builtin) license: GPL file: drivers/pci/hotplug/acpiphp description: ACPI Hot Plug PCI Controller Driver author: Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>, Matthew Wilcox <willy@infradead.org> parm: disable:disable acpiphp driver (bool) modinfo pci_hotplug name: pci_hotplug filename: (builtin) parm: debug_acpi:Debugging mode for ACPI enabled or not (bool) parm: debug:Debugging mode enabled or not (bool) -
PCI设备不能挂载在部分总线设备如:Root Bus:pcie.0、pxb-pcie一般情况
aarch64架构下是挂载到pcie-root-port下由于
pcie-root-port本身不支持热拔插,所以需要准备多个pcie-root-port才能支持多设备热拔插详情参考这份文档:github.com/qemu/qemu/b…
-
虚拟机类型需要为
virt(aarch64)或者q35(x86)
由于银河麒麟aarch64架构的系统并未支持相关驱动模块,PCIE热拔插功能不支持
4.4.3 virtio-serial
4.4.3.1 修改方案
暂不处理$MAX_SERIAL_PORTS的最大4个串口限制
# serial devices
for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
my $path = $conf->{"serial$i"} or next;
if ($path eq 'socket') {
my $socket = "/var/run/qemu-server/${vmid}.serial$i";
push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
# On aarch64, serial0 is the UART device. QEMU only allows
# connecting UART devices via the '-serial' command line, as
# the device has a fixed slot on the hardware...
if ($arch eq 'aarch64') {
if ($i == 0) {
push @$devices, '-serial', "chardev:serial$i";
push @$devices, '-device', "virtio-serial,id=virtioserial";
} else {
push @$devices, '-device', "virtserialport,bus=virtioserial.0,chardev=serial$i,id=virtserialport.$i,name=virtserialport.$i";
}
} else {
push @$devices, '-device', "isa-serial,chardev=serial$i";
}
} else {
die "no such serial device\n" if ! -c $path;
push @$devices, '-chardev', "serial,id=serial$i,path=$path";
push @$devices, '-device', "isa-serial,chardev=serial$i";
}
}
4.4.3.2 测试验证
为了方便测试,验证时chardev使用pty
4.4.3.2.1 PVE默认最大数量测试(1个UART+3个virtserialport)
测试参数:
-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-device virtio-serial,id=virtioserial,bus=pcie.0,addr=0x2
-chardev pty,id=serial1
-device virtserialport,bus=virtioserial.0,chardev=serial1,id=virtserialport.1,name=virtserialport.1
-chardev pty,id=serial2
-device virtserialport,bus=virtioserial.0,chardev=serial2,id=virtserialport.2,name=virtserialport.2
-chardev pty,id=serial3
-device virtserialport,bus=virtioserial.0,chardev=serial3,id=virtserialport.3,name=virtserialport.3
测试方法:
虚拟机串口发送测试(minicom):
虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符
虚拟机串口接收测试(minicom):
物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符
虚拟机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/vport0p10
物理机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/pts/1
期望结果:
虚拟机串口设备能正常接收或发送字符
测试结果:
| 系统 | 串口id | 虚拟机串口发送测试 | 虚拟机串口接收测试 |
|---|---|---|---|
| Kylin-V10-Desktop(2207) | virtserialport.1 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | virtserialport.2 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | virtserialport.3 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.1 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.2 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.3 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.1 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.2 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.3 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.1 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.2 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.3 | 通过 | 通过 |
4.4.3.2.2 数量测试(1个UART+9个virtserialport)
使用命令行参数启动虚拟机,手动修改命令行参数以跳出PVE的最大限制
测试参数:
-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-device virtio-serial,id=virtioserial,bus=pcie.0,addr=0x2
-chardev pty,id=serial1
-device virtserialport,bus=virtioserial.0,chardev=serial1,id=virtserialport.1,name=virtserialport.1
-chardev pty,id=serial2
-device virtserialport,bus=virtioserial.0,chardev=serial2,id=virtserialport.2,name=virtserialport.2
-chardev pty,id=serial3
-device virtserialport,bus=virtioserial.0,chardev=serial3,id=virtserialport.3,name=virtserialport.3
-chardev pty,id=serial4
-device virtserialport,bus=virtioserial.0,chardev=serial4,id=virtserialport.4,name=virtserialport.4
-chardev pty,id=serial5
-device virtserialport,bus=virtioserial.0,chardev=serial5,id=virtserialport.5,name=virtserialport.5
-chardev pty,id=serial6
-device virtserialport,bus=virtioserial.0,chardev=serial6,id=virtserialport.6,name=virtserialport.6
-chardev pty,id=serial7
-device virtserialport,bus=virtioserial.0,chardev=serial7,id=virtserialport.7,name=virtserialport.7
-chardev pty,id=serial8
-device virtserialport,bus=virtioserial.0,chardev=serial8,id=virtserialport.8,name=virtserialport.8
-chardev pty,id=serial9
-device virtserialport,bus=virtioserial.0,chardev=serial9,id=virtserialport.9,name=virtserialport.9
测试方法:
虚拟机串口发送测试(minicom):
虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符
虚拟机串口接收测试(minicom):
物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符
虚拟机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/vport0p10
物理机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/pts/1
期望结果:
虚拟机串口设备能正常接收或发送字符
测试结果:
| 系统 | 串口id | 虚拟机串口发送测试 | 虚拟机串口接收测试 |
|---|---|---|---|
| Kylin-V10-Desktop(2207) | virtserialport.1 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | virtserialport.2 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | virtserialport.3 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | virtserialport.4 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | virtserialport.5 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | virtserialport.6 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | virtserialport.7 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | virtserialport.8 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | virtserialport.9 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.1 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.2 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.3 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.4 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.5 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.6 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.7 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.8 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.9 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.1 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.2 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.3 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.4 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.5 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.6 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.7 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.8 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.9 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.1 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.2 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.3 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.4 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.5 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.6 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.7 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.8 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.9 | 通过 | 通过 |
遗留问题:
虚拟机启动时报错:kvm: virtio-serial-bus: Guest failure in adding port 8 for device virtioserial.0
virtserialport超过7就会报错,但是串口设备能正常创建,不影响使用
4.4.3.2.3 热拔插测试
需要使用qemu monitor对virtserialport设备进行热拔插
chardev类型无法进行热拔插,仅能对virtserialport热拔插,故如果需要实现串口灵活热拔插,需要提前添加足额的chardev
测试参数:
-monitor stdio
-chardev socket,id=serial0,path=/var/run/qemu-server/${vmid}.serial0,server=on,wait=off
-serial chardev:serial0
-device virtio-serial,id=virtioserial,bus=pcie.0,addr=0x2
-chardev pty,id=serial1
-device virtserialport,bus=virtioserial.0,chardev=serial1,id=virtserialport.1,name=virtserialport.1
-chardev pty,id=serial2
-device virtserialport,bus=virtioserial.0,chardev=serial2,id=virtserialport.2,name=virtserialport.2
-chardev pty,id=serial3
-device virtserialport,bus=virtioserial.0,chardev=serial3,id=virtserialport.3,name=virtserialport.3
-chardev pty,id=serial4
测试方法:
热拔插USB设备(qemu monitor):
# 拔出设备
device_del virtserialport.1
# 插入设备(id1)
device_add virtserialport,bus=virtioserial.0,chardev=serial1,id=virtserialport.1,name=virtserialport.1
# 插入设备(id4)
device_add virtserialport,bus=virtioserial.0,chardev=serial4,id=virtserialport.4,name=virtserialport.4
虚拟机串口发送测试(minicom):
虚拟机内串口设备发送字符,物理机对应pty端口可以接收到字符
虚拟机串口接收测试(minicom):
物理机对应pty端口发送字符,虚拟机内串口设备可以接收到字符
虚拟机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/vport0p10
物理机发送/查看字符命令:
# 打开字符设备
minicom -D /dev/pts/1
期望结果:
热拔插USB设备后,虚拟机串口设备能正常接收或发送字符
测试结果:
| 系统 | 拔出串口id | 插入串口id | 虚拟机串口发送测试 | 虚拟机串口接收测试 |
|---|---|---|---|---|
| Kylin-V10-Desktop(2207) | virtserialport.1 | virtserialport.1 | 通过 | 通过 |
| Kylin-V10-Desktop(2207) | / | virtserialport.4 | 通过 | 通过 |
| Kylin-V10-Server(2204) | virtserialport.1 | virtserialport.1 | 通过 | 通过 |
| Kylin-V10-Server(2204) | / | virtserialport.4 | 通过 | 通过 |
| Kylin-V4-Desktop(sp4-20200728) | virtserialport.1 | virtserialport.1 | 失败(拔出后再次插入失败) | 失败(拔出后再次插入失败) |
| Kylin-V4-Desktop(sp4-20200728) | / | virtserialport.4 | 通过 | 通过 |
| Kylin-V4-Server(sp4-20070420) | virtserialport.1 | virtserialport.1 | 失败(拔出后再次插入失败) | 失败(拔出后再次插入失败) |
| Kylin-V4-Server(sp4-20070420) | / | virtserialport.4 | 通过 | 通过 |
4.4.4 最终选型(usb-serial)
测试结果汇总:
| 串口类型 | 支持最大数量 | 热拔插 |
|---|---|---|
| usb-serial | 14(PVE默认写死最大值为14,但最大值取决于USB控制器类型,某些USB控制器支持数量可能少于14) | 支持 |
| pci-serial | /(取决于pcie的架构设计及控制器类型,pcie.0默认支持32路pcie设备) | 有限支持(需要guest支持、pcie控制器支持) |
| virtio-serial | 单个设备最大31个端口,需求更多时可以创建多个设备 | 有限支持(需要guest的对应驱动支持) |
个人主观的选择是usb-serial,仅代表个人观点
先说说为什么不选择其他的设备类型
virtio-serial
从设备角度来说已经脱离了串口设备这样的概念,实际是只有一个Communication controller的pci设备,串口只是该设备下的端口
从驱动角度来说,virtio驱动是在内核中的,在测试中暴露出一些老旧的系统中出现问题
pci-serial
PVE写死了pcie的设备map,增加pci-serial需要削减其他设备的数量
另外测试发现pci-serial挂载在pcie-root-port下时,需要从0地址挂载才能生效,其余地址均初始化失败(其他设备也有该问题)
最后选择了usb-serial,虽然PVE写死了最大USB设备数量,串口会抢占一定的数量,总体来说还算能接受