hv: vpci: add vmsi capability registers rw permission control

Guest may write a MSI capability register with only RW bits setting on. This works
well on native since the hardware will make sure RO register bits could not over-write.
However, the software needs more efforts to achieve this. This patch does this by
defining a RW permission mapping base on bits. When a guest tries to write a MSI
Capability register, only modify the RW bits on vCFG space.

Tracked-On: #4550
Signed-off-by: Li Fei1 <fei1.li@intel.com>
This commit is contained in:
Li Fei1 2020-05-21 08:48:11 +08:00 committed by wenlingz
parent ccbabb4ce8
commit ea0ba47b02
3 changed files with 21 additions and 12 deletions

View File

@ -97,7 +97,7 @@ static void remap_vmsi(const struct pci_vdev *vdev)
/**
* @pre vdev != NULL
*/
void read_vmsi_cfg(const struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t *val)
void read_vmsi_cap_reg(const struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t *val)
{
/* For PIO access, we emulate Capability Structures only */
*val = pci_vdev_read_vcfg(vdev, offset, bytes);
@ -108,16 +108,24 @@ void read_vmsi_cfg(const struct pci_vdev *vdev, uint32_t offset, uint32_t bytes,
*
* @pre vdev != NULL
*/
void write_vmsi_cfg(struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t val)
void write_vmsi_cap_reg(struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t val)
{
uint32_t msgctrl;
/* Capability ID, Next Capability Pointer and Message Control
* (Except MSI Enable bit and Multiple Message Enable) are RO */
static const uint8_t msi_ro_mask[0xEU] = { 0xffU, 0xffU, 0x1eU, 0xffU };
uint32_t msgctrl, old, ro_mask = ~0U;
enable_disable_msi(vdev, false);
pci_vdev_write_vcfg(vdev, offset, bytes, val);
msgctrl = pci_vdev_read_vcfg(vdev, vdev->msi.capoff + PCIR_MSI_CTRL, 2U);
if ((msgctrl & PCIM_MSICTRL_MSI_ENABLE) != 0U) {
remap_vmsi(vdev);
(void)memcpy_s((void *)&ro_mask, bytes, (void *)&msi_ro_mask[offset - vdev->msi.capoff], bytes);
if (ro_mask != ~0U) {
old = pci_vdev_read_vcfg(vdev, offset, bytes);
pci_vdev_write_vcfg(vdev, offset, bytes, (old & ro_mask) | (val & ~ro_mask));
msgctrl = pci_vdev_read_vcfg(vdev, vdev->msi.capoff + PCIR_MSI_CTRL, 2U);
if ((msgctrl & (PCIM_MSICTRL_MSI_ENABLE | PCIM_MSICTRL_MME_MASK)) == PCIM_MSICTRL_MSI_ENABLE) {
remap_vmsi(vdev);
}
}
}
@ -146,11 +154,12 @@ void init_vmsi(struct pci_vdev *vdev)
if (has_msi_cap(vdev)) {
val = pci_pdev_read_cfg(pdev->bdf, vdev->msi.capoff, 4U);
vdev->msi.caplen = ((val & (PCIM_MSICTRL_64BIT << 16U)) != 0U) ? 14U : 10U;
vdev->msi.caplen = ((val & (PCIM_MSICTRL_64BIT << 16U)) != 0U) ? 0xEU : 0xAU;
vdev->msi.is_64bit = ((val & (PCIM_MSICTRL_64BIT << 16U)) != 0U);
val &= ~((uint32_t)PCIM_MSICTRL_MMC_MASK << 16U);
val &= ~((uint32_t)PCIM_MSICTRL_MME_MASK << 16U);
pci_vdev_write_vcfg(vdev, vdev->msi.capoff, 4U, val);
}
}

View File

@ -496,7 +496,7 @@ static int32_t write_pt_dev_cfg(struct pci_vdev *vdev, uint32_t offset,
if (cfg_header_access(offset)) {
write_cfg_header(vdev, offset, bytes, val);
} else if (msicap_access(vdev, offset)) {
write_vmsi_cfg(vdev, offset, bytes, val);
write_vmsi_cap_reg(vdev, offset, bytes, val);
} else if (msixcap_access(vdev, offset)) {
write_vmsix_cfg(vdev, offset, bytes, val);
} else if (sriovcap_access(vdev, offset)) {
@ -521,7 +521,7 @@ static int32_t read_pt_dev_cfg(const struct pci_vdev *vdev, uint32_t offset,
if (cfg_header_access(offset)) {
read_cfg_header(vdev, offset, bytes, val);
} else if (msicap_access(vdev, offset)) {
read_vmsi_cfg(vdev, offset, bytes, val);
read_vmsi_cap_reg(vdev, offset, bytes, val);
} else if (msixcap_access(vdev, offset)) {
read_vmsix_cfg(vdev, offset, bytes, val);
} else if (sriovcap_access(vdev, offset)) {

View File

@ -118,8 +118,8 @@ void vdev_pt_write_vbar(struct pci_vdev *vdev, uint32_t idx, uint32_t val);
void vdev_pt_map_msix(struct pci_vdev *vdev, bool hold_lock);
void init_vmsi(struct pci_vdev *vdev);
void read_vmsi_cfg(const struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t *val);
void write_vmsi_cfg(struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t val);
void read_vmsi_cap_reg(const struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t *val);
void write_vmsi_cap_reg(struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t val);
void deinit_vmsi(struct pci_vdev *vdev);
void init_vmsix(struct pci_vdev *vdev);