From fb3b203e98d3192a8ec724323e31a56c47b95183 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Wed, 5 Nov 2025 17:05:47 +0100 Subject: [PATCH 1/4] fdt/aarch64: fix serial irq line on macOS Apply the same hypervisor-specific correction we do for other devices. Signed-off-by: Sergio Lopez --- src/devices/src/fdt/aarch64.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/devices/src/fdt/aarch64.rs b/src/devices/src/fdt/aarch64.rs index 8ef879fb7..d2a754343 100644 --- a/src/devices/src/fdt/aarch64.rs +++ b/src/devices/src/fdt/aarch64.rs @@ -324,7 +324,14 @@ fn create_serial_node( dev_info: &T, ) -> Result<()> { let serial_reg_prop = generate_prop64(&[dev_info.addr(), dev_info.length()]); + #[cfg(target_os = "linux")] let irq = generate_prop32(&[GIC_FDT_IRQ_TYPE_SPI, dev_info.irq(), IRQ_TYPE_EDGE_RISING]); + #[cfg(target_os = "macos")] + let irq = generate_prop32(&[ + GIC_FDT_IRQ_TYPE_SPI, + dev_info.irq() - 32, + IRQ_TYPE_EDGE_RISING, + ]); let node = fdt.begin_node(&format!("uart@{:x}", dev_info.addr()))?; fdt.property_string("compatible", "arm,pl011")?; From 11e75223aae5632f716a07ba32a147d529e8cb31 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Wed, 5 Nov 2025 17:16:03 +0100 Subject: [PATCH 2/4] fdt/aarch64: add an stdout-path entry On aarch64, some guests use the chosen/stdout-path entry to find out the console they should use during boot. If we have a legacy serial console configure, let's create such entry to enable those guests to boot with a console. Linux guests will still switch to a virtio-console if it becomes available at some point during boot, so there isn't a risk we override that decision. Signed-off-by: Sergio Lopez --- src/devices/src/fdt/aarch64.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/devices/src/fdt/aarch64.rs b/src/devices/src/fdt/aarch64.rs index d2a754343..3fe9609a0 100644 --- a/src/devices/src/fdt/aarch64.rs +++ b/src/devices/src/fdt/aarch64.rs @@ -98,7 +98,7 @@ pub fn create_fdt( fdt.property_u32("interrupt-parent", GIC_PHANDLE)?; create_cpu_nodes(&mut fdt, &vcpu_mpidr)?; create_memory_node(&mut fdt, guest_mem, arch_memory_info)?; - create_chosen_node(&mut fdt, cmdline, initrd)?; + create_chosen_node(&mut fdt, cmdline, initrd, device_info)?; create_gic_node(&mut fdt, gic_device)?; create_timer_node(&mut fdt)?; create_clock_node(&mut fdt)?; @@ -189,14 +189,24 @@ fn create_memory_node( Ok(()) } -fn create_chosen_node( +fn create_chosen_node( fdt: &mut FdtWriter, cmdline: &str, initrd: &Option, + dev_info: &HashMap<(DeviceType, String), T>, ) -> Result<()> { let chosen_node = fdt.begin_node("chosen")?; fdt.property_string("bootargs", cmdline)?; + // If we have a legacy serial device, tell the guest this is the default console. + // Clever guests will still switch to a better console (like virtio-console) if + // it becomes available later, and this gives us a good fallback. + for ((device_type, _device_id), info) in dev_info { + if device_type == &DeviceType::Serial { + fdt.property_string("stdout-path", &format!("/uart@{:x}", info.addr()))?; + } + } + if let Some(initrd_config) = initrd { fdt.property_u64("linux,initrd-start", initrd_config.address.raw_value())?; fdt.property_u64( From b06bff5c50809c829e475a826ce2a22b318eaef4 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Wed, 5 Nov 2025 18:00:11 +0100 Subject: [PATCH 3/4] device_manager/aarch64: register intc on serial We were missing registering the IrqChip on legacy serial devices, which meant they couldn't trigger IRQs. This issue went under the radar because only a few guests rely on them (so far, only NetBSD). Let's fix it here. Signed-off-by: Sergio Lopez --- src/vmm/src/builder.rs | 4 +++- src/vmm/src/device_manager/kvm/mmio.rs | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index c9f5ec64d..fee24ed01 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -870,6 +870,7 @@ pub fn build_microvm( &vm, &mut mmio_device_manager, &mut kernel_cmdline, + intc.clone(), serial_devices, )?; } @@ -1623,11 +1624,12 @@ fn attach_legacy_devices( vm: &Vm, mmio_device_manager: &mut MMIODeviceManager, kernel_cmdline: &mut kernel::cmdline::Cmdline, + intc: IrqChip, serial: Vec>>, ) -> std::result::Result<(), StartMicrovmError> { for s in serial { mmio_device_manager - .register_mmio_serial(vm.fd(), kernel_cmdline, s) + .register_mmio_serial(vm.fd(), kernel_cmdline, intc.clone(), s) .map_err(Error::RegisterMMIODevice) .map_err(StartMicrovmError::Internal)?; } diff --git a/src/vmm/src/device_manager/kvm/mmio.rs b/src/vmm/src/device_manager/kvm/mmio.rs index 56d1ae05b..eef147c2f 100644 --- a/src/vmm/src/device_manager/kvm/mmio.rs +++ b/src/vmm/src/device_manager/kvm/mmio.rs @@ -11,6 +11,8 @@ use std::{fmt, io}; #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] use devices::fdt::DeviceInfoForFDT; +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +use devices::legacy::IrqChip; use devices::{BusDevice, DeviceType}; use kernel::cmdline as kernel_cmdline; use kvm_ioctls::{IoEventAddress, VmFd}; @@ -192,6 +194,7 @@ impl MMIODeviceManager { &mut self, vm: &VmFd, cmdline: &mut kernel_cmdline::Cmdline, + intc: IrqChip, serial: Arc>, ) -> Result<()> { if self.irq > self.last_irq { @@ -201,6 +204,8 @@ impl MMIODeviceManager { vm.register_irqfd(serial.lock().unwrap().interrupt_evt(), self.irq) .map_err(Error::RegisterIrqFd)?; + serial.lock().unwrap().set_intc(intc); + self.bus .insert(serial, self.mmio_base, MMIO_LEN) .map_err(Error::BusError)?; From 233d4ea43d518051723e77188869943d46271381 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Tue, 2 Dec 2025 11:00:20 +0100 Subject: [PATCH 4/4] builder: set raw mode for ttys on serial devices Check if an FD being used as input for a legacy serial device is actually a tty, and if so, set raw mode for it. Signed-off-by: Sergio Lopez --- src/vmm/src/builder.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index fee24ed01..eead3ab33 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -11,7 +11,7 @@ use kernel::cmdline::Cmdline; use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::fs::File; -use std::io::{self, Read}; +use std::io::{self, IsTerminal, Read}; use std::os::fd::AsRawFd; use std::os::fd::{BorrowedFd, FromRawFd}; use std::path::PathBuf; @@ -735,7 +735,16 @@ pub fn build_microvm( )?); }; + // We can't call to `setup_terminal_raw_mode` until `Vmm` is created, + // so let's keep track of FDs connected to legacy serial devices here + // and set raw mode on them later. + let mut serial_ttys = Vec::new(); + for s in &vm_resources.serial_consoles { + let input = unsafe { BorrowedFd::borrow_raw(s.input_fd) }; + if input.is_terminal() { + serial_ttys.push(input); + } let input: Option> = if s.input_fd >= 0 { Some(Box::new(unsafe { File::from_raw_fd(s.input_fd) })) } else { @@ -949,6 +958,11 @@ pub fn build_microvm( pio_device_manager, }; + // Set raw mode for FDs that are connected to legacy serial devices. + for serial_tty in serial_ttys { + setup_terminal_raw_mode(&mut vmm, Some(serial_tty), false); + } + #[cfg(not(feature = "tee"))] attach_balloon_device(&mut vmm, event_manager, intc.clone())?; #[cfg(not(feature = "tee"))]