xref: /illumos-gate/usr/src/lib/libppt/common/libppt.c (revision eb9a1df2)
1*eb9a1df2SHans Rosenfeld /*
2*eb9a1df2SHans Rosenfeld  * This file and its contents are supplied under the terms of the
3*eb9a1df2SHans Rosenfeld  * Common Development and Distribution License ("CDDL"), version 1.0.
4*eb9a1df2SHans Rosenfeld  * You may only use this file in accordance with the terms of version
5*eb9a1df2SHans Rosenfeld  * 1.0 of the CDDL.
6*eb9a1df2SHans Rosenfeld  *
7*eb9a1df2SHans Rosenfeld  * A full copy of the text of the CDDL should have accompanied this
8*eb9a1df2SHans Rosenfeld  * source.  A copy of the CDDL is also available via the Internet at
9*eb9a1df2SHans Rosenfeld  * http://www.illumos.org/license/CDDL.
10*eb9a1df2SHans Rosenfeld  */
11*eb9a1df2SHans Rosenfeld 
12*eb9a1df2SHans Rosenfeld /*
13*eb9a1df2SHans Rosenfeld  * Copyright 2018 Joyent, Inc.
14*eb9a1df2SHans Rosenfeld  *
15*eb9a1df2SHans Rosenfeld  * Convenience routines for identifying current or available devices that are
16*eb9a1df2SHans Rosenfeld  * suitable for PCI passthrough to a bhyve guest.
17*eb9a1df2SHans Rosenfeld  */
18*eb9a1df2SHans Rosenfeld 
19*eb9a1df2SHans Rosenfeld #include <libdevinfo.h>
20*eb9a1df2SHans Rosenfeld #include <libppt.h>
21*eb9a1df2SHans Rosenfeld 
22*eb9a1df2SHans Rosenfeld #include <sys/param.h>
23*eb9a1df2SHans Rosenfeld #include <sys/stat.h>
24*eb9a1df2SHans Rosenfeld #include <sys/list.h>
25*eb9a1df2SHans Rosenfeld #include <strings.h>
26*eb9a1df2SHans Rosenfeld #include <stddef.h>
27*eb9a1df2SHans Rosenfeld #include <stdlib.h>
28*eb9a1df2SHans Rosenfeld #include <stdio.h>
29*eb9a1df2SHans Rosenfeld #include <errno.h>
30*eb9a1df2SHans Rosenfeld #include <pcidb.h>
31*eb9a1df2SHans Rosenfeld #include <glob.h>
32*eb9a1df2SHans Rosenfeld 
33*eb9a1df2SHans Rosenfeld typedef struct node_data {
34*eb9a1df2SHans Rosenfeld 	pcidb_hdl_t *nd_db;
35*eb9a1df2SHans Rosenfeld 	list_t nd_matches;
36*eb9a1df2SHans Rosenfeld 	nvlist_t *nd_nvl;
37*eb9a1df2SHans Rosenfeld 	int nd_err;
38*eb9a1df2SHans Rosenfeld } node_data_t;
39*eb9a1df2SHans Rosenfeld 
40*eb9a1df2SHans Rosenfeld typedef struct ppt_match {
41*eb9a1df2SHans Rosenfeld 	list_node_t pm_list;
42*eb9a1df2SHans Rosenfeld 	char pm_path[MAXPATHLEN];
43*eb9a1df2SHans Rosenfeld 	char pm_vendor[5];
44*eb9a1df2SHans Rosenfeld 	char pm_device[5];
45*eb9a1df2SHans Rosenfeld } ppt_match_t;
46*eb9a1df2SHans Rosenfeld 
47*eb9a1df2SHans Rosenfeld static boolean_t
is_pci(di_node_t di_node)48*eb9a1df2SHans Rosenfeld is_pci(di_node_t di_node)
49*eb9a1df2SHans Rosenfeld {
50*eb9a1df2SHans Rosenfeld 	char *svals;
51*eb9a1df2SHans Rosenfeld 
52*eb9a1df2SHans Rosenfeld 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, di_parent_node(di_node),
53*eb9a1df2SHans Rosenfeld 	    "device_type", &svals) != 1)
54*eb9a1df2SHans Rosenfeld 		return (B_FALSE);
55*eb9a1df2SHans Rosenfeld 
56*eb9a1df2SHans Rosenfeld 	return (strcmp(svals, "pci") == 0 || strcmp(svals, "pciex") == 0);
57*eb9a1df2SHans Rosenfeld }
58*eb9a1df2SHans Rosenfeld 
59*eb9a1df2SHans Rosenfeld static int
populate_int_prop(di_node_t di_node,nvlist_t * nvl,const char * name,int * ival)60*eb9a1df2SHans Rosenfeld populate_int_prop(di_node_t di_node, nvlist_t *nvl, const char *name, int *ival)
61*eb9a1df2SHans Rosenfeld {
62*eb9a1df2SHans Rosenfeld 	char val[20];
63*eb9a1df2SHans Rosenfeld 	int *ivals;
64*eb9a1df2SHans Rosenfeld 	int err;
65*eb9a1df2SHans Rosenfeld 
66*eb9a1df2SHans Rosenfeld 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, di_node, name, &ivals) != 1)
67*eb9a1df2SHans Rosenfeld 		return (errno);
68*eb9a1df2SHans Rosenfeld 
69*eb9a1df2SHans Rosenfeld 	(void) snprintf(val, sizeof (val), "%x", ivals[0]);
70*eb9a1df2SHans Rosenfeld 
71*eb9a1df2SHans Rosenfeld 	err = nvlist_add_string(nvl, name, val);
72*eb9a1df2SHans Rosenfeld 
73*eb9a1df2SHans Rosenfeld 	if (err == 0 && ival != NULL)
74*eb9a1df2SHans Rosenfeld 		*ival = ivals[0];
75*eb9a1df2SHans Rosenfeld 
76*eb9a1df2SHans Rosenfeld 	return (err);
77*eb9a1df2SHans Rosenfeld }
78*eb9a1df2SHans Rosenfeld 
79*eb9a1df2SHans Rosenfeld static int
dev_getlabel(pcidb_hdl_t * db,int vid,int did,char * buf,size_t buflen)80*eb9a1df2SHans Rosenfeld dev_getlabel(pcidb_hdl_t *db, int vid, int did, char *buf, size_t buflen)
81*eb9a1df2SHans Rosenfeld {
82*eb9a1df2SHans Rosenfeld 	pcidb_vendor_t *vend = NULL;
83*eb9a1df2SHans Rosenfeld 	pcidb_device_t *dev = NULL;
84*eb9a1df2SHans Rosenfeld 
85*eb9a1df2SHans Rosenfeld 	if ((vend = pcidb_lookup_vendor(db, vid)) == NULL)
86*eb9a1df2SHans Rosenfeld 		return (ENOENT);
87*eb9a1df2SHans Rosenfeld 
88*eb9a1df2SHans Rosenfeld 	if ((dev = pcidb_lookup_device_by_vendor(vend, did)) == NULL)
89*eb9a1df2SHans Rosenfeld 		return (ENOENT);
90*eb9a1df2SHans Rosenfeld 
91*eb9a1df2SHans Rosenfeld 	(void) snprintf(buf, buflen, "%s %s", pcidb_vendor_name(vend),
92*eb9a1df2SHans Rosenfeld 	    pcidb_device_name(dev));
93*eb9a1df2SHans Rosenfeld 
94*eb9a1df2SHans Rosenfeld 	return (0);
95*eb9a1df2SHans Rosenfeld }
96*eb9a1df2SHans Rosenfeld 
97*eb9a1df2SHans Rosenfeld static nvlist_t *
dev_getinfo(di_node_t di_node,pcidb_hdl_t * db,const char * dev,const char * path)98*eb9a1df2SHans Rosenfeld dev_getinfo(di_node_t di_node, pcidb_hdl_t *db,
99*eb9a1df2SHans Rosenfeld     const char *dev, const char *path)
100*eb9a1df2SHans Rosenfeld {
101*eb9a1df2SHans Rosenfeld 	char label[MAXPATHLEN];
102*eb9a1df2SHans Rosenfeld 	nvlist_t *nvl = NULL;
103*eb9a1df2SHans Rosenfeld 	int vid, did;
104*eb9a1df2SHans Rosenfeld 	int err;
105*eb9a1df2SHans Rosenfeld 
106*eb9a1df2SHans Rosenfeld 	if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0)
107*eb9a1df2SHans Rosenfeld 		goto out;
108*eb9a1df2SHans Rosenfeld 
109*eb9a1df2SHans Rosenfeld 	if (dev != NULL && (err = nvlist_add_string(nvl, "dev", dev)) != 0)
110*eb9a1df2SHans Rosenfeld 		goto out;
111*eb9a1df2SHans Rosenfeld 	if ((err = nvlist_add_string(nvl, "path", path)) != 0)
112*eb9a1df2SHans Rosenfeld 		goto out;
113*eb9a1df2SHans Rosenfeld 	if ((err = populate_int_prop(di_node, nvl, "vendor-id", &vid)) != 0)
114*eb9a1df2SHans Rosenfeld 		goto out;
115*eb9a1df2SHans Rosenfeld 	if ((err = populate_int_prop(di_node, nvl, "device-id", &did)) != 0)
116*eb9a1df2SHans Rosenfeld 		goto out;
117*eb9a1df2SHans Rosenfeld 	if ((err = populate_int_prop(di_node, nvl,
118*eb9a1df2SHans Rosenfeld 	    "subsystem-vendor-id", NULL)) != 0)
119*eb9a1df2SHans Rosenfeld 		goto out;
120*eb9a1df2SHans Rosenfeld 	if ((err = populate_int_prop(di_node, nvl, "subsystem-id", NULL)) != 0)
121*eb9a1df2SHans Rosenfeld 		goto out;
122*eb9a1df2SHans Rosenfeld 	if ((err = populate_int_prop(di_node, nvl, "revision-id", NULL)) != 0)
123*eb9a1df2SHans Rosenfeld 		goto out;
124*eb9a1df2SHans Rosenfeld 
125*eb9a1df2SHans Rosenfeld 	err = dev_getlabel(db, vid, did, label, sizeof (label));
126*eb9a1df2SHans Rosenfeld 
127*eb9a1df2SHans Rosenfeld 	if (err == 0) {
128*eb9a1df2SHans Rosenfeld 		err = nvlist_add_string(nvl, "label", label);
129*eb9a1df2SHans Rosenfeld 	} else if (err == ENOENT) {
130*eb9a1df2SHans Rosenfeld 		err = 0;
131*eb9a1df2SHans Rosenfeld 	}
132*eb9a1df2SHans Rosenfeld 
133*eb9a1df2SHans Rosenfeld out:
134*eb9a1df2SHans Rosenfeld 	if (err) {
135*eb9a1df2SHans Rosenfeld 		nvlist_free(nvl);
136*eb9a1df2SHans Rosenfeld 		errno = err;
137*eb9a1df2SHans Rosenfeld 		return (NULL);
138*eb9a1df2SHans Rosenfeld 	}
139*eb9a1df2SHans Rosenfeld 
140*eb9a1df2SHans Rosenfeld 	return (nvl);
141*eb9a1df2SHans Rosenfeld }
142*eb9a1df2SHans Rosenfeld 
143*eb9a1df2SHans Rosenfeld /*
144*eb9a1df2SHans Rosenfeld  * /devices/pci0@0/....@0,1:ppt -> /pci0@0/...@0,1
145*eb9a1df2SHans Rosenfeld  */
146*eb9a1df2SHans Rosenfeld static const char *
fs_to_phys_path(char * fspath)147*eb9a1df2SHans Rosenfeld fs_to_phys_path(char *fspath)
148*eb9a1df2SHans Rosenfeld {
149*eb9a1df2SHans Rosenfeld 	const char prefix[] = "/devices";
150*eb9a1df2SHans Rosenfeld 	char *c;
151*eb9a1df2SHans Rosenfeld 
152*eb9a1df2SHans Rosenfeld 	if ((c = strrchr(fspath, ':')) != NULL && strcmp(c, ":ppt") == 0)
153*eb9a1df2SHans Rosenfeld 		*c = '\0';
154*eb9a1df2SHans Rosenfeld 
155*eb9a1df2SHans Rosenfeld 	c = fspath;
156*eb9a1df2SHans Rosenfeld 
157*eb9a1df2SHans Rosenfeld 	if (strncmp(c, prefix, sizeof (prefix) - 1) == 0)
158*eb9a1df2SHans Rosenfeld 		c += sizeof (prefix) - 1;
159*eb9a1df2SHans Rosenfeld 
160*eb9a1df2SHans Rosenfeld 	return (c);
161*eb9a1df2SHans Rosenfeld }
162*eb9a1df2SHans Rosenfeld 
163*eb9a1df2SHans Rosenfeld /*
164*eb9a1df2SHans Rosenfeld  * Return an nvlist representing the mappings of /dev/ppt* devices to physical
165*eb9a1df2SHans Rosenfeld  * devices.  Of the form:
166*eb9a1df2SHans Rosenfeld  *
167*eb9a1df2SHans Rosenfeld  * /pci@0,0/... {
168*eb9a1df2SHans Rosenfeld  *  dev: "/dev/ppt0"
169*eb9a1df2SHans Rosenfeld  *  path: "/pci@0,0/..."
170*eb9a1df2SHans Rosenfeld  *  vendor-id: "8086"
171*eb9a1df2SHans Rosenfeld  *  device-id: "1528"
172*eb9a1df2SHans Rosenfeld  *  subsystem-vendor-id: "8086"
173*eb9a1df2SHans Rosenfeld  *  subsystem-id: "1528"
174*eb9a1df2SHans Rosenfeld  *  revision-id: "1"
175*eb9a1df2SHans Rosenfeld  *  label: "Intel Corporation ..."
176*eb9a1df2SHans Rosenfeld  * },
177*eb9a1df2SHans Rosenfeld  * /pci@0,0/...
178*eb9a1df2SHans Rosenfeld  *
179*eb9a1df2SHans Rosenfeld  * The nvlist should be freed by the caller.
180*eb9a1df2SHans Rosenfeld  */
181*eb9a1df2SHans Rosenfeld nvlist_t *
ppt_list_assigned(void)182*eb9a1df2SHans Rosenfeld ppt_list_assigned(void)
183*eb9a1df2SHans Rosenfeld {
184*eb9a1df2SHans Rosenfeld 	di_node_t di_root = DI_NODE_NIL;
185*eb9a1df2SHans Rosenfeld 	pcidb_hdl_t *db = NULL;
186*eb9a1df2SHans Rosenfeld 	nvlist_t *nvl = NULL;
187*eb9a1df2SHans Rosenfeld 	glob_t gl;
188*eb9a1df2SHans Rosenfeld 	int err;
189*eb9a1df2SHans Rosenfeld 
190*eb9a1df2SHans Rosenfeld 	bzero(&gl, sizeof (gl));
191*eb9a1df2SHans Rosenfeld 
192*eb9a1df2SHans Rosenfeld 	if ((di_root = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
193*eb9a1df2SHans Rosenfeld 		return (NULL);
194*eb9a1df2SHans Rosenfeld 
195*eb9a1df2SHans Rosenfeld 	if ((db = pcidb_open(PCIDB_VERSION)) == NULL) {
196*eb9a1df2SHans Rosenfeld 		err = errno;
197*eb9a1df2SHans Rosenfeld 		goto out;
198*eb9a1df2SHans Rosenfeld 	}
199*eb9a1df2SHans Rosenfeld 
200*eb9a1df2SHans Rosenfeld 	if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0)
201*eb9a1df2SHans Rosenfeld 		goto out;
202*eb9a1df2SHans Rosenfeld 
203*eb9a1df2SHans Rosenfeld 	if ((err = glob("/dev/ppt*", GLOB_KEEPSTAT | GLOB_ERR,
204*eb9a1df2SHans Rosenfeld 	    NULL, &gl)) != 0) {
205*eb9a1df2SHans Rosenfeld 		err = (err == GLOB_NOMATCH) ? 0 : errno;
206*eb9a1df2SHans Rosenfeld 		goto out;
207*eb9a1df2SHans Rosenfeld 	}
208*eb9a1df2SHans Rosenfeld 
209*eb9a1df2SHans Rosenfeld 	for (size_t i = 0; i < gl.gl_pathc; i++) {
210*eb9a1df2SHans Rosenfeld 		char fspath[MAXPATHLEN];
211*eb9a1df2SHans Rosenfeld 		nvlist_t *info_nvl;
212*eb9a1df2SHans Rosenfeld 		di_node_t di_node;
213*eb9a1df2SHans Rosenfeld 		const char *path;
214*eb9a1df2SHans Rosenfeld 
215*eb9a1df2SHans Rosenfeld 		if (!S_ISLNK(gl.gl_statv[i]->st_mode))
216*eb9a1df2SHans Rosenfeld 			continue;
217*eb9a1df2SHans Rosenfeld 
218*eb9a1df2SHans Rosenfeld 		if (realpath(gl.gl_pathv[i], fspath) == NULL) {
219*eb9a1df2SHans Rosenfeld 			err = errno;
220*eb9a1df2SHans Rosenfeld 			goto out;
221*eb9a1df2SHans Rosenfeld 		}
222*eb9a1df2SHans Rosenfeld 
223*eb9a1df2SHans Rosenfeld 		path = fs_to_phys_path(fspath);
224*eb9a1df2SHans Rosenfeld 
225*eb9a1df2SHans Rosenfeld 		/*
226*eb9a1df2SHans Rosenfeld 		 * path argument is treated as const.
227*eb9a1df2SHans Rosenfeld 		 */
228*eb9a1df2SHans Rosenfeld 		if ((di_node = di_lookup_node(di_root, (char *)path)) == NULL) {
229*eb9a1df2SHans Rosenfeld 			err = errno;
230*eb9a1df2SHans Rosenfeld 			goto out;
231*eb9a1df2SHans Rosenfeld 		}
232*eb9a1df2SHans Rosenfeld 
233*eb9a1df2SHans Rosenfeld 		if (!is_pci(di_node))
234*eb9a1df2SHans Rosenfeld 			continue;
235*eb9a1df2SHans Rosenfeld 
236*eb9a1df2SHans Rosenfeld 		info_nvl = dev_getinfo(di_node, db, gl.gl_pathv[i], path);
237*eb9a1df2SHans Rosenfeld 
238*eb9a1df2SHans Rosenfeld 		if (info_nvl == NULL) {
239*eb9a1df2SHans Rosenfeld 			err = errno;
240*eb9a1df2SHans Rosenfeld 			goto out;
241*eb9a1df2SHans Rosenfeld 		}
242*eb9a1df2SHans Rosenfeld 
243*eb9a1df2SHans Rosenfeld 		err = nvlist_add_nvlist(nvl, path, info_nvl);
244*eb9a1df2SHans Rosenfeld 		nvlist_free(info_nvl);
245*eb9a1df2SHans Rosenfeld 
246*eb9a1df2SHans Rosenfeld 		if (err)
247*eb9a1df2SHans Rosenfeld 			goto out;
248*eb9a1df2SHans Rosenfeld 	}
249*eb9a1df2SHans Rosenfeld 
250*eb9a1df2SHans Rosenfeld out:
251*eb9a1df2SHans Rosenfeld 	if (di_root != DI_NODE_NIL)
252*eb9a1df2SHans Rosenfeld 		di_fini(di_root);
253*eb9a1df2SHans Rosenfeld 
254*eb9a1df2SHans Rosenfeld 	pcidb_close(db);
255*eb9a1df2SHans Rosenfeld 	globfree(&gl);
256*eb9a1df2SHans Rosenfeld 
257*eb9a1df2SHans Rosenfeld 	if (err) {
258*eb9a1df2SHans Rosenfeld 		nvlist_free(nvl);
259*eb9a1df2SHans Rosenfeld 		errno = err;
260*eb9a1df2SHans Rosenfeld 		return (NULL);
261*eb9a1df2SHans Rosenfeld 	}
262*eb9a1df2SHans Rosenfeld 
263*eb9a1df2SHans Rosenfeld 	return (nvl);
264*eb9a1df2SHans Rosenfeld }
265*eb9a1df2SHans Rosenfeld 
266*eb9a1df2SHans Rosenfeld /*
267*eb9a1df2SHans Rosenfeld  * Read in our list of potential PPT devices.  A boot-module provided file
268*eb9a1df2SHans Rosenfeld  * explicitly over-rides anything delivered.
269*eb9a1df2SHans Rosenfeld  */
270*eb9a1df2SHans Rosenfeld static int
get_matches(list_t * listp)271*eb9a1df2SHans Rosenfeld get_matches(list_t *listp)
272*eb9a1df2SHans Rosenfeld {
273*eb9a1df2SHans Rosenfeld 	int err = 0;
274*eb9a1df2SHans Rosenfeld 	FILE *fp;
275*eb9a1df2SHans Rosenfeld 
276*eb9a1df2SHans Rosenfeld 	list_create(listp, sizeof (ppt_match_t),
277*eb9a1df2SHans Rosenfeld 	    offsetof(ppt_match_t, pm_list));
278*eb9a1df2SHans Rosenfeld 
279*eb9a1df2SHans Rosenfeld 	if ((fp = fopen("/system/boot/etc/ppt_matches", "r")) == NULL) {
280*eb9a1df2SHans Rosenfeld 		if (errno != ENOENT)
281*eb9a1df2SHans Rosenfeld 			return (errno);
282*eb9a1df2SHans Rosenfeld 
283*eb9a1df2SHans Rosenfeld 		if ((fp = fopen("/etc/ppt_matches", "r")) == NULL) {
284*eb9a1df2SHans Rosenfeld 			if (errno == ENOENT)
285*eb9a1df2SHans Rosenfeld 				return (0);
286*eb9a1df2SHans Rosenfeld 			return (errno);
287*eb9a1df2SHans Rosenfeld 		}
288*eb9a1df2SHans Rosenfeld 	}
289*eb9a1df2SHans Rosenfeld 
290*eb9a1df2SHans Rosenfeld 	for (;;) {
291*eb9a1df2SHans Rosenfeld 		char *line = NULL;
292*eb9a1df2SHans Rosenfeld 		ppt_match_t *pm;
293*eb9a1df2SHans Rosenfeld 		size_t cap = 0;
294*eb9a1df2SHans Rosenfeld 		ssize_t read;
295*eb9a1df2SHans Rosenfeld 
296*eb9a1df2SHans Rosenfeld 		if ((read = getline(&line, &cap, fp)) <= 0) {
297*eb9a1df2SHans Rosenfeld 			free(line);
298*eb9a1df2SHans Rosenfeld 			break;
299*eb9a1df2SHans Rosenfeld 		}
300*eb9a1df2SHans Rosenfeld 
301*eb9a1df2SHans Rosenfeld 		if (line[read - 1] == '\n')
302*eb9a1df2SHans Rosenfeld 			line[read - 1] = '\0';
303*eb9a1df2SHans Rosenfeld 
304*eb9a1df2SHans Rosenfeld 		if ((pm = malloc(sizeof (*pm))) == NULL) {
305*eb9a1df2SHans Rosenfeld 			err = errno;
306*eb9a1df2SHans Rosenfeld 			free(line);
307*eb9a1df2SHans Rosenfeld 			goto out;
308*eb9a1df2SHans Rosenfeld 		}
309*eb9a1df2SHans Rosenfeld 
310*eb9a1df2SHans Rosenfeld 		bzero(pm, sizeof (*pm));
311*eb9a1df2SHans Rosenfeld 
312*eb9a1df2SHans Rosenfeld 		if (sscanf(line, "pciex%4s,%4s", &pm->pm_vendor,
313*eb9a1df2SHans Rosenfeld 		    &pm->pm_device) == 2 ||
314*eb9a1df2SHans Rosenfeld 		    sscanf(line, "pci%4s,%4s", &pm->pm_vendor,
315*eb9a1df2SHans Rosenfeld 		    &pm->pm_device) == 2 ||
316*eb9a1df2SHans Rosenfeld 		    sscanf(line, "pciex%4s", &pm->pm_vendor) == 1 ||
317*eb9a1df2SHans Rosenfeld 		    sscanf(line, "pci%4s", &pm->pm_vendor) == 1) {
318*eb9a1df2SHans Rosenfeld 			list_insert_tail(listp, pm);
319*eb9a1df2SHans Rosenfeld 		} else if (line[0] == '/') {
320*eb9a1df2SHans Rosenfeld 			(void) strlcpy(pm->pm_path, line, sizeof (pm->pm_path));
321*eb9a1df2SHans Rosenfeld 			list_insert_tail(listp, pm);
322*eb9a1df2SHans Rosenfeld 		} else {
323*eb9a1df2SHans Rosenfeld 			/*
324*eb9a1df2SHans Rosenfeld 			 * Ignore any line we don't understand.
325*eb9a1df2SHans Rosenfeld 			 */
326*eb9a1df2SHans Rosenfeld 			free(pm);
327*eb9a1df2SHans Rosenfeld 		}
328*eb9a1df2SHans Rosenfeld 
329*eb9a1df2SHans Rosenfeld 		free(line);
330*eb9a1df2SHans Rosenfeld 	}
331*eb9a1df2SHans Rosenfeld 
332*eb9a1df2SHans Rosenfeld out:
333*eb9a1df2SHans Rosenfeld 	(void) fclose(fp);
334*eb9a1df2SHans Rosenfeld 	return (err);
335*eb9a1df2SHans Rosenfeld }
336*eb9a1df2SHans Rosenfeld 
337*eb9a1df2SHans Rosenfeld static boolean_t
match_ppt(list_t * matches,nvlist_t * nvl)338*eb9a1df2SHans Rosenfeld match_ppt(list_t *matches, nvlist_t *nvl)
339*eb9a1df2SHans Rosenfeld {
340*eb9a1df2SHans Rosenfeld 	char *vendor;
341*eb9a1df2SHans Rosenfeld 	char *device;
342*eb9a1df2SHans Rosenfeld 	char *path;
343*eb9a1df2SHans Rosenfeld 
344*eb9a1df2SHans Rosenfeld 	if (nvlist_lookup_string(nvl, "path", &path) != 0 ||
345*eb9a1df2SHans Rosenfeld 	    nvlist_lookup_string(nvl, "vendor-id", &vendor) != 0 ||
346*eb9a1df2SHans Rosenfeld 	    nvlist_lookup_string(nvl, "device-id", &device) != 0)
347*eb9a1df2SHans Rosenfeld 		return (B_FALSE);
348*eb9a1df2SHans Rosenfeld 
349*eb9a1df2SHans Rosenfeld 	for (ppt_match_t *pm = list_head(matches); pm != NULL;
350*eb9a1df2SHans Rosenfeld 	    pm = list_next(matches, pm)) {
351*eb9a1df2SHans Rosenfeld 		if (pm->pm_path[0] != '\0' && strcmp(pm->pm_path, path) == 0)
352*eb9a1df2SHans Rosenfeld 			return (B_TRUE);
353*eb9a1df2SHans Rosenfeld 
354*eb9a1df2SHans Rosenfeld 		if (pm->pm_vendor[0] != '\0' &&
355*eb9a1df2SHans Rosenfeld 		    strcmp(pm->pm_vendor, vendor) == 0) {
356*eb9a1df2SHans Rosenfeld 			if (pm->pm_device[0] == '\0')
357*eb9a1df2SHans Rosenfeld 				return (B_TRUE);
358*eb9a1df2SHans Rosenfeld 			if (strcmp(pm->pm_device, device) == 0)
359*eb9a1df2SHans Rosenfeld 				return (B_TRUE);
360*eb9a1df2SHans Rosenfeld 		}
361*eb9a1df2SHans Rosenfeld 	}
362*eb9a1df2SHans Rosenfeld 
363*eb9a1df2SHans Rosenfeld 	return (B_FALSE);
364*eb9a1df2SHans Rosenfeld }
365*eb9a1df2SHans Rosenfeld 
366*eb9a1df2SHans Rosenfeld static int
inspect_node(di_node_t di_node,void * arg)367*eb9a1df2SHans Rosenfeld inspect_node(di_node_t di_node, void *arg)
368*eb9a1df2SHans Rosenfeld {
369*eb9a1df2SHans Rosenfeld 	node_data_t *data = arg;
370*eb9a1df2SHans Rosenfeld 	nvlist_t *info_nvl = NULL;
371*eb9a1df2SHans Rosenfeld 	char *devname = NULL;
372*eb9a1df2SHans Rosenfeld 	const char *driver;
373*eb9a1df2SHans Rosenfeld 	char *path = NULL;
374*eb9a1df2SHans Rosenfeld 
375*eb9a1df2SHans Rosenfeld 	if (!is_pci(di_node))
376*eb9a1df2SHans Rosenfeld 		return (DI_WALK_CONTINUE);
377*eb9a1df2SHans Rosenfeld 
378*eb9a1df2SHans Rosenfeld 	driver = di_driver_name(di_node);
379*eb9a1df2SHans Rosenfeld 
380*eb9a1df2SHans Rosenfeld 	if (driver != NULL && strcmp(driver, "ppt") == 0) {
381*eb9a1df2SHans Rosenfeld 		if (asprintf(&devname, "/dev/ppt%d",
382*eb9a1df2SHans Rosenfeld 		    di_instance(di_node)) < 0) {
383*eb9a1df2SHans Rosenfeld 			data->nd_err = errno;
384*eb9a1df2SHans Rosenfeld 			goto out;
385*eb9a1df2SHans Rosenfeld 		}
386*eb9a1df2SHans Rosenfeld 	}
387*eb9a1df2SHans Rosenfeld 
388*eb9a1df2SHans Rosenfeld 	if ((path = di_devfs_path(di_node)) == NULL) {
389*eb9a1df2SHans Rosenfeld 		data->nd_err = ENOENT;
390*eb9a1df2SHans Rosenfeld 		goto out;
391*eb9a1df2SHans Rosenfeld 	}
392*eb9a1df2SHans Rosenfeld 
393*eb9a1df2SHans Rosenfeld 	info_nvl = dev_getinfo(di_node, data->nd_db, devname, path);
394*eb9a1df2SHans Rosenfeld 
395*eb9a1df2SHans Rosenfeld 	if (info_nvl == NULL)
396*eb9a1df2SHans Rosenfeld 		goto out;
397*eb9a1df2SHans Rosenfeld 
398*eb9a1df2SHans Rosenfeld 	if (devname == NULL && !match_ppt(&data->nd_matches, info_nvl))
399*eb9a1df2SHans Rosenfeld 		goto out;
400*eb9a1df2SHans Rosenfeld 
401*eb9a1df2SHans Rosenfeld 	data->nd_err = nvlist_add_nvlist(data->nd_nvl, path, info_nvl);
402*eb9a1df2SHans Rosenfeld 
403*eb9a1df2SHans Rosenfeld out:
404*eb9a1df2SHans Rosenfeld 	free(path);
405*eb9a1df2SHans Rosenfeld 	free(devname);
406*eb9a1df2SHans Rosenfeld 	nvlist_free(info_nvl);
407*eb9a1df2SHans Rosenfeld 	return (data->nd_err ? DI_WALK_TERMINATE : DI_WALK_CONTINUE);
408*eb9a1df2SHans Rosenfeld }
409*eb9a1df2SHans Rosenfeld 
410*eb9a1df2SHans Rosenfeld /*
411*eb9a1df2SHans Rosenfeld  * Like ppt_list_assigned() output, but includes all devices that could be used
412*eb9a1df2SHans Rosenfeld  * for passthrough, whether assigned or not.
413*eb9a1df2SHans Rosenfeld  */
414*eb9a1df2SHans Rosenfeld nvlist_t *
ppt_list(void)415*eb9a1df2SHans Rosenfeld ppt_list(void)
416*eb9a1df2SHans Rosenfeld {
417*eb9a1df2SHans Rosenfeld 	node_data_t nd = { NULL, };
418*eb9a1df2SHans Rosenfeld 	di_node_t di_root;
419*eb9a1df2SHans Rosenfeld 	int err;
420*eb9a1df2SHans Rosenfeld 
421*eb9a1df2SHans Rosenfeld 	if ((di_root = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
422*eb9a1df2SHans Rosenfeld 		return (NULL);
423*eb9a1df2SHans Rosenfeld 
424*eb9a1df2SHans Rosenfeld 	if ((err = get_matches(&nd.nd_matches)) != 0)
425*eb9a1df2SHans Rosenfeld 		goto out;
426*eb9a1df2SHans Rosenfeld 
427*eb9a1df2SHans Rosenfeld 	if ((nd.nd_db = pcidb_open(PCIDB_VERSION)) == NULL) {
428*eb9a1df2SHans Rosenfeld 		err = errno;
429*eb9a1df2SHans Rosenfeld 		goto out;
430*eb9a1df2SHans Rosenfeld 	}
431*eb9a1df2SHans Rosenfeld 
432*eb9a1df2SHans Rosenfeld 	if ((err = nvlist_alloc(&nd.nd_nvl, NV_UNIQUE_NAME, 0)) != 0)
433*eb9a1df2SHans Rosenfeld 		goto out;
434*eb9a1df2SHans Rosenfeld 
435*eb9a1df2SHans Rosenfeld 	if ((err = di_walk_node(di_root, DI_WALK_CLDFIRST,
436*eb9a1df2SHans Rosenfeld 	    &nd, inspect_node)) != 0)
437*eb9a1df2SHans Rosenfeld 		goto out;
438*eb9a1df2SHans Rosenfeld 
439*eb9a1df2SHans Rosenfeld 	err = nd.nd_err;
440*eb9a1df2SHans Rosenfeld 
441*eb9a1df2SHans Rosenfeld out:
442*eb9a1df2SHans Rosenfeld 	pcidb_close(nd.nd_db);
443*eb9a1df2SHans Rosenfeld 
444*eb9a1df2SHans Rosenfeld 	for (ppt_match_t *pm = list_head(&nd.nd_matches); pm != NULL; ) {
445*eb9a1df2SHans Rosenfeld 		ppt_match_t *next = list_next(&nd.nd_matches, pm);
446*eb9a1df2SHans Rosenfeld 		free(pm);
447*eb9a1df2SHans Rosenfeld 		pm = next;
448*eb9a1df2SHans Rosenfeld 	}
449*eb9a1df2SHans Rosenfeld 
450*eb9a1df2SHans Rosenfeld 	if (di_root != DI_NODE_NIL)
451*eb9a1df2SHans Rosenfeld 		di_fini(di_root);
452*eb9a1df2SHans Rosenfeld 
453*eb9a1df2SHans Rosenfeld 	if (err) {
454*eb9a1df2SHans Rosenfeld 		nvlist_free(nd.nd_nvl);
455*eb9a1df2SHans Rosenfeld 		errno = err;
456*eb9a1df2SHans Rosenfeld 		return (NULL);
457*eb9a1df2SHans Rosenfeld 	}
458*eb9a1df2SHans Rosenfeld 
459*eb9a1df2SHans Rosenfeld 	return (nd.nd_nvl);
460*eb9a1df2SHans Rosenfeld }
461*eb9a1df2SHans Rosenfeld 
462*eb9a1df2SHans Rosenfeld /*
463*eb9a1df2SHans Rosenfeld  * Given a physical path such as "/devices/pci0@0...", return the "/dev/pptX"
464*eb9a1df2SHans Rosenfeld  * that is bound to it, if any.  The "/devices/" prefix is optional.  The
465*eb9a1df2SHans Rosenfeld  * physical path may have the ":ppt" minor name suffix.
466*eb9a1df2SHans Rosenfeld  *
467*eb9a1df2SHans Rosenfeld  * Returns ENOENT if no such PPT device exists.
468*eb9a1df2SHans Rosenfeld  */
469*eb9a1df2SHans Rosenfeld int
ppt_devpath_to_dev(const char * inpath,char * buf,size_t buflen)470*eb9a1df2SHans Rosenfeld ppt_devpath_to_dev(const char *inpath, char *buf, size_t buflen)
471*eb9a1df2SHans Rosenfeld {
472*eb9a1df2SHans Rosenfeld 	char fspath[MAXPATHLEN] = "";
473*eb9a1df2SHans Rosenfeld 	nvpair_t *nvp = NULL;
474*eb9a1df2SHans Rosenfeld 	const char *devpath;
475*eb9a1df2SHans Rosenfeld 	int err = ENOENT;
476*eb9a1df2SHans Rosenfeld 	nvlist_t *nvl;
477*eb9a1df2SHans Rosenfeld 
478*eb9a1df2SHans Rosenfeld 	if (strlcat(fspath, inpath, sizeof (fspath)) >= sizeof (fspath))
479*eb9a1df2SHans Rosenfeld 		return (ENAMETOOLONG);
480*eb9a1df2SHans Rosenfeld 
481*eb9a1df2SHans Rosenfeld 	devpath = fs_to_phys_path(fspath);
482*eb9a1df2SHans Rosenfeld 
483*eb9a1df2SHans Rosenfeld 	if ((nvl = ppt_list_assigned()) == NULL)
484*eb9a1df2SHans Rosenfeld 		return (errno);
485*eb9a1df2SHans Rosenfeld 
486*eb9a1df2SHans Rosenfeld 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
487*eb9a1df2SHans Rosenfeld 		const char *name = nvpair_name(nvp);
488*eb9a1df2SHans Rosenfeld 		char *ppt = NULL;
489*eb9a1df2SHans Rosenfeld 		nvlist_t *props;
490*eb9a1df2SHans Rosenfeld 
491*eb9a1df2SHans Rosenfeld 		(void) nvpair_value_nvlist(nvp, &props);
492*eb9a1df2SHans Rosenfeld 
493*eb9a1df2SHans Rosenfeld 		if (strcmp(name, devpath) == 0) {
494*eb9a1df2SHans Rosenfeld 			(void) nvlist_lookup_string(props, "dev", &ppt);
495*eb9a1df2SHans Rosenfeld 
496*eb9a1df2SHans Rosenfeld 			err = 0;
497*eb9a1df2SHans Rosenfeld 
498*eb9a1df2SHans Rosenfeld 			if (strlcpy(buf, ppt, buflen) >= buflen)
499*eb9a1df2SHans Rosenfeld 				err = ENAMETOOLONG;
500*eb9a1df2SHans Rosenfeld 			break;
501*eb9a1df2SHans Rosenfeld 		}
502*eb9a1df2SHans Rosenfeld 	}
503*eb9a1df2SHans Rosenfeld 
504*eb9a1df2SHans Rosenfeld 	nvlist_free(nvl);
505*eb9a1df2SHans Rosenfeld 	return (err);
506*eb9a1df2SHans Rosenfeld }
507