Apple Silicon 上 Cirros 控制台VNC打不了字
前置:本地学习:用 DevStack 安装 OpenStack、创建第一台实例 目标:分清哪些是苹果芯片的问题、哪些是这张 aarch64 镜像的问题,并把 noVNC 修到能登录、能打字。
引言
在 Apple Silicon 的 DevStack 上开 Cirros,控制台经常不是「坏了」一种样子,而是连着变三次:先完全没画面,再能看到登录行但打不了字,换成另一块显卡后又停在三行 EFI 日志上。
这不全是苹果芯片的问题。没有 KVM、CPU 型号被认错,才是芯片这边的事。后面画面不刷新、键盘没反应,是 Cirros 这张 aarch64 镜像把对应驱动裁掉了。换一台 ARM 服务器、用同一张镜像,后半段一样会遇到。
本文按现象写修复,环境是 Multipass 里的 DevStack,镜像 cirros-0.6.3-aarch64-disk,虚机内核 5.15.0-117-generic。
先分清卡在哪一层
| 你看到的 | 虚机实际状态 | 原因 |
|---|---|---|
| 一直是 Display output is not active,串口日志 0 字节 | 固件没跑起来 | CPU 被选成 32 位 cortex-a15 |
GRUB:error: out of memory |
卡在引导器 | m1.nano 只有 192MB |
看得到 vm1 login:,打什么都没反应 |
系统已起来,画面冻住 | virtio-gpu 不再刷新 |
停在 EFI stub: Exiting boot services... |
系统已起来,显存没人接 | ramfb 缺少 simplefb,USB 键盘缺少 xhci |
第三、第四种不要在 noVNC 里反复点。先用串口看它是不是已经在登录提示上:
bash
multipass exec devstack -- sudo virsh console <instance名>
退出按 Ctrl+]。Cirros 默认用户 cirros,密码 gocubsgo(输入时不显示)。实例名用下面这条看,不要猜编号:
bash
openstack server show vm1 -c OS-EXT-SRV-ATTR:instance_name -f value

上图是第四种。固件把这三行画出来就退出了,后面没有驱动继续写这块显存,所以光标停住。同一时刻串口上已经是 vm1 login:。
1. 完全没画面:指定 64 位 CPU
Apple Silicon 上没有 KVM,virt_type 必须是 qemu。安装篇里如果只写了 cpu_mode = none,libvirt 认不出这颗主机 CPU,会退到 32 位的 cortex-a15,而 AAVMF 固件是 64 位的。虚机起不来,VNC 一直空。
改 /etc/nova/nova-cpu.conf 的 [libvirt]:
ini
cpu_mode = custom
cpu_models = cortex-a72
virt_type = qemu
bash
sudo systemctl restart devstack@n-cpu.service
已经创建的虚机不会跟着变,CPU 型号写在创建时的 domain 里。删掉重建。
2. GRUB 内存不够
aarch64 上这份 Cirros 的 GRUB 2.06,用 m1.nano(192MB)会报 error: out of memory。规格用 m1.tiny:1 vCPU、512MB 内存、1GB 磁盘。
3. 看得到登录行,但不是焦点问题
Nova 在 AArch64 上默认显卡是 virtio。x86 上这块显卡还能退回 VGA 文本模式,aarch64 的 virt 机器没有这条退路。
这张镜像带了 virtio-gpu.ko,所以能画出第一帧,vm1 login: 会出现在 noVNC 上。紧接着内核日志里有:
text
virtio_gpu virtio5: [drm] drm_plane_enable_fb_damage_clips() not called
这之后的画面更新不再发给 QEMU,noVNC 停在那一帧。点上方灰条、点黑色区域,都不会出现字符。
也不要改成 bochs。Nova 允许这个名字,但这张镜像里没有 bochs.ko,控制台会直接黑。
4. 显卡改成 ramfb
ramfb 就是一块普通显存:客人往里写,VNC 就能看到,不依赖 virtio-gpu 那次刷新。这台机器上的 QEMU 和 libvirt 10 都认识它。挡住的是 Nova:VideoModel 的允许列表里没有 ramfb,AArch64 默认仍返回 virtio。
编辑 /opt/stack/nova/nova/objects/fields.py:
python
class VideoModel(BaseNovaEnum):
CIRRUS = "cirrus"
QXL = "qxl"
VGA = "vga"
VMVGA = "vmvga"
XEN = "xen"
VIRTIO = 'virtio'
GOP = 'gop'
NONE = 'none'
BOCHS = 'bochs'
RAMFB = 'ramfb'
ALL = (CIRRUS, QXL, VGA, VMVGA, XEN, VIRTIO, GOP, NONE, BOCHS, RAMFB)
这是 DevStack 学习环境里的改动,生产集群不要手改计算节点源码。驱动齐全的云镜像(例如 Ubuntu)通常不必走这条路。
创建实例时,API、调度器、conductor、compute 各自用自己进程里 的允许列表做校验。只重启 nova-compute,创建会报:
text
Invalid image metadata. Error: Field value ramfb is invalid
这几个都要重启。conductor 会等手头任务结束,停服务要一两分钟是正常的。
bash
sudo systemctl restart \
devstack@n-api.service \
devstack@n-sch.service \
devstack@n-cond-cell1.service \
devstack@n-super-cond.service \
devstack@n-cpu.service
然后把属性写到镜像上,删掉旧实例再创建。正在跑的虚机不会热更新显卡;从卷启动的实例更是在创建时就把型号写进 domain 了,改 Glance 对它无效。
bash
source ~/devstack/openrc admin admin
openstack image set --property hw_video_model=ramfb cirros-0.6.3-aarch64-disk
openstack server delete vm1
openstack server create --flavor m1.tiny \
--image cirros-0.6.3-aarch64-disk \
--network shared --wait vm1
确认两处都是 ramfb:镜像属性,以及这台虚机的 domain XML。instance-00000005 只是这次的编号。

到这里,固件阶段的输出应该已经出现。如果仍停在下面这三行,继续第 5 节,不要再改显卡型号:
text
EFI stub: Booting Linux Kernel...
EFI stub: Using DTB from configuration table
EFI stub: Exiting boot services...
5. 停在 EFI stub:补帧缓冲和 USB 键盘
ramfb 只保证固件能画。进内核之后,这张镜像把对应驱动裁掉了,所以是两件独立的事。
画面。 客人里有平台设备 simple-framebuffer.0,但没有 simplefb.ko / simpledrm.ko。内核把 EFI 帧缓冲转成了 simple-framebuffer,没有模块就绑不上,/sys/class/graphics 里只有 fbcon、没有 fb0。VNC 永远停在固件最后一帧。
键盘。 libvirt 给的是 USB 3 控制器 qemu-xhci 和 USB 键盘。这个内核编进了 EHCI / OHCI / UHCI,没有把 xHCI 编进去,Cirros 又把 xhci-pci.ko 裁掉了。/proc/bus/input/devices 里只有电源按钮。noVNC 的按键和 virsh send-key 都到不了虚机。
先在串口里看内核版本,模块包必须和它一致:
bash
uname -r
# 5.15.0-117-generic
在 DevStack 那台 Ubuntu 上下这个 arm64 包并解开:
bash
curl -fsS -o /tmp/linux-modules.deb \
http://ports.ubuntu.com/pool/main/l/linux/linux-modules-5.15.0-117-generic_5.15.0-117.127_arm64.deb
mkdir -p /tmp/linux-modules
dpkg-deb -x /tmp/linux-modules.deb /tmp/linux-modules
要用的三个文件,都在 /tmp/linux-modules/lib/modules/5.15.0-117-generic/kernel/ 下面:
| 文件 | 作用 |
|---|---|
drivers/usb/host/xhci-pci-renesas.ko |
xhci-pci 的依赖,必须先加载 |
drivers/usb/host/xhci-pci.ko |
认出 qemu-xhci 和 USB 键盘 |
drivers/video/fbdev/simplefb.ko |
把 ramfb 的显存接成 fb0 |
拷到虚机里的同一相对路径(/lib/modules/5.15.0-117-generic/kernel/...)。图形控制台还不能用时,用 virsh console 登录再拷;虚机关机后,也可以用 libguestfs 的 virt-copy-in 写进那块磁盘。
Cirros 开机时会跑 /etc/init.d/rcS,它执行 /etc/init.d/S??*。放一个真实文件,不要只放指向别处的符号链接:
bash
sudo tee /etc/init.d/S15console-fix >/dev/null << 'EOF'
#!/bin/sh
insmod /lib/modules/5.15.0-117-generic/kernel/drivers/usb/host/xhci-pci-renesas.ko
insmod /lib/modules/5.15.0-117-generic/kernel/drivers/usb/host/xhci-pci.ko
insmod /lib/modules/5.15.0-117-generic/kernel/drivers/video/fbdev/simplefb.ko
EOF
sudo chmod 755 /etc/init.d/S15console-fix
sudo reboot
顺序不能反:先 renesas,再 xhci-pci,最后 simplefb。重启后打开控制台,先点一下黑色画面。用户名 cirros,密码 gocubsgo。

哪些算苹果芯片,哪些不算
| 现象 | 是不是 Apple Silicon 特有 |
|---|---|
没有 KVM,必须用 qemu;CPU 要指定 cortex-a72,否则会变成 cortex-a15 |
是。普通 x86,或带 KVM 的 ARM 服务器,不会走这条 |
| aarch64 的 virt 机器没有 VGA 兼容显卡 | 是这套架构的事,不是 M 系列独有 |
| virtio-gpu 画出一帧后不再刷新 | 这张 Cirros 内核的问题。同一镜像在别的 ARM 机器上一样 |
| ramfb 停在 EFI stub,USB 键盘不存在 | 镜像裁掉了 simplefb 和 xhci-pci,同样不是芯片特有 |
CPU 型号和「不能用 KVM」按苹果芯片处理。显卡不刷新、键盘没有驱动,按这张 aarch64 Cirros 镜像处理。