From 731e9182d6b6bb9566c6444627707429b735b013 Mon Sep 17 00:00:00 2001 From: Junming Liu Date: Tue, 11 Aug 2020 20:54:24 +0800 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 1f63fb436..1c3c5faf6 100644 --- a/devicemodel/hw/pci/passthrough.c +++ b/devicemodel/hw/pci/passthrough.c @@ -89,6 +89,9 @@ struct passthru_dev { struct { int capoff; } msix; + struct { + int capoff; + } pmcap; bool pcie_cap; struct pcisel sel; int phys_pin; @@ -98,6 +101,7 @@ struct passthru_dev { * need_reset - reset dev before passthrough */ bool need_reset; + bool d3hot_reset; bool (*has_virt_pcicfg_regs)(int offset); }; @@ -165,8 +169,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); } @@ -175,6 +181,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; @@ -344,6 +369,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); @@ -488,6 +519,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; struct acrn_assign_pcidev pcidev = {}; uint16_t vendor = 0, device = 0; @@ -515,6 +547,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) @@ -532,6 +566,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();