1 /*
2  * Copyright (C) 2018-2022 Intel Corporation.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <err.h>
11 #include <errno.h>
12 #include <sys/queue.h>
13 #include <pciaccess.h>
14 
15 #include "pci_core.h"
16 #include "mevent.h"
17 
18 #define MAX_DEV_PER_GSI 4
19 
20 /* physical info of the GSI sharing group */
21 struct gsi_sharing_group {
22 	uint8_t gsi;
23 	/* the number of devices that are sharing the same GSI */
24 	int shared_dev_num;
25 	struct {
26 		char *dev_name;
27 		int assigned_to_this_vm;
28 	} dev[MAX_DEV_PER_GSI];
29 
30 	LIST_ENTRY(gsi_sharing_group) gsg_list;
31 };
32 static LIST_HEAD(gsg_struct, gsi_sharing_group) gsg_head;
33 
34 static int
update_gsi_sharing_info(char * dev_name,uint8_t gsi)35 update_gsi_sharing_info(char *dev_name, uint8_t gsi)
36 {
37 	int gsi_shared = 0;
38 	struct gsi_sharing_group *group = NULL;
39 
40 	LIST_FOREACH(group, &gsg_head, gsg_list) {
41 		if (gsi != group->gsi)
42 			continue;
43 
44 		if (group->shared_dev_num >= MAX_DEV_PER_GSI) {
45 			pr_err("max %d devices share one GSI", MAX_DEV_PER_GSI);
46 			return -EINVAL;
47 		}
48 
49 		gsi_shared = 1;
50 		break;
51 	}
52 
53 	if (gsi_shared == 0) {
54 		group = calloc(1, sizeof(struct gsi_sharing_group));
55 		if (!group) {
56 			pr_err("%s: calloc FAIL!", __func__);
57 			return -ENOMEM;
58 		}
59 
60 		group->gsi = gsi;
61 		group->shared_dev_num = 0;
62 
63 		LIST_INSERT_HEAD(&gsg_head, group, gsg_list);
64 	}
65 
66 	if (group != NULL) {
67 		group->dev[group->shared_dev_num].dev_name = dev_name;
68 		group->dev[group->shared_dev_num].assigned_to_this_vm = 0;
69 		group->shared_dev_num++;
70 	}
71 
72 	return 0;
73 }
74 
75 /*
76  * return 1 if MSI/MSI-x is supported
77  * return 0 if MSI/MSI-x is NOT supported
78  */
79 static int
check_msi_capability(char * dev_name)80 check_msi_capability(char *dev_name)
81 {
82 	int bus, slot, func;
83 	uint16_t status;
84 	uint8_t cap_ptr, cap_id;
85 	struct pci_device *phys_dev;
86 
87 	/* only check the MSI/MSI-x capability for PCI device */
88 	if (parse_bdf(dev_name, &bus, &slot, &func, 16) != 0)
89 		return 0;
90 
91 	phys_dev = pci_device_find_by_slot(0, bus, slot, func);
92 	if (!phys_dev)
93 		return 0;
94 
95 	pci_device_cfg_read_u16(phys_dev, &status, PCIR_STATUS);
96 	if (status & PCIM_STATUS_CAPPRESENT) {
97 		pci_device_cfg_read_u8(phys_dev, &cap_ptr, PCIR_CAP_PTR);
98 
99 		while (cap_ptr != 0 && cap_ptr != 0xff) {
100 			pci_device_cfg_read_u8(phys_dev, &cap_id,
101 						cap_ptr + PCICAP_ID);
102 
103 			if (cap_id == PCIY_MSI)
104 				return 1;
105 			else if (cap_id == PCIY_MSIX)
106 				return 1;
107 
108 			pci_device_cfg_read_u8(phys_dev, &cap_ptr,
109 						cap_ptr + PCICAP_NEXTPTR);
110 		}
111 	}
112 	return 0;
113 }
114 
115 int
create_gsi_sharing_groups(void)116 create_gsi_sharing_groups(void)
117 {
118 	uint8_t gsi;
119 	char *dev_name;
120 	int i, error, msi_support;
121 	struct gsi_sharing_group *group = NULL, *temp = NULL;
122 
123 	error = pciaccess_init();
124 	if (error < 0)
125 		return error;
126 
127 	for (i = 0; i < num_gsi_dev_mapping_tables; i++) {
128 		dev_name = gsi_dev_mapping_tables[i].dev_name;
129 		gsi = gsi_dev_mapping_tables[i].gsi;
130 
131 		/* skip the devices that support MSI/MSI-x */
132 		msi_support = check_msi_capability(dev_name);
133 		if (msi_support == 1)
134 			continue;
135 
136 		/* insert the device into gsi_sharing_group */
137 		error = update_gsi_sharing_info(dev_name, gsi);
138 		if (error < 0)
139 			return error;
140 	}
141 
142 	pciaccess_cleanup();
143 
144 	/*
145 	 * clean up gsg_head - the list for gsi_sharing_group
146 	 * delete the element without gsi sharing condition (shared_dev_num < 2)
147 	 */
148 	list_foreach_safe(group, &gsg_head, gsg_list, temp) {
149 		if (group->shared_dev_num < 2) {
150 			LIST_REMOVE(group, gsg_list);
151 			free(group);
152 		}
153 	}
154 
155 	return 0;
156 }
157 
158 /*
159  * update passthrough info in gsi_sharing_group
160  * set assigned_to_this_vm as 1 if the PCI device is assigned to current VM
161  */
162 void
update_pt_info(uint16_t phys_bdf)163 update_pt_info(uint16_t phys_bdf)
164 {
165 	char *name;
166 	int i, bus, slot, func;
167 	struct gsi_sharing_group *group;
168 
169 	LIST_FOREACH(group, &gsg_head, gsg_list) {
170 		for (i = 0; i < (group->shared_dev_num); i++) {
171 			name = group->dev[i].dev_name;
172 			if (parse_bdf(name, &bus, &slot, &func, 16) != 0)
173 				continue;
174 
175 			if (phys_bdf == (PCI_BDF(bus, slot, func)))
176 				group->dev[i].assigned_to_this_vm = 1;
177 		}
178 	}
179 }
180 
181 /* check if all devices in one GSI sharing group are assigned to same VM */
182 int
check_gsi_sharing_violation(void)183 check_gsi_sharing_violation(void)
184 {
185 	struct gsi_sharing_group *group, *temp;
186 	int i, error, violation;
187 
188 	error = 0;
189 	LIST_FOREACH(group, &gsg_head, gsg_list) {
190 		/*
191 		 * All the PCI devices that are sharing the same GSI should be
192 		 * assigned to same VM to avoid physical GSI sharing between
193 		 * multiple VMs.
194 		 *
195 		 * If the value of 'assigned_to_this_vm' for each device in one
196 		 * gsi_sharing_group are all the same, either all 0 or all 1, it
197 		 * indicates that there is no GSI sharing between multiple VMs.
198 		 * All 0 means that all devices are NOT assigned to current VM.
199 		 * All 1 means that all devices are assigned to current VM.
200 		 *
201 		 * Otherwise, the passthrough will be rejected due to violation.
202 		 */
203 		violation = 0;
204 		for (i = 1; i < (group->shared_dev_num); i++)  {
205 			if (group->dev[i].assigned_to_this_vm !=
206 				group->dev[i - 1].assigned_to_this_vm) {
207 				violation = 1;
208 				break;
209 			}
210 		}
211 
212 		if (violation == 0)
213 			continue;
214 
215 		/* reject the passthrough since gsi sharing violation occurs */
216 		pr_err("GSI SHARING VIOLATION!");
217 		pr_err("following physical devices are sharing same GSI, please "
218 			"assign them to same VM to avoid physical GSI sharing "
219 			"between multiple VMs");
220 		for (i = 0; i < (group->shared_dev_num); i++) {
221 			pr_info("device %s \t assigned_to_this_vm %d",
222 				group->dev[i].dev_name,
223 				group->dev[i].assigned_to_this_vm);
224 		}
225 		error = -EINVAL;
226 		break;
227 	}
228 
229 	/* destroy the gsg_head after all the checks have been done */
230 	list_foreach_safe(group, &gsg_head, gsg_list, temp) {
231 		LIST_REMOVE(group, gsg_list);
232 		free(group);
233 	}
234 
235 	return error;
236 }
237