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