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