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 #define PCI_DEV "pci" 277 #define DRMN_DEV "drmn" 278 static int 279 linsysfs_run_bus(device_t dev, struct pfs_node *dir, struct pfs_node *scsi, 280 struct pfs_node *chardev, struct pfs_node *drm, char *path, char *prefix) 281 { 282 struct scsi_host_queue *scsi_host; 283 struct pfs_node *sub_dir, *cur_file; 284 int i, nchildren, error; 285 device_t *children, parent; 286 devclass_t devclass; 287 const char *name = NULL; 288 struct pci_devinfo *dinfo; 289 char *device, *host, *new_path, *devname; 290 291 new_path = path; 292 devname = malloc(16, M_TEMP, M_WAITOK); 293 294 parent = device_get_parent(dev); 295 if (parent) { 296 devclass = device_get_devclass(parent); 297 if (devclass != NULL) 298 name = devclass_get_name(devclass); 299 if (name && strcmp(name, PCI_DEV) == 0) { 300 dinfo = device_get_ivars(dev); 301 if (dinfo) { 302 device = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 303 new_path = malloc(MAXPATHLEN, M_TEMP, 304 M_WAITOK); 305 new_path[0] = '\000'; 306 strcpy(new_path, path); 307 host = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); 308 device[0] = '\000'; 309 sprintf(device, "%s:%02x:%02x.%x", 310 prefix, 311 dinfo->cfg.bus, 312 dinfo->cfg.slot, 313 dinfo->cfg.func); 314 strcat(new_path, "/"); 315 strcat(new_path, device); 316 dir = pfs_create_dir(dir, device, 317 NULL, NULL, NULL, 0); 318 cur_file = pfs_create_file(dir, "vendor", 319 &linsysfs_fill_vendor, NULL, NULL, NULL, 320 PFS_RD); 321 cur_file->pn_data = (void*)dev; 322 cur_file = pfs_create_file(dir, "device", 323 &linsysfs_fill_device, NULL, NULL, NULL, 324 PFS_RD); 325 cur_file->pn_data = (void*)dev; 326 cur_file = pfs_create_file(dir, 327 "subsystem_vendor", 328 &linsysfs_fill_subvendor, NULL, NULL, NULL, 329 PFS_RD); 330 cur_file->pn_data = (void*)dev; 331 cur_file = pfs_create_file(dir, 332 "subsystem_device", 333 &linsysfs_fill_subdevice, NULL, NULL, NULL, 334 PFS_RD); 335 cur_file->pn_data = (void*)dev; 336 cur_file = pfs_create_file(dir, "revision", 337 &linsysfs_fill_revid, NULL, NULL, NULL, 338 PFS_RD); 339 cur_file->pn_data = (void*)dev; 340 cur_file = pfs_create_file(dir, "config", 341 &linsysfs_fill_config, NULL, NULL, NULL, 342 PFS_RD); 343 cur_file->pn_data = (void*)dev; 344 cur_file = pfs_create_file(dir, "uevent", 345 &linsysfs_fill_uevent_pci, NULL, NULL, 346 NULL, PFS_RD); 347 cur_file->pn_data = (void*)dev; 348 cur_file = pfs_create_link(dir, "subsystem", 349 &linsysfs_fill_data, NULL, NULL, NULL, 0); 350 /* libdrm just checks that the link ends in "/pci" */ 351 cur_file->pn_data = "/sys/bus/pci"; 352 353 if (dinfo->cfg.baseclass == PCIC_STORAGE) { 354 /* DJA only make this if needed */ 355 sprintf(host, "host%d", host_number++); 356 strcat(new_path, "/"); 357 strcat(new_path, host); 358 pfs_create_dir(dir, host, 359 NULL, NULL, NULL, 0); 360 scsi_host = malloc(sizeof( 361 struct scsi_host_queue), 362 M_DEVBUF, M_NOWAIT); 363 scsi_host->path = malloc( 364 strlen(new_path) + 1, 365 M_DEVBUF, M_NOWAIT); 366 scsi_host->path[0] = '\000'; 367 bcopy(new_path, scsi_host->path, 368 strlen(new_path) + 1); 369 scsi_host->name = "unknown"; 370 371 sub_dir = pfs_create_dir(scsi, host, 372 NULL, NULL, NULL, 0); 373 pfs_create_link(sub_dir, "device", 374 &linsysfs_link_scsi_host, 375 NULL, NULL, NULL, 0); 376 pfs_create_file(sub_dir, "proc_name", 377 &linsysfs_scsiname, 378 NULL, NULL, NULL, PFS_RD); 379 scsi_host->name 380 = linux_driver_get_name_dev(dev); 381 TAILQ_INSERT_TAIL(&scsi_host_q, 382 scsi_host, scsi_host_next); 383 } 384 free(device, M_TEMP); 385 free(host, M_TEMP); 386 } 387 } 388 389 devclass = device_get_devclass(dev); 390 if (devclass != NULL) 391 name = devclass_get_name(devclass); 392 else 393 name = NULL; 394 if (name != NULL && strcmp(name, DRMN_DEV) == 0 && 395 device_get_unit(dev) >= 0) { 396 dinfo = device_get_ivars(parent); 397 if (dinfo != NULL && dinfo->cfg.baseclass == PCIC_DISPLAY) { 398 sprintf(devname, "226:%d", 399 device_get_unit(dev)); 400 sub_dir = pfs_create_dir(chardev, 401 devname, NULL, NULL, NULL, 0); 402 cur_file = pfs_create_link(sub_dir, 403 "device", &linsysfs_fill_vgapci, NULL, 404 NULL, NULL, PFS_RD); 405 cur_file->pn_data = (void*)dir; 406 cur_file = pfs_create_file(sub_dir, 407 "uevent", &linsysfs_fill_uevent_drm, NULL, 408 NULL, NULL, PFS_RD); 409 cur_file->pn_data = (void*)dev; 410 sprintf(devname, "card%d", 411 device_get_unit(dev)); 412 sub_dir = pfs_create_dir(drm, 413 devname, NULL, NULL, NULL, 0); 414 cur_file = pfs_create_link(sub_dir, 415 "device", &linsysfs_fill_vgapci, NULL, 416 NULL, NULL, PFS_RD); 417 cur_file->pn_data = (void*)dir; 418 } 419 } 420 } 421 422 error = device_get_children(dev, &children, &nchildren); 423 if (error == 0) { 424 for (i = 0; i < nchildren; i++) 425 if (children[i]) 426 linsysfs_run_bus(children[i], dir, scsi, 427 chardev, drm, new_path, prefix); 428 free(children, M_TEMP); 429 } 430 if (new_path != path) 431 free(new_path, M_TEMP); 432 free(devname, M_TEMP); 433 434 return (1); 435 } 436 437 /* 438 * Filler function for sys/devices/system/cpu/online 439 */ 440 static int 441 linsysfs_cpuonline(PFS_FILL_ARGS) 442 { 443 444 sbuf_printf(sb, "%d-%d\n", CPU_FIRST(), mp_maxid); 445 return (0); 446 } 447 448 /* 449 * Filler function for sys/devices/system/cpu/cpuX/online 450 */ 451 static int 452 linsysfs_cpuxonline(PFS_FILL_ARGS) 453 { 454 455 sbuf_printf(sb, "1\n"); 456 return (0); 457 } 458 459 static void 460 linsysfs_listcpus(struct pfs_node *dir) 461 { 462 struct pfs_node *cpu; 463 char *name; 464 int i, count, len; 465 466 len = 1; 467 count = mp_maxcpus; 468 while (count > 10) { 469 count /= 10; 470 len++; 471 } 472 len += sizeof("cpu"); 473 name = malloc(len, M_TEMP, M_WAITOK); 474 475 for (i = 0; i < mp_ncpus; ++i) { 476 /* /sys/devices/system/cpu/cpuX */ 477 sprintf(name, "cpu%d", i); 478 cpu = pfs_create_dir(dir, name, NULL, NULL, NULL, 0); 479 480 pfs_create_file(cpu, "online", &linsysfs_cpuxonline, 481 NULL, NULL, NULL, PFS_RD); 482 } 483 free(name, M_TEMP); 484 } 485 486 /* 487 * Constructor 488 */ 489 static int 490 linsysfs_init(PFS_INIT_ARGS) 491 { 492 struct pfs_node *root; 493 struct pfs_node *class; 494 struct pfs_node *dir, *sys, *cpu; 495 struct pfs_node *drm; 496 struct pfs_node *pci; 497 struct pfs_node *scsi; 498 struct pfs_node *devdir, *chardev; 499 devclass_t devclass; 500 device_t dev; 501 502 TAILQ_INIT(&scsi_host_q); 503 504 root = pi->pi_root; 505 506 /* /sys/class/... */ 507 class = pfs_create_dir(root, "class", NULL, NULL, NULL, 0); 508 scsi = pfs_create_dir(class, "scsi_host", NULL, NULL, NULL, 0); 509 drm = pfs_create_dir(class, "drm", NULL, NULL, NULL, 0); 510 511 /* /sys/dev/... */ 512 devdir = pfs_create_dir(root, "dev", NULL, NULL, NULL, 0); 513 chardev = pfs_create_dir(devdir, "char", NULL, NULL, NULL, 0); 514 515 /* /sys/devices/... */ 516 dir = pfs_create_dir(root, "devices", NULL, NULL, NULL, 0); 517 pci = pfs_create_dir(dir, "pci0000:00", NULL, NULL, NULL, 0); 518 519 devclass = devclass_find("root"); 520 if (devclass == NULL) { 521 return (0); 522 } 523 524 dev = devclass_get_device(devclass, 0); 525 linsysfs_run_bus(dev, pci, scsi, chardev, drm, "/pci0000:00", "0000"); 526 527 /* /sys/devices/system */ 528 sys = pfs_create_dir(dir, "system", NULL, NULL, NULL, 0); 529 530 /* /sys/devices/system/cpu */ 531 cpu = pfs_create_dir(sys, "cpu", NULL, NULL, NULL, 0); 532 533 pfs_create_file(cpu, "online", &linsysfs_cpuonline, 534 NULL, NULL, NULL, PFS_RD); 535 536 linsysfs_listcpus(cpu); 537 538 return (0); 539 } 540 541 /* 542 * Destructor 543 */ 544 static int 545 linsysfs_uninit(PFS_INIT_ARGS) 546 { 547 struct scsi_host_queue *scsi_host, *scsi_host_tmp; 548 549 TAILQ_FOREACH_SAFE(scsi_host, &scsi_host_q, scsi_host_next, 550 scsi_host_tmp) { 551 TAILQ_REMOVE(&scsi_host_q, scsi_host, scsi_host_next); 552 free(scsi_host->path, M_TEMP); 553 free(scsi_host, M_TEMP); 554 } 555 556 return (0); 557 } 558 559 PSEUDOFS(linsysfs, 1, VFCF_JAIL); 560 #if defined(__amd64__) 561 MODULE_DEPEND(linsysfs, linux_common, 1, 1, 1); 562 #else 563 MODULE_DEPEND(linsysfs, linux, 1, 1, 1); 564 #endif 565