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