vmm: Add acpi table for fw_cfg device

This allows the fw_cfg device to be recognized by the guest linux
kernel. This becomes more relavnt in the following cl where I add
the option to load files into the guest via fw_cfg. The Linux kernel
already has a fw_cfg driver that will automatically load these files
under /sys when CONFIG_FW_CFG_SYSFS is enabled in the kernel config

For arm we must add fw_cfg to the devices tree

Signed-off-by: Alex Orozco <alexorozco@google.com>
This commit is contained in:
Alex Orozco 2025-05-19 21:38:21 +00:00 committed by Bo Chen
parent edee53ac1a
commit 971f552e09
6 changed files with 69 additions and 5 deletions

View file

@ -6,6 +6,7 @@ version = "0.1.0"
[features]
default = []
fw_cfg = []
kvm = ["hypervisor/kvm"]
sev_snp = []
tdx = []

View file

@ -850,6 +850,21 @@ fn create_gpio_node<T: DeviceInfoForFdt + Clone + Debug>(
Ok(())
}
// https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/fw-cfg.txt
#[cfg(feature = "fw_cfg")]
fn create_fw_cfg_node<T: DeviceInfoForFdt + Clone + Debug>(
fdt: &mut FdtWriter,
dev_info: &T,
) -> FdtWriterResult<()> {
// FwCfg node
let fw_cfg_node = fdt.begin_node(&format!("fw-cfg@{:x}", dev_info.addr()))?;
fdt.property("compatible", b"qemu,fw-cfg-mmio\0")?;
fdt.property_array_u64("reg", &[dev_info.addr(), dev_info.length()])?;
fdt.end_node(fw_cfg_node)?;
Ok(())
}
fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::BuildHasher>(
fdt: &mut FdtWriter,
dev_info: &HashMap<(DeviceType, String), T, S>,
@ -865,6 +880,8 @@ fn create_devices_node<T: DeviceInfoForFdt + Clone + Debug, S: ::std::hash::Buil
DeviceType::Virtio(_) => {
ordered_virtio_device.push(info);
}
#[cfg(feature = "fw_cfg")]
DeviceType::FwCfg => create_fw_cfg_node(fdt, info)?,
}
}

View file

@ -155,6 +155,9 @@ pub enum DeviceType {
/// Device Type: GPIO.
#[cfg(target_arch = "aarch64")]
Gpio,
/// Device Type: fw_cfg.
#[cfg(feature = "fw_cfg")]
FwCfg,
}
/// Default (smallest) memory page size for the supported architectures.

View file

@ -44,6 +44,6 @@ arch = { path = "../arch" }
[features]
default = []
fw_cfg = ["bitfield-struct", "linux-loader", "zerocopy"]
fw_cfg = ["arch/fw_cfg", "bitfield-struct", "linux-loader", "zerocopy"]
kvm = ["arch/kvm"]
pvmemcontrol = []

View file

@ -91,6 +91,8 @@ const FW_CFG_KNOWN_ITEMS: usize = 0x20;
pub const FW_CFG_FILE_FIRST: u16 = 0x20;
pub const FW_CFG_DMA_SIGNATURE: [u8; 8] = *b"QEMU CFG";
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/qemu_fw_cfg.h
pub const FW_CFG_ACPI_ID: &str = "QEMU0002";
// Reserved (must be enabled)
const FW_CFG_F_RESERVED: u8 = 1 << 0;
// DMA Toggle Bit (enabled by default)

View file

@ -53,6 +53,8 @@ use devices::gic;
use devices::interrupt_controller::InterruptController;
#[cfg(target_arch = "x86_64")]
use devices::ioapic;
#[cfg(all(feature = "fw_cfg", target_arch = "x86_64"))]
use devices::legacy::fw_cfg::FW_CFG_ACPI_ID;
#[cfg(target_arch = "aarch64")]
use devices::legacy::Pl011;
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
@ -1495,11 +1497,29 @@ impl DeviceManager {
// default address for fw_cfg on arm via mmio
// https://github.com/torvalds/linux/blob/master/drivers/firmware/qemu_fw_cfg.c#L27
#[cfg(target_arch = "aarch64")]
self.address_manager
.mmio_bus
.insert(fw_cfg.clone(), PORT_FW_CFG_BASE, PORT_FW_CFG_WIDTH)
.map_err(DeviceManagerError::ErrorAddingFwCfgToBus)?;
{
self.address_manager
.mmio_bus
.insert(fw_cfg.clone(), PORT_FW_CFG_BASE, PORT_FW_CFG_WIDTH)
.map_err(DeviceManagerError::ErrorAddingFwCfgToBus)?;
let fw_cfg_irq = self
.address_manager
.allocator
.lock()
.unwrap()
.allocate_irq()
.unwrap();
self.id_to_dev_info.insert(
(DeviceType::FwCfg, "fw-cfg".to_string()),
MmioDeviceInfo {
addr: PORT_FW_CFG_BASE,
len: PORT_FW_CFG_WIDTH,
irq: fw_cfg_irq,
},
);
}
Ok(())
}
@ -5003,6 +5023,27 @@ impl Aml for DeviceManager {
)
.to_aml_bytes(sink);
#[cfg(all(feature = "fw_cfg", target_arch = "x86_64"))]
if self.fw_cfg.is_some() {
aml::Device::new(
"_SB_.FWCF".into(),
vec![
&aml::Name::new("_HID".into(), &FW_CFG_ACPI_ID.to_string()),
&aml::Name::new("_STA".into(), &0xB_usize),
&aml::Name::new(
"_CRS".into(),
&aml::ResourceTemplate::new(vec![&aml::IO::new(
PORT_FW_CFG_BASE as u16,
PORT_FW_CFG_BASE as u16,
0x01,
PORT_FW_CFG_WIDTH as u8,
)]),
),
],
)
.to_aml_bytes(sink);
}
// Serial device
#[cfg(target_arch = "x86_64")]
let serial_irq = 4;