diff --git a/hypervisor/arch/x86/timer.c b/hypervisor/arch/x86/timer.c index 36eaa2673..849b6691b 100644 --- a/hypervisor/arch/x86/timer.c +++ b/hypervisor/arch/x86/timer.c @@ -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(); + } + } +} diff --git a/hypervisor/dm/vpci/pci_pt.c b/hypervisor/dm/vpci/pci_pt.c index 1bb71a614..124eb056e 100644 --- a/hypervisor/dm/vpci/pci_pt.c +++ b/hypervisor/dm/vpci/pci_pt.c @@ -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); diff --git a/hypervisor/dm/vpci/vpci.c b/hypervisor/dm/vpci/vpci.c index 78fa17cb8..9f146653b 100644 --- a/hypervisor/dm/vpci/vpci.c +++ b/hypervisor/dm/vpci/vpci.c @@ -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); diff --git a/hypervisor/hw/pci.c b/hypervisor/hw/pci.c index ea3013382..1da24fcdb 100644 --- a/hypervisor/hw/pci.c +++ b/hypervisor/hw/pci.c @@ -42,6 +42,7 @@ #include #include #include +#include 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]); + } +} diff --git a/hypervisor/include/arch/x86/timer.h b/hypervisor/include/arch/x86/timer.h index 71c5af6ac..5e1d44737 100644 --- a/hypervisor/include/arch/x86/timer.h +++ b/hypervisor/include/arch/x86/timer.h @@ -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. diff --git a/hypervisor/include/dm/vpci.h b/hypervisor/include/dm/vpci.h index a51107550..1e3642417 100644 --- a/hypervisor/include/dm/vpci.h +++ b/hypervisor/include/dm/vpci.h @@ -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; diff --git a/hypervisor/include/hw/pci.h b/hypervisor/include/hw/pci.h index 42fc3f9da..95dd936dc 100644 --- a/hypervisor/include/hw/pci.h +++ b/hypervisor/include/hw/pci.h @@ -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_ */