From 4b03f3002e9290cbb64299b47c0696668f862aeb Mon Sep 17 00:00:00 2001 From: Long Liu Date: Mon, 20 Jul 2020 09:36:27 +0000 Subject: [PATCH] DM: PT: Add "d3hot_reset" sub-parameter for passthrough device Some passthrough devices have no reset mechanisms which cause the device stay in unknown status during boot/reboot flow. And such unknown status cause unexpected behaviors in the guest. Except the ordinary reset mechanisms like FLR, we can utilize enter/exit D3cold as the reset that D3cold will power gate the entire hardware. But the D3cold is implemented as ACPI method which has no user interface in the SOS side. But the D3cold is implemented as ACPI method which has no user interface in the SOS side. But base on our experience, some devices can utilize D3hot instead of D3cold. But it is not useful for all PCI devices as the power status of D3hot is implementation defined. Provide one new API to program PowerState(D0/D1/D2/D3hot) in PMCSR register. Add "d3hot_reset" sub-parameter for passthrough device to enable utilize enter/exit D3hot flow to implement reset mechanisms. Tracked-On: #5067 Signed-off-by: Long Liu Reviewed-by: Yuan Liu Acked-by: Yu Wang --- devicemodel/hw/pci/passthrough.c | 37 +++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/devicemodel/hw/pci/passthrough.c b/devicemodel/hw/pci/passthrough.c index 3a14bf4f3..e1d985263 100644 --- a/devicemodel/hw/pci/passthrough.c +++ b/devicemodel/hw/pci/passthrough.c @@ -107,6 +107,9 @@ struct passthru_dev { struct { int capoff; } msix; + struct { + int capoff; + } pmcap; bool pcie_cap; struct pcisel sel; int phys_pin; @@ -116,6 +119,7 @@ struct passthru_dev { * need_reset - reset dev before passthrough */ bool need_reset; + bool d3hot_reset; bool (*has_virt_pcicfg_regs)(int offset); }; @@ -183,8 +187,10 @@ cfginit_cap(struct vmctx *ctx, struct passthru_dev *ptdev) ptdev->msi.capoff = ptr; } else if (cap == PCIY_MSIX) { ptdev->msix.capoff = ptr; - } else if (cap == PCIY_EXPRESS) + } else if (cap == PCIY_EXPRESS) { ptdev->pcie_cap = true; + } else if (cap == PCIY_PMG) + ptdev->pmcap.capoff = ptr; ptr = read_config(phys_dev, ptr + PCICAP_NEXTPTR, 1); } @@ -193,6 +199,25 @@ cfginit_cap(struct vmctx *ctx, struct passthru_dev *ptdev) return 0; } +static int +passthru_set_power_state(struct passthru_dev *ptdev, uint16_t dpsts) { + int ret = -1; + uint32_t val; + + dpsts &= PCIM_PSTAT_DMASK; + if (ptdev->pmcap.capoff != 0) { + val = read_config(ptdev->phys_dev, + ptdev->pmcap.capoff + PCIR_POWER_STATUS, 2); + val = (val & ~PCIM_PSTAT_DMASK) | dpsts; + + write_config(ptdev->phys_dev, + ptdev->pmcap.capoff + PCIR_POWER_STATUS, 2, val); + + ret = 0; + } + return ret; +} + static inline int ptdev_msix_table_bar(struct passthru_dev *ptdev) { return ptdev->dev->msix.table_bar; @@ -362,6 +387,12 @@ cfginit(struct vmctx *ctx, struct passthru_dev *ptdev, int bus, } } + if (ptdev->d3hot_reset) { + if ((passthru_set_power_state(ptdev, PCIM_PSTAT_D3) != 0) || + passthru_set_power_state(ptdev, PCIM_PSTAT_D0) != 0) + warnx("ptdev %x/%x/%x do d3hot_reset failed!\n", bus, slot, func); + } + if (cfginitbar(ctx, ptdev) != 0) { warnx("failed to initialize BARs for PCI %x/%x/%x", bus, slot, func); @@ -523,6 +554,7 @@ passthru_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) char *opt; bool keep_gsi = false; bool need_reset = true; + bool d3hot_reset = false; int vmsix_on_msi_bar_id = -1; struct acrn_assign_pcidev pcidev = {}; uint16_t vendor = 0, device = 0; @@ -551,6 +583,8 @@ passthru_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) keep_gsi = true; else if (!strncmp(opt, "no_reset", 8)) need_reset = false; + else if (!strncmp(opt, "d3hot_reset", 11)) + d3hot_reset = true; else if (!strncmp(opt, "gpu", 3)) { /* Create the dedicated "igd-lpc" on 00:1f.0 for IGD passthrough */ if (pci_parse_slot("31,igd-lpc") != 0) @@ -575,6 +609,7 @@ passthru_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) ptdev->phys_bdf = PCI_BDF(bus, slot, func); ptdev->need_reset = need_reset; + ptdev->d3hot_reset = d3hot_reset; update_pt_info(ptdev->phys_bdf); error = pciaccess_init();