hv: pci: Add guest cfg header access handling of type 1 device

When guests resume form s3, an error occurs in guest:

```
pcieport 0000:00:1c.0: refused to change power state from D0 to D3hot
```

PCI bridge (type 1 device) will access configuration space header but
now acrn is not supported. So add handling support.

Tracked-On: #8623
Signed-off-by: Haiwei Li <haiwei.li@intel.com>
This commit is contained in:
Haiwei Li 2024-06-19 14:02:53 +08:00 committed by acrnsi-robot
parent 2cd0edaf9c
commit 5283c147ef
2 changed files with 57 additions and 14 deletions

View File

@ -414,22 +414,41 @@ struct cfg_header_perm {
*
* For each mask, only low 16-bits takes effect.
*
* If bit x is set the pt_mask, it indicates that the corresponding 4 Bytes register
* for bit x is pass through to guest. Otherwise, it's virtualized.
* When guest read:
* If bit x is set the pt_mask, it indicates that the corresponding 4 Bytes register for bit x is pass through to guest.
* Otherwise, bit x is not set the pt_mask, it's virtualized.
*
* If bit x is set the ro_mask, it indicates that the corresponding 4 Bytes register
* for bit x is read-only. Otherwise, it's writable.
* When guest write:
* If bit x is set the ro_mask, it indicates that the corresponding 4 Bytes register for bit x is not writable.
* Otherwise, that is, bit x is not set the ro_mask,
* If bit x is set the pt_mask, it indicates that the corresponding 4 Bytes register for bit x is pass through to guest.
* If bit x is not set the pt_mask, it's virtualized.
*/
uint32_t pt_mask;
uint32_t ro_mask;
/* For type 0 device */
uint32_t type0_pt_mask;
uint32_t type0_ro_mask;
/* For type 1 device */
uint32_t type1_pt_mask;
uint32_t type1_ro_mask;
};
static const struct cfg_header_perm cfg_hdr_perm = {
/* Only Command (0x04-0x05) and Status (0x06-0x07) Registers are pass through */
.pt_mask = 0x0002U,
.type0_pt_mask = 0x0002U,
/* Command (0x04-0x05) and Status (0x06-0x07) Registers and
* Base Address Registers (0x10-0x27) are writable */
.ro_mask = (uint16_t)~0x03f2U
.type0_ro_mask = (uint16_t)~0x03f2U,
/* Command (0x04-0x05) and Status (0x06-0x07) Registers and
* from Primary Bus Number to I/O Base Limit 16 Bits (0x18-0x33)
* are pass through
*/
.type1_pt_mask = 0x1fc2U,
/* Command (0x04-0x05) and Status (0x06-0x07) Registers and
* Base Address Registers (0x10-0x17) and
* Secondary Status (0x1e-0x1f) are writable
* Note: should handle I/O Base (0x1c) specially
*/
.type1_ro_mask = (uint16_t)~0xb2U
};
@ -440,6 +459,7 @@ static int32_t read_cfg_header(const struct pci_vdev *vdev,
uint32_t offset, uint32_t bytes, uint32_t *val)
{
int32_t ret = 0;
uint32_t pt_mask;
if ((offset == PCIR_BIOS) && is_quirk_ptdev(vdev)) {
/* the access of PCIR_BIOS is emulated for quirk_ptdev */
@ -452,8 +472,13 @@ static int32_t read_cfg_header(const struct pci_vdev *vdev,
*val = ~0U;
}
} else {
/* ToDo: add cfg_hdr_perm for Type 1 device */
if (bitmap32_test(((uint16_t)offset) >> 2U, &cfg_hdr_perm.pt_mask)) {
if (is_bridge(vdev->pdev)) {
pt_mask = cfg_hdr_perm.type1_pt_mask;
} else {
pt_mask = cfg_hdr_perm.type0_pt_mask;
}
if (bitmap32_test(((uint16_t)offset) >> 2U, &pt_mask)) {
*val = pci_pdev_read_cfg(vdev->pdev->bdf, offset, bytes);
/* MSE(Memory Space Enable) bit always be set for an assigned VF */
@ -476,6 +501,7 @@ static int32_t write_cfg_header(struct pci_vdev *vdev,
{
bool dev_is_bridge = is_bridge(vdev->pdev);
int32_t ret = 0;
uint32_t pt_mask, ro_mask;
if ((offset == PCIR_BIOS) && is_quirk_ptdev(vdev)) {
/* the access of PCIR_BIOS is emulated for quirk_ptdev */
@ -507,10 +533,25 @@ static int32_t write_cfg_header(struct pci_vdev *vdev,
}
}
/* ToDo: add cfg_hdr_perm for Type 1 device */
if (!bitmap32_test(((uint16_t)offset) >> 2U, &cfg_hdr_perm.ro_mask)) {
if (bitmap32_test(((uint16_t)offset) >> 2U, &cfg_hdr_perm.pt_mask)) {
pci_pdev_write_cfg(vdev->pdev->bdf, offset, bytes, val);
if (dev_is_bridge) {
ro_mask = cfg_hdr_perm.type1_ro_mask;
pt_mask = cfg_hdr_perm.type1_pt_mask;
} else {
ro_mask = cfg_hdr_perm.type0_ro_mask;
pt_mask = cfg_hdr_perm.type0_pt_mask;
}
if (!bitmap32_test(((uint16_t)offset) >> 2U, &ro_mask)) {
if (bitmap32_test(((uint16_t)offset) >> 2U, &pt_mask)) {
/* I/O Base (0x1c) and I/O Limit (0x1d) are read-only */
if (!((offset == PCIR_IO_BASE) && (bytes <= 2)) && (offset != PCIR_IO_LIMIT)) {
uint32_t value = val;
if ((offset == PCIR_IO_BASE) && (bytes == 4U)) {
uint16_t phys_val = (uint16_t)pci_pdev_read_cfg(vdev->pdev->bdf, offset, 2U);
value = (val & PCIR_SECSTATUS_LINE_MASK) | phys_val;
}
pci_pdev_write_cfg(vdev->pdev->bdf, offset, bytes, value);
}
} else {
pci_vdev_write_vcfg(vdev, offset, bytes, val);
}

View File

@ -107,8 +107,10 @@
#define PCIR_SECBUS_1 0x19U
#define PCIR_SUBBUS_1 0x1AU
#define PCIR_IO_BASE 0x1CU
#define PCIR_IO_LIMIT 0x1DU
#define PCIR_MEM_BASE 0x20U
#define PCIR_IO_BASE_UPPER_16 0x30U
#define PCIR_SECSTATUS_LINE_MASK 0xFFFF0000U
/* Capability Register Offsets */
#define PCICAP_ID 0x0U