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 <long.liu@intel.com>
Reviewed-by: Yuan Liu <yuan1.liu@intel.com>
Acked-by: Yu Wang <yu1.wang@intel.com>
This commit is contained in:
Long Liu 2020-07-20 09:36:27 +00:00 committed by wenlingz
parent 89f26d9e81
commit 4b03f3002e
1 changed files with 36 additions and 1 deletions

View File

@ -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();