hv: vpci: restore PCI BARs when doing PCIe FLR

ACRN hypervisor should trap guest doing PCIe FLR. Besides, it should save some states
before doing the FLR and restore them later, only BARs values for now.
This patch will trap guest Device Capabilities Register write operation if the device
supports PCI Express Capability and check whether it wants to do device FLR. If it does,
call pdev_do_flr to do the job.

Tracked-On: #3465
Signed-off-by: Li Fei1 <fei1.li@intel.com>
This commit is contained in:
Li Fei1 2019-12-19 18:51:19 +08:00 committed by wenlingz
parent 92ed860187
commit bb06f6f9bb
7 changed files with 77 additions and 3 deletions

View File

@ -338,3 +338,22 @@ void udelay(uint32_t us)
while (rdtsc() < dest_tsc) {
}
}
/*
* @pre ms <= MAX_UINT32 / 1000U
*/
void msleep(uint32_t ms)
{
uint64_t dest_tsc, delta_tsc;
/* Calculate number of ticks to wait */
delta_tsc = us_to_ticks(ms * 1000U);
dest_tsc = rdtsc() + delta_tsc;
/* Loop until time expired */
while (rdtsc() < dest_tsc) {
if (need_reschedule(get_pcpu_id())) {
schedule();
}
}
}

View File

@ -233,6 +233,9 @@ void init_vdev_pt(struct pci_vdev *vdev)
vdev->nr_bars = vdev->pdev->nr_bars;
pbdf.value = vdev->pdev->bdf.value;
vdev->has_flr = vdev->pdev->has_flr;
vdev->pcie_capoff = vdev->pdev->pcie_capoff;
for (idx = 0U; idx < vdev->nr_bars; idx++) {
vbar = &vdev->bar[idx];
offset = pci_bar_offset(idx);

View File

@ -337,6 +337,10 @@ static int32_t vpci_write_pt_dev_cfg(struct pci_vdev *vdev, uint32_t offset,
vmsi_write_cfg(vdev, offset, bytes, val);
} else if (msixcap_access(vdev, offset)) {
vmsix_write_cfg(vdev, offset, bytes, val);
} else if (vdev->has_flr && ((vdev->pcie_capoff + PCIR_PCIE_DEVCTRL) == offset) &&
((val & PCIM_PCIE_FLR) != 0U)) {
/* Assume that guest write FLR must be 4 bytes aligned */
pdev_do_flr(vdev->pdev->bdf, offset, bytes, val);
} else {
/* passthru to physical device */
pci_pdev_write_cfg(vdev->pdev->bdf, offset, bytes, val);

View File

@ -42,6 +42,7 @@
#include <bits.h>
#include <board.h>
#include <platform_acpi_info.h>
#include <timer.h>
static spinlock_t pci_device_lock;
static uint32_t num_pci_pdev;
@ -393,7 +394,7 @@ static inline uint32_t pci_pdev_get_nr_bars(uint8_t hdr_type)
switch (hdr_type) {
case PCIM_HDRTYPE_NORMAL:
nr_bars = 6U;
nr_bars = PCI_STD_NUM_BARS;
break;
case PCIM_HDRTYPE_BRIDGE:
@ -417,6 +418,7 @@ static void pci_read_cap(struct pci_pdev *pdev)
uint32_t msgctrl;
uint32_t len, offset, idx;
uint32_t table_info;
uint32_t pcie_devcap;
ptr = (uint8_t)pci_pdev_read_cfg(pdev->bdf, PCIR_CAP_PTR, 1U);
@ -424,11 +426,11 @@ static void pci_read_cap(struct pci_pdev *pdev)
cap = (uint8_t)pci_pdev_read_cfg(pdev->bdf, ptr + PCICAP_ID, 1U);
/* Ignore all other Capability IDs for now */
if ((cap == PCIY_MSI) || (cap == PCIY_MSIX)) {
if ((cap == PCIY_MSI) || (cap == PCIY_MSIX) || (cap == PCIY_PCIE)) {
offset = ptr;
if (cap == PCIY_MSI) {
pdev->msi_capoff = offset;
} else {
} else if (cap == PCIY_MSIX) {
pdev->msix.capoff = offset;
pdev->msix.caplen = MSIX_CAPLEN;
len = pdev->msix.caplen;
@ -449,6 +451,11 @@ static void pci_read_cap(struct pci_pdev *pdev)
for (idx = 0U; idx < len; idx++) {
pdev->msix.cap[idx] = (uint8_t)pci_pdev_read_cfg(pdev->bdf, offset + idx, 1U);
}
} else {
/* PCI Express Capability */
pdev->pcie_capoff = offset;
pcie_devcap = pci_pdev_read_cfg(pdev->bdf, offset + PCIR_PCIE_DEVCAP, 4U);
pdev->has_flr = ((pcie_devcap & PCIM_PCIE_FLRCAP) != 0U) ? true : false;
}
}
@ -489,3 +496,27 @@ static void init_pdev(uint16_t pbdf, uint32_t drhd_index)
pr_err("%s, failed to alloc pci_pdev!\n", __func__);
}
}
void pdev_do_flr(union pci_bdf bdf, uint32_t offset, uint32_t bytes, uint32_t val)
{
uint32_t idx;
uint32_t bars[PCI_STD_NUM_BARS];
for (idx = 0U; idx < PCI_STD_NUM_BARS; idx++) {
bars[idx] = pci_pdev_read_cfg(bdf, pci_bar_offset(idx), 4U);
}
/* do the real reset */
pci_pdev_write_cfg(bdf, offset, bytes, val);
/*
* After an FLR has been initiated by writing a 1b to
* the Initiate Function Level Reset bit,
* the Function must complete the FLR within 100 ms
*/
msleep(100U);
for (idx = 0U; idx < PCI_STD_NUM_BARS; idx++) {
pci_pdev_write_cfg(bdf, pci_bar_offset(idx), 4U, bars[idx]);
}
}

View File

@ -50,6 +50,7 @@ struct hv_timer {
#define CYCLES_PER_MS us_to_ticks(1000U)
void udelay(uint32_t us);
void msleep(uint32_t ms);
/**
* @brief convert us to ticks.

View File

@ -99,6 +99,9 @@ struct pci_vdev {
struct pci_msi msi;
struct pci_msix msix;
bool has_flr;
uint32_t pcie_capoff;
/* Pointer to corresponding PCI device's vm_config */
struct acrn_vm_pci_dev_config *pci_dev_config;

View File

@ -131,7 +131,15 @@
#define MSIX_CAPLEN 12U
#define MSIX_TABLE_ENTRY_SIZE 16U
/* PCI Express Capability */
#define PCIY_PCIE 0x10U
#define PCIR_PCIE_DEVCAP 0x04U
#define PCIR_PCIE_DEVCTRL 0x08U
#define PCIM_PCIE_FLRCAP (0x1U << 28U)
#define PCIM_PCIE_FLR (0x1U << 15U)
#define HOST_BRIDGE_BDF 0U
#define PCI_STD_NUM_BARS 6U
union pci_bdf {
uint16_t value;
@ -177,6 +185,10 @@ struct pci_pdev {
uint32_t msi_capoff;
struct pci_msix_cap msix;
/* Function Level Reset Capability */
bool has_flr;
uint32_t pcie_capoff;
};
static inline uint32_t pci_bar_offset(uint32_t idx)
@ -291,5 +303,6 @@ static inline bool is_pci_cfg_bridge(uint8_t header_type)
return ((header_type & PCIM_HDRTYPE) == PCIM_HDRTYPE_BRIDGE);
}
void pdev_do_flr(union pci_bdf bdf, uint32_t offset, uint32_t bytes, uint32_t val);
#endif /* PCI_H_ */