1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2006 IronPort Systems
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/ctype.h>
32#include <sys/kernel.h>
33#include <sys/malloc.h>
34#include <sys/mount.h>
35#include <sys/sbuf.h>
36#include <sys/smp.h>
37#include <sys/bus.h>
38#include <sys/pciio.h>
39
40#include <dev/pci/pcivar.h>
41#include <dev/pci/pcireg.h>
42
43#include <compat/linux/linux_util.h>
44#include <fs/pseudofs/pseudofs.h>
45
46#include <compat/linsysfs/linsysfs.h>
47
48MALLOC_DEFINE(M_LINSYSFS, "linsysfs", "Linsysfs structures");
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#undef PCI_DEV
255#define PCI_DEV "pci"
256#define DRMN_DEV "drmn"
257static int
258linsysfs_run_bus(device_t dev, struct pfs_node *dir, struct pfs_node *scsi,
259    struct pfs_node *chardev, struct pfs_node *drm, char *path, char *prefix)
260{
261	struct scsi_host_queue *scsi_host;
262	struct pfs_node *sub_dir, *cur_file;
263	int i, nchildren, error;
264	device_t *children, parent;
265	devclass_t devclass;
266	const char *name = NULL;
267	struct pci_devinfo *dinfo;
268	char *device, *host, *new_path, *devname;
269
270	new_path = path;
271	devname = malloc(16, M_TEMP, M_WAITOK);
272
273	parent = device_get_parent(dev);
274	if (parent) {
275		devclass = device_get_devclass(parent);
276		if (devclass != NULL)
277			name = devclass_get_name(devclass);
278		if (name && strcmp(name, PCI_DEV) == 0) {
279			dinfo = device_get_ivars(dev);
280			if (dinfo) {
281				device = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
282				new_path = malloc(MAXPATHLEN, M_TEMP,
283				    M_WAITOK);
284				new_path[0] = '\000';
285				strcpy(new_path, path);
286				host = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
287				device[0] = '\000';
288				sprintf(device, "%s:%02x:%02x.%x",
289				    prefix,
290				    dinfo->cfg.bus,
291				    dinfo->cfg.slot,
292				    dinfo->cfg.func);
293				strcat(new_path, "/");
294				strcat(new_path, device);
295				dir = pfs_create_dir(dir, device,
296				    NULL, NULL, NULL, 0);
297				cur_file = pfs_create_file(dir, "vendor",
298				    &linsysfs_fill_vendor, NULL, NULL, NULL,
299				    PFS_RD);
300				cur_file->pn_data = (void*)dev;
301				cur_file = pfs_create_file(dir, "device",
302				    &linsysfs_fill_device, NULL, NULL, NULL,
303				    PFS_RD);
304				cur_file->pn_data = (void*)dev;
305				cur_file = pfs_create_file(dir,
306				    "subsystem_vendor",
307				    &linsysfs_fill_subvendor, NULL, NULL, NULL,
308				    PFS_RD);
309				cur_file->pn_data = (void*)dev;
310				cur_file = pfs_create_file(dir,
311				    "subsystem_device",
312				    &linsysfs_fill_subdevice, NULL, NULL, NULL,
313				    PFS_RD);
314				cur_file->pn_data = (void*)dev;
315				cur_file = pfs_create_file(dir, "revision",
316				    &linsysfs_fill_revid, NULL, NULL, NULL,
317				    PFS_RD);
318				cur_file->pn_data = (void*)dev;
319				cur_file = pfs_create_file(dir, "config",
320				    &linsysfs_fill_config, NULL, NULL, NULL,
321				    PFS_RD);
322				cur_file->pn_data = (void*)dev;
323				cur_file = pfs_create_file(dir, "uevent",
324				    &linsysfs_fill_uevent_pci, NULL, NULL,
325				    NULL, PFS_RD);
326				cur_file->pn_data = (void*)dev;
327				cur_file = pfs_create_link(dir, "subsystem",
328				    &linsysfs_fill_data, NULL, NULL, NULL, 0);
329				/* libdrm just checks that the link ends in "/pci" */
330				cur_file->pn_data = "/sys/bus/pci";
331
332				if (dinfo->cfg.baseclass == PCIC_STORAGE) {
333					/* DJA only make this if needed */
334					sprintf(host, "host%d", host_number++);
335					strcat(new_path, "/");
336					strcat(new_path, host);
337					pfs_create_dir(dir, host,
338					    NULL, NULL, NULL, 0);
339					scsi_host = malloc(sizeof(
340					    struct scsi_host_queue),
341					    M_DEVBUF, M_NOWAIT);
342					scsi_host->path = malloc(
343					    strlen(new_path) + 1,
344					    M_DEVBUF, M_NOWAIT);
345					scsi_host->path[0] = '\000';
346					bcopy(new_path, scsi_host->path,
347					    strlen(new_path) + 1);
348					scsi_host->name = "unknown";
349
350					sub_dir = pfs_create_dir(scsi, host,
351					    NULL, NULL, NULL, 0);
352					pfs_create_link(sub_dir, "device",
353					    &linsysfs_link_scsi_host,
354					    NULL, NULL, NULL, 0);
355					pfs_create_file(sub_dir, "proc_name",
356					    &linsysfs_scsiname,
357					    NULL, NULL, NULL, PFS_RD);
358					scsi_host->name
359					    = linux_driver_get_name_dev(dev);
360					TAILQ_INSERT_TAIL(&scsi_host_q,
361					    scsi_host, scsi_host_next);
362				}
363				free(device, M_TEMP);
364				free(host, M_TEMP);
365			}
366		}
367
368		devclass = device_get_devclass(dev);
369		if (devclass != NULL)
370			name = devclass_get_name(devclass);
371		else
372			name = NULL;
373		if (name != NULL && strcmp(name, DRMN_DEV) == 0 &&
374		    device_get_unit(dev) >= 0) {
375			dinfo = device_get_ivars(parent);
376			if (dinfo != NULL && dinfo->cfg.baseclass == PCIC_DISPLAY) {
377				pfs_create_dir(dir, "drm", NULL, NULL, NULL, 0);
378				sprintf(devname, "226:%d",
379				    device_get_unit(dev));
380				sub_dir = pfs_create_dir(chardev,
381				    devname, NULL, NULL, NULL, 0);
382				cur_file = pfs_create_link(sub_dir,
383				    "device", &linsysfs_fill_vgapci, NULL,
384				    NULL, NULL, PFS_RD);
385				cur_file->pn_data = (void*)dir;
386				cur_file = pfs_create_file(sub_dir,
387				    "uevent", &linsysfs_fill_uevent_drm, NULL,
388				    NULL, NULL, PFS_RD);
389				cur_file->pn_data = (void*)dev;
390				sprintf(devname, "card%d",
391				    device_get_unit(dev));
392				sub_dir = pfs_create_dir(drm,
393				    devname, NULL, NULL, NULL, 0);
394				cur_file = pfs_create_link(sub_dir,
395				    "device", &linsysfs_fill_vgapci, NULL,
396				    NULL, NULL, PFS_RD);
397				cur_file->pn_data = (void*)dir;
398			}
399		}
400	}
401
402	error = device_get_children(dev, &children, &nchildren);
403	if (error == 0) {
404		for (i = 0; i < nchildren; i++)
405			if (children[i])
406				linsysfs_run_bus(children[i], dir, scsi,
407				    chardev, drm, new_path, prefix);
408		free(children, M_TEMP);
409	}
410	if (new_path != path)
411		free(new_path, M_TEMP);
412	free(devname, M_TEMP);
413
414	return (1);
415}
416
417/*
418 * Filler function for sys/devices/system/cpu/{online,possible,present}
419 */
420static int
421linsysfs_cpuonline(PFS_FILL_ARGS)
422{
423
424	sbuf_printf(sb, "%d-%d\n", CPU_FIRST(), mp_maxid);
425	return (0);
426}
427
428/*
429 * Filler function for sys/devices/system/cpu/cpuX/online
430 */
431static int
432linsysfs_cpuxonline(PFS_FILL_ARGS)
433{
434
435	sbuf_printf(sb, "1\n");
436	return (0);
437}
438
439static void
440linsysfs_listcpus(struct pfs_node *dir)
441{
442	struct pfs_node *cpu;
443	char *name;
444	int i, count, len;
445
446	len = 1;
447	count = mp_maxcpus;
448	while (count > 10) {
449		count /= 10;
450		len++;
451	}
452	len += sizeof("cpu");
453	name = malloc(len, M_TEMP, M_WAITOK);
454
455	for (i = 0; i < mp_ncpus; ++i) {
456		/* /sys/devices/system/cpu/cpuX */
457		sprintf(name, "cpu%d", i);
458		cpu = pfs_create_dir(dir, name, NULL, NULL, NULL, 0);
459
460		pfs_create_file(cpu, "online", &linsysfs_cpuxonline,
461		    NULL, NULL, NULL, PFS_RD);
462	}
463	free(name, M_TEMP);
464}
465
466/*
467 * Constructor
468 */
469static int
470linsysfs_init(PFS_INIT_ARGS)
471{
472	struct pfs_node *root;
473	struct pfs_node *class;
474	struct pfs_node *dir, *sys, *cpu;
475	struct pfs_node *drm;
476	struct pfs_node *pci;
477	struct pfs_node *scsi;
478	struct pfs_node *devdir, *chardev;
479	struct pfs_node *kernel;
480	devclass_t devclass;
481	device_t dev;
482
483	TAILQ_INIT(&scsi_host_q);
484
485	root = pi->pi_root;
486
487	/* /sys/bus/... */
488	dir = pfs_create_dir(root, "bus", NULL, NULL, NULL, 0);
489
490	/* /sys/class/... */
491	class = pfs_create_dir(root, "class", NULL, NULL, NULL, 0);
492	scsi = pfs_create_dir(class, "scsi_host", NULL, NULL, NULL, 0);
493	drm = pfs_create_dir(class, "drm", NULL, NULL, NULL, 0);
494	pfs_create_dir(class, "power_supply", NULL, NULL, NULL, 0);
495
496	/* /sys/class/net/.. */
497	net = pfs_create_dir(class, "net", NULL, NULL, NULL, 0);
498
499	/* /sys/dev/... */
500	devdir = pfs_create_dir(root, "dev", NULL, NULL, NULL, 0);
501	chardev = pfs_create_dir(devdir, "char", NULL, NULL, NULL, 0);
502
503	/* /sys/devices/... */
504	dir = pfs_create_dir(root, "devices", NULL, NULL, NULL, 0);
505	pci = pfs_create_dir(dir, "pci0000:00", NULL, NULL, NULL, 0);
506
507	devclass = devclass_find("root");
508	if (devclass == NULL) {
509		return (0);
510	}
511
512	dev = devclass_get_device(devclass, 0);
513	linsysfs_run_bus(dev, pci, scsi, chardev, drm, "/pci0000:00", "0000");
514
515	/* /sys/devices/system */
516	sys = pfs_create_dir(dir, "system", NULL, NULL, NULL, 0);
517
518	/* /sys/devices/system/cpu */
519	cpu = pfs_create_dir(sys, "cpu", NULL, NULL, NULL, 0);
520
521	pfs_create_file(cpu, "online", &linsysfs_cpuonline,
522	    NULL, NULL, NULL, PFS_RD);
523	pfs_create_file(cpu, "possible", &linsysfs_cpuonline,
524	    NULL, NULL, NULL, PFS_RD);
525	pfs_create_file(cpu, "present", &linsysfs_cpuonline,
526	    NULL, NULL, NULL, PFS_RD);
527
528	linsysfs_listcpus(cpu);
529
530	/* /sys/kernel */
531	kernel = pfs_create_dir(root, "kernel", NULL, NULL, NULL, 0);
532	/* /sys/kernel/debug, mountpoint for lindebugfs. */
533	pfs_create_dir(kernel, "debug", NULL, NULL, NULL, 0);
534
535	linsysfs_net_init();
536
537	return (0);
538}
539
540/*
541 * Destructor
542 */
543static int
544linsysfs_uninit(PFS_INIT_ARGS)
545{
546	struct scsi_host_queue *scsi_host, *scsi_host_tmp;
547
548	TAILQ_FOREACH_SAFE(scsi_host, &scsi_host_q, scsi_host_next,
549	    scsi_host_tmp) {
550		TAILQ_REMOVE(&scsi_host_q, scsi_host, scsi_host_next);
551		free(scsi_host->path, M_TEMP);
552		free(scsi_host, M_TEMP);
553	}
554
555	linsysfs_net_uninit();
556
557	return (0);
558}
559
560PSEUDOFS(linsysfs, 1, VFCF_JAIL);
561#if defined(__aarch64__) || defined(__amd64__)
562MODULE_DEPEND(linsysfs, linux_common, 1, 1, 1);
563#else
564MODULE_DEPEND(linsysfs, linux, 1, 1, 1);
565#endif
566