acrn-hypervisor/devicemodel/hw/pci/gsi_sharing.c
Geoffroy Van Cutsem 8b16be9185 Remove "All rights reserved" string headers
Many of the license and Intel copyright headers include the "All rights
reserved" string. It is not relevant in the context of the BSD-3-Clause
license that the code is released under. This patch removes those strings
throughout the code (hypervisor, devicemodel and misc).

Tracked-On: #7254
Signed-off-by: Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com>
2022-04-06 13:21:02 +08:00

237 lines
5.4 KiB
C

/*
* Copyright (C) 2018 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <sys/queue.h>
#include <pciaccess.h>
#include "pci_core.h"
#include "mevent.h"
#define MAX_DEV_PER_GSI 4
/* physical info of the GSI sharing group */
struct gsi_sharing_group {
uint8_t gsi;
/* the number of devices that are sharing the same GSI */
int shared_dev_num;
struct {
char *dev_name;
int assigned_to_this_vm;
} dev[MAX_DEV_PER_GSI];
LIST_ENTRY(gsi_sharing_group) gsg_list;
};
static LIST_HEAD(gsg_struct, gsi_sharing_group) gsg_head;
static int
update_gsi_sharing_info(char *dev_name, uint8_t gsi)
{
int gsi_shared = 0;
struct gsi_sharing_group *group = NULL;
LIST_FOREACH(group, &gsg_head, gsg_list) {
if (gsi != group->gsi)
continue;
if (group->shared_dev_num >= MAX_DEV_PER_GSI) {
pr_err("max %d devices share one GSI", MAX_DEV_PER_GSI);
return -EINVAL;
}
gsi_shared = 1;
break;
}
if (gsi_shared == 0) {
group = calloc(1, sizeof(struct gsi_sharing_group));
if (!group) {
pr_err("%s: calloc FAIL!", __func__);
return -ENOMEM;
}
group->gsi = gsi;
group->shared_dev_num = 0;
LIST_INSERT_HEAD(&gsg_head, group, gsg_list);
}
if (group != NULL) {
group->dev[group->shared_dev_num].dev_name = dev_name;
group->dev[group->shared_dev_num].assigned_to_this_vm = 0;
group->shared_dev_num++;
}
return 0;
}
/*
* return 1 if MSI/MSI-x is supported
* return 0 if MSI/MSI-x is NOT supported
*/
static int
check_msi_capability(char *dev_name)
{
int bus, slot, func;
uint16_t status;
uint8_t cap_ptr, cap_id;
struct pci_device *phys_dev;
/* only check the MSI/MSI-x capability for PCI device */
if (parse_bdf(dev_name, &bus, &slot, &func, 16) != 0)
return 0;
phys_dev = pci_device_find_by_slot(0, bus, slot, func);
if (!phys_dev)
return 0;
pci_device_cfg_read_u16(phys_dev, &status, PCIR_STATUS);
if (status & PCIM_STATUS_CAPPRESENT) {
pci_device_cfg_read_u8(phys_dev, &cap_ptr, PCIR_CAP_PTR);
while (cap_ptr != 0 && cap_ptr != 0xff) {
pci_device_cfg_read_u8(phys_dev, &cap_id,
cap_ptr + PCICAP_ID);
if (cap_id == PCIY_MSI)
return 1;
else if (cap_id == PCIY_MSIX)
return 1;
pci_device_cfg_read_u8(phys_dev, &cap_ptr,
cap_ptr + PCICAP_NEXTPTR);
}
}
return 0;
}
int
create_gsi_sharing_groups(void)
{
uint8_t gsi;
char *dev_name;
int i, error, msi_support;
struct gsi_sharing_group *group = NULL, *temp = NULL;
error = pciaccess_init();
if (error < 0)
return error;
for (i = 0; i < num_gsi_dev_mapping_tables; i++) {
dev_name = gsi_dev_mapping_tables[i].dev_name;
gsi = gsi_dev_mapping_tables[i].gsi;
/* skip the devices that support MSI/MSI-x */
msi_support = check_msi_capability(dev_name);
if (msi_support == 1)
continue;
/* insert the device into gsi_sharing_group */
error = update_gsi_sharing_info(dev_name, gsi);
if (error < 0)
return error;
}
pciaccess_cleanup();
/*
* clean up gsg_head - the list for gsi_sharing_group
* delete the element without gsi sharing condition (shared_dev_num < 2)
*/
list_foreach_safe(group, &gsg_head, gsg_list, temp) {
if (group->shared_dev_num < 2) {
LIST_REMOVE(group, gsg_list);
free(group);
}
}
return 0;
}
/*
* update passthrough info in gsi_sharing_group
* set assigned_to_this_vm as 1 if the PCI device is assigned to current VM
*/
void
update_pt_info(uint16_t phys_bdf)
{
char *name;
int i, bus, slot, func;
struct gsi_sharing_group *group;
LIST_FOREACH(group, &gsg_head, gsg_list) {
for (i = 0; i < (group->shared_dev_num); i++) {
name = group->dev[i].dev_name;
if (parse_bdf(name, &bus, &slot, &func, 16) != 0)
continue;
if (phys_bdf == (PCI_BDF(bus, slot, func)))
group->dev[i].assigned_to_this_vm = 1;
}
}
}
/* check if all devices in one GSI sharing group are assigned to same VM */
int
check_gsi_sharing_violation(void)
{
struct gsi_sharing_group *group, *temp;
int i, error, violation;
error = 0;
LIST_FOREACH(group, &gsg_head, gsg_list) {
/*
* All the PCI devices that are sharing the same GSI should be
* assigned to same VM to avoid physical GSI sharing between
* multiple VMs.
*
* If the value of 'assigned_to_this_vm' for each device in one
* gsi_sharing_group are all the same, either all 0 or all 1, it
* indicates that there is no GSI sharing between multiple VMs.
* All 0 means that all devices are NOT assigned to current VM.
* All 1 means that all devices are assigned to current VM.
*
* Otherwise, the passthrough will be rejected due to violation.
*/
violation = 0;
for (i = 1; i < (group->shared_dev_num); i++) {
if (group->dev[i].assigned_to_this_vm !=
group->dev[i - 1].assigned_to_this_vm) {
violation = 1;
break;
}
}
if (violation == 0)
continue;
/* reject the passthrough since gsi sharing violation occurs */
pr_err("GSI SHARING VIOLATION!");
pr_err("following physical devices are sharing same GSI, please "
"assign them to same VM to avoid physical GSI sharing "
"between multiple VMs");
for (i = 0; i < (group->shared_dev_num); i++) {
pr_info("device %s \t assigned_to_this_vm %d",
group->dev[i].dev_name,
group->dev[i].assigned_to_this_vm);
}
error = -EINVAL;
break;
}
/* destroy the gsg_head after all the checks have been done */
list_foreach_safe(group, &gsg_head, gsg_list, temp) {
LIST_REMOVE(group, gsg_list);
free(group);
}
return error;
}