1/*-
2 * Copyright (c) 2006 IronPort Systems
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/11/sys/compat/linsysfs/linsysfs.c 353299 2019-10-08 10:24:01Z tijl $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/malloc.h>
34#include <sys/mount.h>
35#include <sys/proc.h>
36#include <sys/sbuf.h>
37#include <sys/smp.h>
38#include <sys/socket.h>
39#include <sys/bus.h>
40#include <sys/pciio.h>
41
42#include <dev/pci/pcivar.h>
43#include <dev/pci/pcireg.h>
44
45#include <net/if.h>
46
47#include <compat/linux/linux_util.h>
48#include <fs/pseudofs/pseudofs.h>
49
50struct scsi_host_queue {
51	TAILQ_ENTRY(scsi_host_queue) scsi_host_next;
52	char *path;
53	char *name;
54};
55
56TAILQ_HEAD(,scsi_host_queue) scsi_host_q;
57
58static int host_number = 0;
59
60static int
61atoi(const char *str)
62{
63	return (int)strtol(str, (char **)NULL, 10);
64}
65
66/*
67 * Filler function for proc_name
68 */
69static int
70linsysfs_scsiname(PFS_FILL_ARGS)
71{
72	struct scsi_host_queue *scsi_host;
73	int index;
74
75	if (strncmp(pn->pn_parent->pn_name, "host", 4) == 0) {
76		index = atoi(&pn->pn_parent->pn_name[4]);
77	} else {
78		sbuf_printf(sb, "unknown\n");
79		return (0);
80	}
81	TAILQ_FOREACH(scsi_host, &scsi_host_q, scsi_host_next) {
82		if (index-- == 0) {
83			sbuf_printf(sb, "%s\n", scsi_host->name);
84			return (0);
85		}
86	}
87	sbuf_printf(sb, "unknown\n");
88	return (0);
89}
90
91/*
92 * Filler function for device sym-link
93 */
94static int
95linsysfs_link_scsi_host(PFS_FILL_ARGS)
96{
97	struct scsi_host_queue *scsi_host;
98	int index;
99
100	if (strncmp(pn->pn_parent->pn_name, "host", 4) == 0) {
101		index = atoi(&pn->pn_parent->pn_name[4]);
102	} else {
103		sbuf_printf(sb, "unknown\n");
104		return (0);
105	}
106	TAILQ_FOREACH(scsi_host, &scsi_host_q, scsi_host_next) {
107		if (index-- == 0) {
108			sbuf_printf(sb, "../../../devices%s", scsi_host->path);
109			return(0);
110		}
111	}
112	sbuf_printf(sb, "unknown\n");
113	return (0);
114}
115
116static int
117linsysfs_fill_data(PFS_FILL_ARGS)
118{
119	sbuf_printf(sb, "%s", (char *)pn->pn_data);
120	return (0);
121}
122
123static int
124linsysfs_fill_vendor(PFS_FILL_ARGS)
125{
126	sbuf_printf(sb, "0x%04x\n", pci_get_vendor((device_t)pn->pn_data));
127	return (0);
128}
129
130static int
131linsysfs_fill_device(PFS_FILL_ARGS)
132{
133	sbuf_printf(sb, "0x%04x\n", pci_get_device((device_t)pn->pn_data));
134	return (0);
135}
136
137static int
138linsysfs_fill_subvendor(PFS_FILL_ARGS)
139{
140	sbuf_printf(sb, "0x%04x\n", pci_get_subvendor((device_t)pn->pn_data));
141	return (0);
142}
143
144static int
145linsysfs_fill_subdevice(PFS_FILL_ARGS)
146{
147	sbuf_printf(sb, "0x%04x\n", pci_get_subdevice((device_t)pn->pn_data));
148	return (0);
149}
150
151static int
152linsysfs_fill_revid(PFS_FILL_ARGS)
153{
154	sbuf_printf(sb, "0x%x\n", pci_get_revid((device_t)pn->pn_data));
155	return (0);
156}
157
158static int
159linsysfs_fill_config(PFS_FILL_ARGS)
160{
161	uint8_t config[48];
162	device_t dev;
163	uint32_t reg;
164
165	dev = (device_t)pn->pn_data;
166	bzero(config, sizeof(config));
167	reg = pci_get_vendor(dev);
168	config[0] = reg;
169	config[1] = reg >> 8;
170	reg = pci_get_device(dev);
171	config[2] = reg;
172	config[3] = reg >> 8;
173	reg = pci_get_revid(dev);
174	config[8] = reg;
175	reg = pci_get_subvendor(dev);
176	config[44] = reg;
177	config[45] = reg >> 8;
178	reg = pci_get_subdevice(dev);
179	config[46] = reg;
180	config[47] = reg >> 8;
181	sbuf_bcat(sb, config, sizeof(config));
182	return (0);
183}
184
185/*
186 * Filler function for PCI uevent file
187 */
188static int
189linsysfs_fill_uevent_pci(PFS_FILL_ARGS)
190{
191	device_t dev;
192
193	dev = (device_t)pn->pn_data;
194	sbuf_printf(sb, "DRIVER=%s\nPCI_CLASS=%X\nPCI_ID=%04X:%04X\n"
195	    "PCI_SUBSYS_ID=%04X:%04X\nPCI_SLOT_NAME=%04d:%02x:%02x.%x\n",
196	    linux_driver_get_name_dev(dev), pci_get_class(dev),
197	    pci_get_vendor(dev), pci_get_device(dev), pci_get_subvendor(dev),
198	    pci_get_subdevice(dev), pci_get_domain(dev), pci_get_bus(dev),
199	    pci_get_slot(dev), pci_get_function(dev));
200	return (0);
201}
202
203/*
204 * Filler function for drm uevent file
205 */
206static int
207linsysfs_fill_uevent_drm(PFS_FILL_ARGS)
208{
209	device_t dev;
210	int unit;
211
212	dev = (device_t)pn->pn_data;
213	unit = device_get_unit(dev);
214	sbuf_printf(sb,
215	    "MAJOR=226\nMINOR=%d\nDEVNAME=dri/card%d\nDEVTYPE=dri_minor\n",
216	    unit, unit);
217	return (0);
218}
219
220static char *
221get_full_pfs_path(struct pfs_node *cur)
222{
223	char *temp, *path;
224
225	temp = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
226	path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
227	path[0] = '\0';
228
229	do {
230		snprintf(temp, MAXPATHLEN, "%s/%s", cur->pn_name, path);
231		strlcpy(path, temp, MAXPATHLEN);
232		cur = cur->pn_parent;
233	} while (cur->pn_parent != NULL);
234
235	path[strlen(path) - 1] = '\0'; /* remove extra slash */
236	free(temp, M_TEMP);
237	return (path);
238}
239
240/*
241 * Filler function for symlink from drm char device to PCI device
242 */
243static int
244linsysfs_fill_vgapci(PFS_FILL_ARGS)
245{
246	char *path;
247
248	path = get_full_pfs_path((struct pfs_node*)pn->pn_data);
249	sbuf_printf(sb, "../../../%s", path);
250	free(path, M_TEMP);
251	return (0);
252}
253
254#define PCI_DEV "pci"
255#define DRMN_DEV "drmn"
256static int
257linsysfs_run_bus(device_t dev, struct pfs_node *dir, struct pfs_node *scsi,
258    struct pfs_node *chardev, struct pfs_node *drm, char *path, char *prefix)
259{
260	struct scsi_host_queue *scsi_host;
261	struct pfs_node *sub_dir, *cur_file;
262	int i, nchildren, error;
263	device_t *children, parent;
264	devclass_t devclass;
265	const char *name = NULL;
266	struct pci_devinfo *dinfo;
267	char *device, *host, *new_path, *devname;
268
269	new_path = path;
270	devname = malloc(16, M_TEMP, M_WAITOK);
271
272	parent = device_get_parent(dev);
273	if (parent) {
274		devclass = device_get_devclass(parent);
275		if (devclass != NULL)
276			name = devclass_get_name(devclass);
277		if (name && strcmp(name, PCI_DEV) == 0) {
278			dinfo = device_get_ivars(dev);
279			if (dinfo) {
280				device = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
281				new_path = malloc(MAXPATHLEN, M_TEMP,
282				    M_WAITOK);
283				new_path[0] = '\000';
284				strcpy(new_path, path);
285				host = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
286				device[0] = '\000';
287				sprintf(device, "%s:%02x:%02x.%x",
288				    prefix,
289				    dinfo->cfg.bus,
290				    dinfo->cfg.slot,
291				    dinfo->cfg.func);
292				strcat(new_path, "/");
293				strcat(new_path, device);
294				dir = pfs_create_dir(dir, device,
295				    NULL, NULL, NULL, 0);
296				cur_file = pfs_create_file(dir, "vendor",
297				    &linsysfs_fill_vendor, NULL, NULL, NULL,
298				    PFS_RD);
299				cur_file->pn_data = (void*)dev;
300				cur_file = pfs_create_file(dir, "device",
301				    &linsysfs_fill_device, NULL, NULL, NULL,
302				    PFS_RD);
303				cur_file->pn_data = (void*)dev;
304				cur_file = pfs_create_file(dir,
305				    "subsystem_vendor",
306				    &linsysfs_fill_subvendor, NULL, NULL, NULL,
307				    PFS_RD);
308				cur_file->pn_data = (void*)dev;
309				cur_file = pfs_create_file(dir,
310				    "subsystem_device",
311				    &linsysfs_fill_subdevice, NULL, NULL, NULL,
312				    PFS_RD);
313				cur_file->pn_data = (void*)dev;
314				cur_file = pfs_create_file(dir, "revision",
315				    &linsysfs_fill_revid, NULL, NULL, NULL,
316				    PFS_RD);
317				cur_file->pn_data = (void*)dev;
318				cur_file = pfs_create_file(dir, "config",
319				    &linsysfs_fill_config, NULL, NULL, NULL,
320				    PFS_RD);
321				cur_file->pn_data = (void*)dev;
322				cur_file = pfs_create_file(dir, "uevent",
323				    &linsysfs_fill_uevent_pci, NULL, NULL,
324				    NULL, PFS_RD);
325				cur_file->pn_data = (void*)dev;
326				cur_file = pfs_create_link(dir, "subsystem",
327				    &linsysfs_fill_data, NULL, NULL, NULL, 0);
328				/* libdrm just checks that the link ends in "/pci" */
329				cur_file->pn_data = "/sys/bus/pci";
330
331				if (dinfo->cfg.baseclass == PCIC_STORAGE) {
332					/* DJA only make this if needed */
333					sprintf(host, "host%d", host_number++);
334					strcat(new_path, "/");
335					strcat(new_path, host);
336					pfs_create_dir(dir, host,
337					    NULL, NULL, NULL, 0);
338					scsi_host = malloc(sizeof(
339					    struct scsi_host_queue),
340					    M_DEVBUF, M_NOWAIT);
341					scsi_host->path = malloc(
342					    strlen(new_path) + 1,
343					    M_DEVBUF, M_NOWAIT);
344					scsi_host->path[0] = '\000';
345					bcopy(new_path, scsi_host->path,
346					    strlen(new_path) + 1);
347					scsi_host->name = "unknown";
348
349					sub_dir = pfs_create_dir(scsi, host,
350					    NULL, NULL, NULL, 0);
351					pfs_create_link(sub_dir, "device",
352					    &linsysfs_link_scsi_host,
353					    NULL, NULL, NULL, 0);
354					pfs_create_file(sub_dir, "proc_name",
355					    &linsysfs_scsiname,
356					    NULL, NULL, NULL, PFS_RD);
357					scsi_host->name
358					    = linux_driver_get_name_dev(dev);
359					TAILQ_INSERT_TAIL(&scsi_host_q,
360					    scsi_host, scsi_host_next);
361				}
362				free(device, M_TEMP);
363				free(host, M_TEMP);
364			}
365		}
366
367		devclass = device_get_devclass(dev);
368		if (devclass != NULL)
369			name = devclass_get_name(devclass);
370		else
371			name = NULL;
372		if (name != NULL && strcmp(name, DRMN_DEV) == 0 &&
373		    device_get_unit(dev) >= 0) {
374			dinfo = device_get_ivars(parent);
375			if (dinfo != NULL && dinfo->cfg.baseclass == PCIC_DISPLAY) {
376				pfs_create_dir(dir, "drm", NULL, NULL, NULL, 0);
377				sprintf(devname, "226:%d",
378				    device_get_unit(dev));
379				sub_dir = pfs_create_dir(chardev,
380				    devname, NULL, NULL, NULL, 0);
381				cur_file = pfs_create_link(sub_dir,
382				    "device", &linsysfs_fill_vgapci, NULL,
383				    NULL, NULL, PFS_RD);
384				cur_file->pn_data = (void*)dir;
385				cur_file = pfs_create_file(sub_dir,
386				    "uevent", &linsysfs_fill_uevent_drm, NULL,
387				    NULL, NULL, PFS_RD);
388				cur_file->pn_data = (void*)dev;
389				sprintf(devname, "card%d",
390				    device_get_unit(dev));
391				sub_dir = pfs_create_dir(drm,
392				    devname, NULL, NULL, NULL, 0);
393				cur_file = pfs_create_link(sub_dir,
394				    "device", &linsysfs_fill_vgapci, NULL,
395				    NULL, NULL, PFS_RD);
396				cur_file->pn_data = (void*)dir;
397			}
398		}
399	}
400
401	error = device_get_children(dev, &children, &nchildren);
402	if (error == 0) {
403		for (i = 0; i < nchildren; i++)
404			if (children[i])
405				linsysfs_run_bus(children[i], dir, scsi,
406				    chardev, drm, new_path, prefix);
407		free(children, M_TEMP);
408	}
409	if (new_path != path)
410		free(new_path, M_TEMP);
411	free(devname, M_TEMP);
412
413	return (1);
414}
415
416/*
417 * Filler function for sys/devices/system/cpu/online
418 */
419static int
420linsysfs_cpuonline(PFS_FILL_ARGS)
421{
422
423	sbuf_printf(sb, "%d-%d\n", CPU_FIRST(), mp_maxid);
424	return (0);
425}
426
427/*
428 * Filler function for sys/devices/system/cpu/cpuX/online
429 */
430static int
431linsysfs_cpuxonline(PFS_FILL_ARGS)
432{
433
434	sbuf_printf(sb, "1\n");
435	return (0);
436}
437
438static void
439linsysfs_listcpus(struct pfs_node *dir)
440{
441	struct pfs_node *cpu;
442	char *name;
443	int i, count, len;
444
445	len = 1;
446	count = mp_maxcpus;
447	while (count > 10) {
448		count /= 10;
449		len++;
450	}
451	len += sizeof("cpu");
452	name = malloc(len, M_TEMP, M_WAITOK);
453
454	for (i = 0; i < mp_ncpus; ++i) {
455		/* /sys/devices/system/cpu/cpuX */
456		sprintf(name, "cpu%d", i);
457		cpu = pfs_create_dir(dir, name, NULL, NULL, NULL, 0);
458
459		pfs_create_file(cpu, "online", &linsysfs_cpuxonline,
460		    NULL, NULL, NULL, PFS_RD);
461	}
462	free(name, M_TEMP);
463}
464
465/*
466 * Constructor
467 */
468static int
469linsysfs_init(PFS_INIT_ARGS)
470{
471	struct pfs_node *root;
472	struct pfs_node *class;
473	struct pfs_node *dir, *sys, *cpu;
474	struct pfs_node *drm;
475	struct pfs_node *pci;
476	struct pfs_node *scsi;
477	struct pfs_node *devdir, *chardev;
478	devclass_t devclass;
479	device_t dev;
480
481	TAILQ_INIT(&scsi_host_q);
482
483	root = pi->pi_root;
484
485	/* /sys/class/... */
486	class = pfs_create_dir(root, "class", NULL, NULL, NULL, 0);
487	scsi = pfs_create_dir(class, "scsi_host", NULL, NULL, NULL, 0);
488	drm = pfs_create_dir(class, "drm", NULL, NULL, NULL, 0);
489
490	/* /sys/dev/... */
491	devdir = pfs_create_dir(root, "dev", NULL, NULL, NULL, 0);
492	chardev = pfs_create_dir(devdir, "char", NULL, NULL, NULL, 0);
493
494	/* /sys/devices/... */
495	dir = pfs_create_dir(root, "devices", NULL, NULL, NULL, 0);
496	pci = pfs_create_dir(dir, "pci0000:00", NULL, NULL, NULL, 0);
497
498	devclass = devclass_find("root");
499	if (devclass == NULL) {
500		return (0);
501	}
502
503	dev = devclass_get_device(devclass, 0);
504	linsysfs_run_bus(dev, pci, scsi, chardev, drm, "/pci0000:00", "0000");
505
506	/* /sys/devices/system */
507	sys = pfs_create_dir(dir, "system", NULL, NULL, NULL, 0);
508
509	/* /sys/devices/system/cpu */
510	cpu = pfs_create_dir(sys, "cpu", NULL, NULL, NULL, 0);
511
512	pfs_create_file(cpu, "online", &linsysfs_cpuonline,
513	    NULL, NULL, NULL, PFS_RD);
514
515	linsysfs_listcpus(cpu);
516
517	return (0);
518}
519
520/*
521 * Destructor
522 */
523static int
524linsysfs_uninit(PFS_INIT_ARGS)
525{
526	struct scsi_host_queue *scsi_host, *scsi_host_tmp;
527
528	TAILQ_FOREACH_SAFE(scsi_host, &scsi_host_q, scsi_host_next,
529	    scsi_host_tmp) {
530		TAILQ_REMOVE(&scsi_host_q, scsi_host, scsi_host_next);
531		free(scsi_host->path, M_TEMP);
532		free(scsi_host, M_TEMP);
533	}
534
535	return (0);
536}
537
538PSEUDOFS(linsysfs, 1, PR_ALLOW_MOUNT_LINSYSFS);
539#if defined(__amd64__)
540MODULE_DEPEND(linsysfs, linux_common, 1, 1, 1);
541#else
542MODULE_DEPEND(linsysfs, linux, 1, 1, 1);
543#endif
544