mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
When BAR reprogramming is detected, detect_bar_reprogramming() eagerly updates the BAR address in config space before the actual MMIO remapping occurs. If the subsequent move_bar() fails (e.g. the new address falls outside the allocator range), the config register retains the new address while the MMIO bus still uses the old one, leaving the device broken. Add restore_bar_addr() to undo the config space update when move_bar() fails, so the device remains functional at its original address. For 64-bit BARs, restore both the low and high BAR slots as well as the corresponding config registers, mirroring the two-slot update logic in detect_bar_reprogramming(). Implement restore_bar_addr() for all PciDevice implementations (VirtioPciDevice, VfioPciDevice, VfioUserPciDevice, IvshmemDevice, PvPanicDevice, and PvmemcontrolPciDevice) by delegating to their respective PciConfiguration::restore_bar_addr(). Signed-off-by: CMGS <ilskdw@gmail.com>
122 lines
4.5 KiB
Rust
122 lines
4.5 KiB
Rust
// Copyright 2018 The Chromium OS Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE-BSD-3-Clause file.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
|
|
|
use std::any::Any;
|
|
use std::sync::{Arc, Barrier};
|
|
use std::{io, result};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use thiserror::Error;
|
|
use vm_allocator::{AddressAllocator, SystemAllocator};
|
|
use vm_device::Resource;
|
|
|
|
use crate::PciBarConfiguration;
|
|
use crate::configuration::{self, PciBarRegionType};
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum Error {
|
|
/// Setup of the device capabilities failed.
|
|
#[error("Setup of the device capabilities failed")]
|
|
CapabilitiesSetup(#[source] configuration::Error),
|
|
/// Allocating space for an IO BAR failed.
|
|
#[error("Allocating space for an IO BAR failed")]
|
|
IoAllocationFailed(u64),
|
|
/// Registering an IO BAR failed.
|
|
#[error("Registering an IO BAR failed")]
|
|
IoRegistrationFailed(u64, #[source] configuration::Error),
|
|
/// Expected resource not found.
|
|
#[error("Expected resource not found")]
|
|
MissingResource,
|
|
/// Invalid resource.
|
|
#[error("Invalid resource: {0:?}")]
|
|
InvalidResource(Resource),
|
|
}
|
|
pub type Result<T> = std::result::Result<T, Error>;
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
|
pub struct BarReprogrammingParams {
|
|
pub old_base: u64,
|
|
pub new_base: u64,
|
|
pub len: u64,
|
|
pub region_type: PciBarRegionType,
|
|
}
|
|
|
|
pub trait PciDevice: Send {
|
|
/// Allocates the needed PCI BARs space using the `allocate` function which takes a size and
|
|
/// returns an address. Returns a Vec of (GuestAddress, GuestUsize) tuples.
|
|
fn allocate_bars(
|
|
&mut self,
|
|
_allocator: &mut SystemAllocator,
|
|
_mmio32_allocator: &mut AddressAllocator,
|
|
_mmio64_allocator: &mut AddressAllocator,
|
|
_resources: Option<Vec<Resource>>,
|
|
) -> Result<Vec<PciBarConfiguration>> {
|
|
Ok(Vec::new())
|
|
}
|
|
|
|
/// Frees the PCI BARs previously allocated with a call to allocate_bars().
|
|
fn free_bars(
|
|
&mut self,
|
|
_allocator: &mut SystemAllocator,
|
|
_mmio32_allocator: &mut AddressAllocator,
|
|
_mmio64_allocator: &mut AddressAllocator,
|
|
) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
/// Sets a register in the configuration space.
|
|
/// * `reg_idx` - The index of the config register to modify.
|
|
/// * `offset` - Offset into the register.
|
|
fn write_config_register(
|
|
&mut self,
|
|
reg_idx: usize,
|
|
offset: u64,
|
|
data: &[u8],
|
|
) -> (Vec<BarReprogrammingParams>, Option<Arc<Barrier>>);
|
|
/// Gets a register from the configuration space.
|
|
/// * `reg_idx` - The index of the config register to read.
|
|
fn read_config_register(&mut self, reg_idx: usize) -> u32;
|
|
/// Reads from a BAR region mapped into the device.
|
|
/// * `addr` - The guest address inside the BAR.
|
|
/// * `data` - Filled with the data from `addr`.
|
|
fn read_bar(&mut self, _base: u64, _offset: u64, _data: &mut [u8]) {}
|
|
/// Writes to a BAR region mapped into the device.
|
|
/// * `addr` - The guest address inside the BAR.
|
|
/// * `data` - The data to write.
|
|
fn write_bar(&mut self, _base: u64, _offset: u64, _data: &[u8]) -> Option<Arc<Barrier>> {
|
|
None
|
|
}
|
|
/// Relocates the BAR to a different address in guest address space.
|
|
fn move_bar(&mut self, _old_base: u64, _new_base: u64) -> result::Result<(), io::Error> {
|
|
Ok(())
|
|
}
|
|
/// Restore BAR address in config space after a failed move_bar.
|
|
/// This rolls back the address update made by detect_bar_reprogramming()
|
|
/// so that the config register stays consistent with the MMIO bus mapping.
|
|
fn restore_bar_addr(&mut self, _params: &BarReprogrammingParams) {}
|
|
/// Provides a mutable reference to the Any trait. This is useful to let
|
|
/// the caller have access to the underlying type behind the trait.
|
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
|
|
|
/// Optionally returns a unique identifier.
|
|
fn id(&self) -> Option<String>;
|
|
}
|
|
|
|
/// This trait defines a set of functions which can be triggered whenever a
|
|
/// PCI device is modified in any way.
|
|
pub trait DeviceRelocation: Send + Sync {
|
|
/// The BAR needs to be moved to a different location in the guest address
|
|
/// space. This follows a decision from the software running in the guest.
|
|
fn move_bar(
|
|
&self,
|
|
old_base: u64,
|
|
new_base: u64,
|
|
len: u64,
|
|
pci_dev: &mut dyn PciDevice,
|
|
region_type: PciBarRegionType,
|
|
) -> result::Result<(), io::Error>;
|
|
}
|