-
Notifications
You must be signed in to change notification settings - Fork 4
Rework fw_cfg and add bootorder entry
#170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: gardenlinux
Are you sure you want to change the base?
Changes from all commits
fa3cd55
8e253d9
f7ba097
03ab3c1
7a2a2c1
ed98bd1
9ac85eb
6c823a4
c4ba2b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1370,6 +1370,7 @@ impl PciDeviceCommonConfig { | |
| } | ||
|
|
||
| impl DiskConfig { | ||
| #[cfg(not(feature = "fw_cfg"))] | ||
| pub const SYNTAX: &'static str = "Disk parameters \ | ||
| \"path=<disk_image_path>,readonly=on|off,direct=on|off,iommu=on|off,\ | ||
| num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,\ | ||
|
|
@@ -1382,6 +1383,20 @@ impl DiskConfig { | |
| serial=<serial_number>,backing_files=on|off,sparse=on|off,\ | ||
| image_type=<raw,qcow2,vhd,vhdx>,lock_granularity=byte-range|full"; | ||
|
|
||
| #[cfg(feature = "fw_cfg")] | ||
| pub const SYNTAX: &'static str = "Disk parameters \ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer a concatenation to the existing |
||
| \"path=<disk_image_path>,readonly=on|off,direct=on|off,iommu=on|off,\ | ||
| num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,\ | ||
| vhost_user=on|off,socket=<vhost_user_socket_path>,\ | ||
| bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,\ | ||
| ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,\ | ||
| id=<device_id>,pci_segment=<segment_id>,pci_device_id=<pci_slot>,\ | ||
| rate_limit_group=<group_id>,\ | ||
| queue_affinity=<list_of_queue_indices_with_their_associated_cpuset>,\ | ||
| serial=<serial_number>,backing_files=on|off,sparse=on|off,\ | ||
| image_type=<raw,qcow2,vhd,vhdx>,lock_granularity=byte-range|full,\ | ||
| bootindex=<index>"; | ||
|
|
||
| pub fn parse(disk: &str) -> Result<Self> { | ||
| let mut parser = OptionParser::new(); | ||
| parser | ||
|
|
@@ -1409,6 +1424,9 @@ impl DiskConfig { | |
| .add("lock_granularity") | ||
| .add_all(PciDeviceCommonConfig::OPTIONS_IOMMU); | ||
|
|
||
| #[cfg(feature = "fw_cfg")] | ||
| parser.add("bootindex"); | ||
|
|
||
| parser.parse(disk).map_err(Error::ParseDisk)?; | ||
|
|
||
| let path = parser.get("path").map(PathBuf::from); | ||
|
|
@@ -1538,6 +1556,11 @@ impl DiskConfig { | |
|
|
||
| let pci_common = PciDeviceCommonConfig::parse(disk)?; | ||
|
|
||
| #[cfg(feature = "fw_cfg")] | ||
| let bootindex = parser | ||
| .convert::<u16>("bootindex") | ||
| .map_err(Error::ParseDisk)?; | ||
|
|
||
| Ok(DiskConfig { | ||
| pci_common, | ||
| path, | ||
|
|
@@ -1557,6 +1580,8 @@ impl DiskConfig { | |
| sparse, | ||
| image_type, | ||
| lock_granularity, | ||
| #[cfg(feature = "fw_cfg")] | ||
| bootindex, | ||
| }) | ||
| } | ||
|
|
||
|
|
@@ -4125,6 +4150,8 @@ mod unit_tests { | |
| sparse: true, | ||
| image_type: ImageType::Unknown, | ||
| lock_granularity: LockGranularityChoice::default(), | ||
| #[cfg(feature = "fw_cfg")] | ||
| bootindex: Default::default(), | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -119,6 +119,8 @@ use crate::interrupt::{LegacyUserspaceInterruptManager, MsiInterruptManager}; | |||||
| use crate::memory_manager::{Error as MemoryManagerError, MEMORY_MANAGER_ACPI_SIZE, MemoryManager}; | ||||||
| use crate::pci_segment::PciSegment; | ||||||
| use crate::serial_manager::{Error as SerialManagerError, SerialManager}; | ||||||
| #[cfg(feature = "fw_cfg")] | ||||||
| use crate::vm_config::FwCfgConfig; | ||||||
| #[cfg(feature = "ivshmem")] | ||||||
| use crate::vm_config::IvshmemConfig; | ||||||
| use crate::vm_config::{ | ||||||
|
|
@@ -1149,6 +1151,9 @@ pub struct DeviceManager { | |||||
| #[cfg(feature = "ivshmem")] | ||||||
| // ivshmem device | ||||||
| ivshmem_device: Option<Arc<Mutex<devices::IvshmemDevice>>>, | ||||||
|
|
||||||
| #[cfg(feature = "fw_cfg")] | ||||||
| boot_order: HashMap<u16, String>, | ||||||
| } | ||||||
|
|
||||||
| /// Create per-PCI-segment MMIO allocators over the range `[start, end]`. | ||||||
|
|
@@ -1439,6 +1444,8 @@ impl DeviceManager { | |||||
| #[cfg(feature = "ivshmem")] | ||||||
| ivshmem_device: None, | ||||||
| _acpi_cpu_hotplug_controller: acpi_cpu_hotplug_controller, | ||||||
| #[cfg(feature = "fw_cfg")] | ||||||
| boot_order: HashMap::new(), | ||||||
| }; | ||||||
|
|
||||||
| let device_manager = Arc::new(Mutex::new(device_manager)); | ||||||
|
|
@@ -1565,6 +1572,18 @@ impl DeviceManager { | |||||
| self.ivshmem_device = self.add_ivshmem_device(ivshmem, snapshot)?; | ||||||
| } | ||||||
|
|
||||||
| #[cfg(feature = "fw_cfg")] | ||||||
| { | ||||||
| // if we have a boot order but no fw_cfg device, we must create on | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| let mut vm_config = self.config.lock().unwrap(); | ||||||
| if !self.boot_order.is_empty() | ||||||
| && vm_config.payload.is_some() | ||||||
| && vm_config.payload.as_mut().unwrap().fw_cfg_config.is_none() | ||||||
| { | ||||||
| vm_config.payload.as_mut().unwrap().fw_cfg_config = Some(FwCfgConfig::default()); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| Ok(()) | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -2895,6 +2914,11 @@ impl DeviceManager { | |||||
| .unwrap() | ||||||
| .insert(id.clone(), device_node!(id, migratable_device)); | ||||||
|
|
||||||
| #[cfg(feature = "fw_cfg")] | ||||||
| if let Some(bootindex) = disk_cfg.bootindex { | ||||||
| self.boot_order.insert(bootindex, id.clone()); | ||||||
| } | ||||||
|
|
||||||
| Ok(MetaVirtioDevice { | ||||||
| virtio_device, | ||||||
| pci_common: disk_cfg.pci_common.clone(), | ||||||
|
|
@@ -4403,11 +4427,31 @@ impl DeviceManager { | |||||
| } | ||||||
|
|
||||||
| // Update the device tree with correct resource information. | ||||||
| #[cfg(feature = "fw_cfg")] | ||||||
| let node_id = node.id.clone(); | ||||||
|
|
||||||
| node.resources = new_resources; | ||||||
| node.migratable = Some(Arc::clone(&virtio_pci_device) as Arc<Mutex<dyn Migratable>>); | ||||||
| node.pci_bdf = Some(pci_device_bdf); | ||||||
| node.pci_device_handle = Some(PciDeviceHandle::Virtio(virtio_pci_device)); | ||||||
| self.device_tree.lock().unwrap().insert(id, node); | ||||||
| self.device_tree.lock().unwrap().insert(id.clone(), node); | ||||||
|
|
||||||
| #[cfg(feature = "fw_cfg")] | ||||||
| { | ||||||
| // Find the virtio device in the boot item list | ||||||
| let boot_entry = self | ||||||
| .boot_order | ||||||
| .iter() | ||||||
| .find(|(_k, v)| *v == virtio_device_id); | ||||||
|
|
||||||
| // Update the device ID in the boot item list | ||||||
| if let Some((k, _)) = boot_entry { | ||||||
| self.boot_order.insert( | ||||||
| *k, | ||||||
| self.device_tree.lock().unwrap().fw_cfg_path_by_id(&node_id), | ||||||
| ); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| Ok(pci_device_bdf) | ||||||
| } | ||||||
|
|
@@ -5319,7 +5363,6 @@ impl DeviceManager { | |||||
| self.vfio_ops = None; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Helps the environment converge quickly after a live migration by | ||||||
| /// prompting devices to advertise the VM from its new host. | ||||||
| /// | ||||||
|
|
@@ -5362,6 +5405,19 @@ impl DeviceManager { | |||||
| MAX_DELAY, | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| /// Returns a list of devices to boot from in the order expected | ||||||
| #[cfg(feature = "fw_cfg")] | ||||||
| pub fn get_bootitems(&self) -> Vec<String> { | ||||||
| let mut result: Vec<String> = Vec::new(); | ||||||
| let mut keys: Vec<u16> = self.boot_order.clone().into_keys().collect(); | ||||||
| keys.sort(); | ||||||
|
|
||||||
| for k in &keys { | ||||||
| result.push(self.boot_order.get(k).unwrap().clone()); | ||||||
| } | ||||||
| result | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Starts a thread that periodically performs the post-migration announcements. | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,11 +7,27 @@ use std::sync::{Arc, Mutex}; | |
|
|
||
| use pci::PciBdf; | ||
| use serde::{Deserialize, Serialize}; | ||
| #[cfg(feature = "fw_cfg")] | ||
| use thiserror::Error; | ||
| use vm_device::Resource; | ||
| use vm_migration::Migratable; | ||
|
|
||
| use crate::device_manager::PciDeviceHandle; | ||
|
|
||
| #[cfg(feature = "fw_cfg")] | ||
| #[derive(Debug, Error)] | ||
| pub enum DeviceNodePathError { | ||
| /// Device is not attached to a bus. | ||
| #[error("Can't create a open firmware device path from a device not attached to a bus")] | ||
| NoBusDevice, | ||
| /// Device is not bootable. | ||
| #[error("Can't create a open firmware device path for a non-bootable device")] | ||
| DeviceNotBootable, | ||
| /// Device is not a PCI device. | ||
| #[error("Currently only PCI devices are supported!")] | ||
| NoPciDevice, | ||
| } | ||
|
|
||
| #[derive(Clone, Serialize, Deserialize)] | ||
| pub struct DeviceNode { | ||
| pub id: String, | ||
|
|
@@ -37,6 +53,36 @@ impl DeviceNode { | |
| pci_device_handle: None, | ||
| } | ||
| } | ||
|
|
||
| /// Returns the open firmware device path | ||
| /// | ||
| /// The open firmware path is used by Qemu in it's `fw_cfg` device to pass boot order hints | ||
| /// to EDK2/OVMF. [0] | ||
| /// | ||
| /// [0] https://github.com/tianocore/edk2/blob/2816ff0ab0d6505bf580aa8eec02cc2b89f04230/OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.c#L1222 | ||
| #[cfg(feature = "fw_cfg")] | ||
| pub fn openfw_device_path(&self) -> Result<String, DeviceNodePathError> { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit, feel free to resolve: if we flatten the nested clause, it will lower the cognitive load to read this function. e.g. use guard clauses in the beginning #[cfg(feature = "fw_cfg")]
pub fn openfw_device_path(&self) -> Result<String, DeviceNodePathError> {
let Some(handle) = &self.pci_device_handle else {
return Err(DeviceNodePathError::NoBusDevice);
};
let PciDeviceHandle::Virtio(virtio_handle) = handle else {
return Err(DeviceNodePathError::NoPciDevice);
};
let bdf = self.pci_bdf.expect("PCI devices should have a bdf!");
let device_type = virtio_handle
.lock()
.unwrap()
.virtio_device()
.lock()
.unwrap()
.device_type();
match device_type {
0x2 /* VirtioBlk */ => Ok(format!("/pci@i0cf8/scsi@{:x}/disk@0,0", bdf.device())),
_ => Err(DeviceNodePathError::DeviceNotBootable),
}
|
||
| if let Some(handle) = &self.pci_device_handle { | ||
| if let PciDeviceHandle::Virtio(virtio_handle) = handle { | ||
| let virtio_device = virtio_handle.lock().unwrap(); | ||
| let device_type = virtio_device.virtio_device().lock().unwrap().device_type(); | ||
| if let Some(bdf) = self.pci_bdf { | ||
| match device_type { | ||
| 0x2 /* VirtioBlk */ => { | ||
| Ok(format!("/pci@i0cf8/scsi@{:x}/disk@0,0", bdf.device())) | ||
| } | ||
| _ => Err(DeviceNodePathError::DeviceNotBootable), | ||
| } | ||
| } else { | ||
| unreachable!("PCI devices should have a bdf!") | ||
| } | ||
| } else { | ||
| Err(DeviceNodePathError::NoPciDevice) | ||
| } | ||
| } else { | ||
| Err(DeviceNodePathError::NoBusDevice) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[macro_export] | ||
|
|
@@ -102,6 +148,11 @@ impl DeviceTree { | |
| None | ||
| } | ||
| } | ||
|
|
||
| #[cfg(feature = "fw_cfg")] | ||
| pub fn fw_cfg_path_by_id(&self, id: &str) -> String { | ||
| self.get(id).unwrap().openfw_device_path().unwrap() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we handle the error cases |
||
| } | ||
| } | ||
|
|
||
| // Breadth first traversal iterator. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are all these TODO's? Do we need those?